Compare commits

..

2 Commits

717 changed files with 7341 additions and 14272 deletions
+2 -2
View File
@@ -542,8 +542,8 @@ jobs:
strategy:
fail-fast: false
matrix:
node_index: [0, 1, 2, 3]
total_nodes: [4]
node_index: [0, 1, 2, 3, 4, 5, 6, 7]
total_nodes: [8]
env:
NODE_ENV: test
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
Generated
+95 -42
View File
@@ -48,7 +48,7 @@ dependencies = [
"infer",
"path-ext",
"pdf-extract",
"rand 0.9.1",
"rand 0.9.0",
"rayon",
"readability",
"serde_json",
@@ -162,7 +162,7 @@ dependencies = [
"napi",
"napi-build",
"napi-derive",
"rand 0.9.1",
"rand 0.9.0",
"rayon",
"sha3",
"tiktoken-rs",
@@ -656,9 +656,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.20"
version = "1.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
dependencies = [
"shlex",
]
@@ -774,9 +774,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.37"
version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
dependencies = [
"clap_builder",
"clap_derive",
@@ -784,9 +784,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.37"
version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
dependencies = [
"anstream",
"anstyle",
@@ -1784,7 +1784,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.57.0",
"windows-core 0.61.0",
]
[[package]]
@@ -2110,9 +2110,9 @@ dependencies = [
[[package]]
name = "lib0"
version = "0.16.10"
version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29dc19a026a0d45fc391898c6d4a6d0a5aab5ae6a826ebddc0f33572ffdae8dc"
checksum = "daf23122cb1c970b77ea6030eac5e328669415b65d2ab245c99bfb110f9d62dc"
dependencies = [
"serde",
"serde_json",
@@ -2132,7 +2132,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -2223,7 +2223,7 @@ dependencies = [
"md-5",
"nom 8.0.0",
"nom_locate",
"rand 0.9.1",
"rand 0.9.0",
"rangemap",
"sha2",
"stringprep",
@@ -2570,12 +2570,11 @@ dependencies = [
[[package]]
name = "objc2-core-foundation"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925"
dependencies = [
"bitflags 2.9.0",
"dispatch2",
"objc2",
]
@@ -2587,9 +2586,9 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-foundation"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998"
dependencies = [
"bitflags 2.9.0",
"block2",
@@ -3038,12 +3037,13 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
"zerocopy 0.8.24",
]
[[package]]
@@ -3110,7 +3110,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463"
dependencies = [
"num-traits",
"rand 0.9.1",
"rand 0.9.0",
]
[[package]]
@@ -3690,9 +3690,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e"
checksum = "14e22987355fbf8cfb813a0cf8cd97b1b4ec834b94dbd759a9e8679d41fabe83"
dependencies = [
"sqlx-core",
"sqlx-macros",
@@ -3703,9 +3703,9 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3"
checksum = "55c4720d7d4cd3d5b00f61d03751c685ad09c33ae8290c8a2c11335e0604300b"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -3740,9 +3740,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce"
checksum = "175147fcb75f353ac7675509bc58abb2cb291caf0fd24a3623b8f7e3eb0a754b"
dependencies = [
"proc-macro2",
"quote",
@@ -3753,9 +3753,9 @@ dependencies = [
[[package]]
name = "sqlx-macros-core"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7"
checksum = "1cde983058e53bfa75998e1982086c5efe3c370f3250bf0357e344fa3352e32b"
dependencies = [
"dotenvy",
"either",
@@ -3779,9 +3779,9 @@ dependencies = [
[[package]]
name = "sqlx-mysql"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7"
checksum = "847d2e5393a4f39e47e4f36cab419709bc2b83cbe4223c60e86e1471655be333"
dependencies = [
"atoi",
"base64 0.22.1",
@@ -3822,9 +3822,9 @@ dependencies = [
[[package]]
name = "sqlx-postgres"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6"
checksum = "cc35947a541b9e0a2e3d85da444f1c4137c13040267141b208395a0d0ca4659f"
dependencies = [
"atoi",
"base64 0.22.1",
@@ -3860,9 +3860,9 @@ dependencies = [
[[package]]
name = "sqlx-sqlite"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc"
checksum = "6c48291dac4e5ed32da0927a0b981788be65674aeb62666d19873ab4289febde"
dependencies = [
"atoi",
"chrono",
@@ -5112,10 +5112,23 @@ dependencies = [
"windows-implement 0.58.0",
"windows-interface 0.58.0",
"windows-result 0.2.0",
"windows-strings",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface 0.59.1",
"windows-link",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
@@ -5138,6 +5151,17 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "windows-interface"
version = "0.57.0"
@@ -5160,6 +5184,17 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "windows-link"
version = "0.1.1"
@@ -5184,6 +5219,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
@@ -5194,6 +5238,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -5419,14 +5472,14 @@ dependencies = [
"path-ext",
"proptest",
"proptest-derive",
"rand 0.9.1",
"rand 0.9.0",
"rand_chacha 0.9.0",
"rand_distr",
"serde",
"serde_json",
"smol_str",
"thiserror 2.0.12",
"yrs 0.23.1",
"yrs 0.23.0",
]
[[package]]
@@ -5452,12 +5505,12 @@ dependencies = [
"phf 0.11.3",
"proptest",
"proptest-derive",
"rand 0.9.1",
"rand 0.9.0",
"rand_chacha 0.9.0",
"regex",
"y-octo",
"y-sync",
"yrs 0.23.1",
"yrs 0.23.0",
]
[[package]]
@@ -5511,9 +5564,9 @@ dependencies = [
[[package]]
name = "yrs"
version = "0.23.1"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a7cab84724ae7f361a8c92465f5160922cbb941a499e1a8cacd103351ab9c78"
checksum = "0189b51d8ab1283e7c1f1f515c610875262e629cf258bec530da5cd4aa115d59"
dependencies = [
"arc-swap",
"async-lock",
+5 -14
View File
@@ -19,7 +19,6 @@
"@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:*",
@@ -122,9 +121,6 @@
"./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",
@@ -143,9 +139,7 @@
"./blocks/paragraph": "./src/blocks/paragraph/index.ts",
"./blocks/paragraph/store": "./src/blocks/paragraph/store.ts",
"./blocks/paragraph/view": "./src/blocks/paragraph/view.ts",
"./blocks/root": "./src/blocks/root/index.ts",
"./blocks/root/store": "./src/blocks/root/store.ts",
"./blocks/root/view": "./src/blocks/root/view.ts",
"./blocks/root": "./src/blocks/root.ts",
"./blocks/surface": "./src/blocks/surface/index.ts",
"./blocks/surface/store": "./src/blocks/surface/store.ts",
"./blocks/surface/view": "./src/blocks/surface/view.ts",
@@ -198,12 +192,9 @@
"./widgets/viewport-overlay/view": "./src/widgets/viewport-overlay/view.ts",
"./widgets/page-dragging-area": "./src/widgets/page-dragging-area/index.ts",
"./widgets/page-dragging-area/view": "./src/widgets/page-dragging-area/view.ts",
"./fragments/doc-title": "./src/fragments/doc-title/index.ts",
"./fragments/doc-title/view": "./src/fragments/doc-title/view.ts",
"./fragments/frame-panel": "./src/fragments/frame-panel/index.ts",
"./fragments/frame-panel/view": "./src/fragments/frame-panel/view.ts",
"./fragments/outline": "./src/fragments/outline/index.ts",
"./fragments/outline/view": "./src/fragments/outline/view.ts",
"./fragments/doc-title": "./src/fragments/doc-title.ts",
"./fragments/frame-panel": "./src/fragments/frame-panel.ts",
"./fragments/outline": "./src/fragments/outline.ts",
"./gfx/text": "./src/gfx/text/index.ts",
"./gfx/text/store": "./src/gfx/text/store.ts",
"./gfx/text/view": "./src/gfx/text/view.ts",
@@ -284,6 +275,6 @@
"version": "0.21.0",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"vitest": "3.1.2"
"vitest": "3.1.1"
}
}
@@ -2448,121 +2448,6 @@ World!
});
expect(target.file).toBe(markdown);
});
test('callout', async () => {
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'block:vu6SK6WJpW',
flavour: 'affine:page',
props: {
title: {
'$blocksuite:internal:text$': true,
delta: [],
},
},
children: [
{
type: 'block',
id: 'block:Tk4gSPocAt',
flavour: 'affine:surface',
props: {
elements: {},
},
children: [],
},
{
type: 'block',
id: 'block:WfnS5ZDCJT',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'block:8hOLxad5Fv',
flavour: 'affine:callout',
props: {
emoji: '💡',
},
children: [
{
type: 'block',
id: 'block:8hOLxad5Fv',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [{ insert: 'First callout' }],
},
},
children: [],
},
],
},
{
type: 'block',
id: 'block:8hOLxadvdv',
flavour: 'affine:callout',
props: {
emoji: '',
},
children: [
{
type: 'block',
id: 'block:8hOLxad5Fv',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [{ insert: 'Second callout without emoji' }],
},
},
children: [],
},
],
},
{
type: 'block',
id: 'block:8hOLxbfdb',
flavour: 'affine:paragraph',
props: {
type: 'quote',
text: {
'$blocksuite:internal:text$': true,
delta: [{ insert: 'This is a regular blockquote' }],
},
},
children: [],
},
],
},
],
};
const markdown = `> \\[!💡]
>
> First callout
> \\[!]
>
> Second callout without emoji
> This is a regular blockquote
`;
const mdAdapter = new MarkdownAdapter(createJob(), provider);
const target = await mdAdapter.fromBlockSnapshot({
snapshot: blockSnapshot,
});
expect(target.file).toBe(markdown);
});
});
describe('markdown to snapshot', () => {
@@ -3703,7 +3588,7 @@ bbb
props: {
xywh: '[0,0,800,95]',
background: {
dark: '#252525',
dark: '#000000',
light: '#ffffff',
},
index: 'a0',
@@ -4297,62 +4182,4 @@ hhh
});
expect(nanoidReplacement(rawSliceSnapshot!)).toEqual(sliceSnapshot);
});
describe('callout', () => {
const calloutBlockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:callout',
props: {
emoji: '💬',
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[2]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [{ insert: 'This is a callout' }],
},
},
children: [],
},
],
},
],
};
test('callout start with escape character', async () => {
const markdown = '> \\[!💬]\n> This is a callout';
const mdAdapter = new MarkdownAdapter(createJob(), provider);
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
file: markdown,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(calloutBlockSnapshot);
});
test('callout start without escape character', async () => {
const markdown = '> [!💬]\n> This is a callout';
const mdAdapter = new MarkdownAdapter(createJob(), provider);
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
file: markdown,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(calloutBlockSnapshot);
});
});
});
@@ -0,0 +1,65 @@
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];
}
@@ -0,0 +1,37 @@
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,
];
@@ -0,0 +1,6 @@
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';
@@ -0,0 +1,43 @@
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,
];
@@ -0,0 +1,9 @@
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,
];
@@ -0,0 +1,34 @@
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,
];
@@ -0,0 +1,34 @@
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,
];
@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-embed-doc';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-embed-doc/store';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-embed-doc/view';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-root/store';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-root/view';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-components/citation';
+170 -17
View File
@@ -1,24 +1,92 @@
import { type effects as blockRootEffects } from '@blocksuite/affine-block-root/effects';
import { type effects as componentCaptionEffects } from '@blocksuite/affine-components/caption';
import { type effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
import { type effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
import { type effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
import { type effects as componentDropIndicatorEffects } from '@blocksuite/affine-components/drop-indicator';
import { type effects as componentEmbedCardModalEffects } from '@blocksuite/affine-components/embed-card-modal';
import { type effects as componentHighlightDropdownMenuEffects } from '@blocksuite/affine-components/highlight-dropdown-menu';
import { type effects as componentLinkPreviewEffects } from '@blocksuite/affine-components/link-preview';
import { type effects as componentLinkedDocTitleEffects } from '@blocksuite/affine-components/linked-doc-title';
import { type effects as componentPortalEffects } from '@blocksuite/affine-components/portal';
import { type effects as componentToggleButtonEffects } from '@blocksuite/affine-components/toggle-button';
import { type effects as componentToolbarEffects } from '@blocksuite/affine-components/toolbar';
import { type effects as componentViewDropdownMenuEffects } from '@blocksuite/affine-components/view-dropdown-menu';
import { type effects as richTextEffects } from '@blocksuite/affine-rich-text/effects';
import { type effects as stdEffects } from '@blocksuite/std/effects';
import { effects as blockAttachmentEffects } from '@blocksuite/affine-block-attachment/effects';
import { effects as blockBookmarkEffects } from '@blocksuite/affine-block-bookmark/effects';
import { effects as blockCalloutEffects } from '@blocksuite/affine-block-callout/effects';
import { effects as blockCodeEffects } from '@blocksuite/affine-block-code/effects';
import { effects as blockDataViewEffects } from '@blocksuite/affine-block-data-view/effects';
import { effects as blockDatabaseEffects } from '@blocksuite/affine-block-database/effects';
import { effects as blockDividerEffects } from '@blocksuite/affine-block-divider/effects';
import { effects as blockEdgelessTextEffects } from '@blocksuite/affine-block-edgeless-text/effects';
import { effects as blockEmbedEffects } from '@blocksuite/affine-block-embed/effects';
import { effects as blockFrameEffects } from '@blocksuite/affine-block-frame/effects';
import { effects as blockImageEffects } from '@blocksuite/affine-block-image/effects';
import { effects as blockLatexEffects } from '@blocksuite/affine-block-latex/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 blockRootEffects } from '@blocksuite/affine-block-root/effects';
import { effects as blockSurfaceEffects } from '@blocksuite/affine-block-surface/effects';
import { effects as blockSurfaceRefEffects } from '@blocksuite/affine-block-surface-ref/effects';
import { effects as blockTableEffects } from '@blocksuite/affine-block-table/effects';
import { BlockSelection } from '@blocksuite/affine-components/block-selection';
import { BlockZeroWidth } from '@blocksuite/affine-components/block-zero-width';
import { effects as componentCaptionEffects } from '@blocksuite/affine-components/caption';
import { effects as componentCardStyleDropdownMenuEffects } from '@blocksuite/affine-components/card-style-dropdown-menu';
import { effects as componentCitationEffects } from '@blocksuite/affine-components/citation';
import { effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
import { effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
import { effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
import { effects as componentDropIndicatorEffects } from '@blocksuite/affine-components/drop-indicator';
import { effects as componentEdgelessLineStylesEffects } from '@blocksuite/affine-components/edgeless-line-styles-panel';
import { effects as componentEdgelessLineWidthEffects } from '@blocksuite/affine-components/edgeless-line-width-panel';
import { effects as componentEdgelessShapeColorPickerEffects } from '@blocksuite/affine-components/edgeless-shape-color-picker';
import { effects as componentEmbedCardModalEffects } from '@blocksuite/affine-components/embed-card-modal';
import { FilterableListComponent } from '@blocksuite/affine-components/filterable-list';
import { effects as componentHighlightDropdownMenuEffects } from '@blocksuite/affine-components/highlight-dropdown-menu';
import { IconButton } from '@blocksuite/affine-components/icon-button';
import { effects as componentLinkPreviewEffects } from '@blocksuite/affine-components/link-preview';
import { effects as componentLinkedDocTitleEffects } from '@blocksuite/affine-components/linked-doc-title';
import { effects as componentOpenDocDropdownMenuEffects } from '@blocksuite/affine-components/open-doc-dropdown-menu';
import { effects as componentPortalEffects } from '@blocksuite/affine-components/portal';
import { effects as componentSizeDropdownMenuEffects } from '@blocksuite/affine-components/size-dropdown-menu';
import { SmoothCorner } from '@blocksuite/affine-components/smooth-corner';
import { effects as componentToggleButtonEffects } from '@blocksuite/affine-components/toggle-button';
import { ToggleSwitch } from '@blocksuite/affine-components/toggle-switch';
import { effects as componentToolbarEffects } from '@blocksuite/affine-components/toolbar';
import { effects as componentTooltipContentWithShortcutEffects } from '@blocksuite/affine-components/tooltip-content-with-shortcut';
import { effects as componentViewDropdownMenuEffects } from '@blocksuite/affine-components/view-dropdown-menu';
import { effects as fragmentDocTitleEffects } from '@blocksuite/affine-fragment-doc-title/effects';
import { effects as fragmentFramePanelEffects } from '@blocksuite/affine-fragment-frame-panel/effects';
import { effects as fragmentOutlineEffects } from '@blocksuite/affine-fragment-outline/effects';
import { effects as inlineFootnoteEffects } from '@blocksuite/affine-inline-footnote/effects';
import { effects as inlineLatexEffects } from '@blocksuite/affine-inline-latex/effects';
import { effects as inlineLinkEffects } from '@blocksuite/affine-inline-link/effects';
import { effects as inlineMentionEffects } from '@blocksuite/affine-inline-mention';
import { effects as inlinePresetEffects } from '@blocksuite/affine-inline-preset/effects';
import { effects as inlineReferenceEffects } from '@blocksuite/affine-inline-reference/effects';
import { effects as richTextEffects } from '@blocksuite/affine-rich-text/effects';
import { effects as widgetDragHandleEffects } from '@blocksuite/affine-widget-drag-handle/effects';
import { effects as widgetEdgelessAutoConnectEffects } from '@blocksuite/affine-widget-edgeless-auto-connect/effects';
import { effects as widgetFrameTitleEffects } from '@blocksuite/affine-widget-frame-title/effects';
import { effects as widgetRemoteSelectionEffects } from '@blocksuite/affine-widget-remote-selection/effects';
import { effects as widgetScrollAnchoringEffects } from '@blocksuite/affine-widget-scroll-anchoring/effects';
import { effects as widgetSlashMenuEffects } from '@blocksuite/affine-widget-slash-menu/effects';
import { effects as widgetToolbarEffects } from '@blocksuite/affine-widget-toolbar/effects';
import { effects as dataViewEffects } from '@blocksuite/data-view/effects';
import { effects as stdEffects } from '@blocksuite/std/effects';
export declare const _GLOBAL_:
| typeof stdEffects
| typeof dataViewEffects
| typeof richTextEffects
| typeof blockNoteEffects
| typeof blockAttachmentEffects
| typeof blockBookmarkEffects
| typeof blockFrameEffects
| typeof blockListEffects
| typeof blockParagraphEffects
| typeof blockEmbedEffects
| typeof blockSurfaceEffects
| typeof blockImageEffects
| typeof blockDatabaseEffects
| typeof blockSurfaceRefEffects
| typeof blockLatexEffects
| typeof blockEdgelessTextEffects
| typeof blockDividerEffects
| typeof blockDataViewEffects
| typeof blockCodeEffects
| typeof blockTableEffects
| typeof blockRootEffects
| typeof blockCalloutEffects
| typeof componentCaptionEffects
| typeof componentContextMenuEffects
| typeof componentDatePickerEffects
@@ -31,4 +99,89 @@ export declare const _GLOBAL_:
| typeof componentToolbarEffects
| typeof componentToggleButtonEffects
| typeof componentColorPickerEffects
| typeof componentViewDropdownMenuEffects;
| typeof componentViewDropdownMenuEffects
| typeof widgetScrollAnchoringEffects
| typeof widgetFrameTitleEffects
| typeof widgetRemoteSelectionEffects
| typeof widgetDragHandleEffects
| typeof widgetEdgelessAutoConnectEffects
| typeof widgetToolbarEffects
| typeof widgetSlashMenuEffects
| typeof fragmentDocTitleEffects
| typeof fragmentFramePanelEffects
| typeof fragmentOutlineEffects;
export function effects() {
stdEffects();
dataViewEffects();
richTextEffects();
inlineReferenceEffects();
inlinePresetEffects();
inlineLinkEffects();
inlineFootnoteEffects();
inlineLatexEffects();
inlineMentionEffects();
blockNoteEffects();
blockAttachmentEffects();
blockBookmarkEffects();
blockFrameEffects();
blockListEffects();
blockParagraphEffects();
blockEmbedEffects();
blockSurfaceEffects();
blockImageEffects();
blockDatabaseEffects();
blockSurfaceRefEffects();
blockLatexEffects();
blockEdgelessTextEffects();
blockDividerEffects();
blockDataViewEffects();
blockCodeEffects();
blockTableEffects();
blockRootEffects();
blockCalloutEffects();
componentCaptionEffects();
componentContextMenuEffects();
componentDatePickerEffects();
componentPortalEffects();
componentToolbarEffects();
componentDropIndicatorEffects();
componentToggleButtonEffects();
componentColorPickerEffects();
componentEmbedCardModalEffects();
componentLinkPreviewEffects();
componentLinkedDocTitleEffects();
componentCitationEffects();
componentCardStyleDropdownMenuEffects();
componentHighlightDropdownMenuEffects();
componentViewDropdownMenuEffects();
componentTooltipContentWithShortcutEffects();
componentSizeDropdownMenuEffects();
componentEdgelessLineWidthEffects();
componentEdgelessLineStylesEffects();
componentEdgelessShapeColorPickerEffects();
componentOpenDocDropdownMenuEffects();
widgetScrollAnchoringEffects();
widgetFrameTitleEffects();
widgetRemoteSelectionEffects();
widgetDragHandleEffects();
widgetEdgelessAutoConnectEffects();
widgetSlashMenuEffects();
widgetToolbarEffects();
fragmentDocTitleEffects();
fragmentFramePanelEffects();
fragmentOutlineEffects();
customElements.define('icon-button', IconButton);
customElements.define('smooth-corner', SmoothCorner);
customElements.define('toggle-switch', ToggleSwitch);
customElements.define('affine-filterable-list', FilterableListComponent);
customElements.define('block-zero-width', BlockZeroWidth);
customElements.define('affine-block-selection', BlockSelection);
}
@@ -0,0 +1,12 @@
import { effects as blockRootEffects } from '@blocksuite/affine-block-root/effects';
import { effects as fragmentDocTitleEffects } from '@blocksuite/affine-fragment-doc-title/effects';
import { effects as fragmentFramePanelEffects } from '@blocksuite/affine-fragment-frame-panel/effects';
import { effects as fragmentOutlineEffects } from '@blocksuite/affine-fragment-outline/effects';
export function effects() {
blockRootEffects();
fragmentDocTitleEffects();
fragmentFramePanelEffects();
fragmentOutlineEffects();
}
@@ -0,0 +1,48 @@
import {
RootBlockHtmlAdapterExtension,
RootBlockMarkdownAdapterExtension,
RootBlockNotionHtmlAdapterExtension,
} from '@blocksuite/affine-block-root';
import {
type StoreExtensionContext,
StoreExtensionProvider,
} from '@blocksuite/affine-ext-loader';
import { RootBlockSchemaExtension } from '@blocksuite/affine-model';
import type { ExtensionType } from '@blocksuite/store';
const defaultBlockHtmlAdapterMatchers = [RootBlockHtmlAdapterExtension];
const defaultBlockMarkdownAdapterMatchers = [RootBlockMarkdownAdapterExtension];
const defaultBlockNotionHtmlAdapterMatchers: ExtensionType[] = [
RootBlockNotionHtmlAdapterExtension,
];
function getHtmlAdapterExtensions(): ExtensionType[] {
return [...defaultBlockHtmlAdapterMatchers];
}
function getMarkdownAdapterExtensions(): ExtensionType[] {
return [...defaultBlockMarkdownAdapterMatchers];
}
function getNotionHtmlAdapterExtensions(): ExtensionType[] {
return [...defaultBlockNotionHtmlAdapterMatchers];
}
const MigratingStoreExtensions: ExtensionType[] = [
RootBlockSchemaExtension,
getHtmlAdapterExtensions(),
getMarkdownAdapterExtensions(),
getNotionHtmlAdapterExtensions(),
].flat();
export class MigratingStoreExtension extends StoreExtensionProvider {
override name = 'migrating';
override setup(context: StoreExtensionContext) {
super.setup(context);
context.register(MigratingStoreExtensions);
}
}
@@ -0,0 +1,42 @@
import {
type ViewExtensionContext,
ViewExtensionProvider,
} from '@blocksuite/affine-ext-loader';
import { effects } from './effects';
import {
MigratingEdgelessEditorBlockSpecs,
MigratingPageEditorBlockSpecs,
MigratingPreviewEdgelessEditorBlockSpecs,
MigratingPreviewPageEditorBlockSpecs,
} from './migrating';
export class MigratingViewExtension extends ViewExtensionProvider {
override name = 'migrating';
override effect() {
super.effect();
effects();
}
override setup(context: ViewExtensionContext) {
super.setup(context);
const scope = context.scope;
if (scope === 'preview-page') {
context.register(MigratingPreviewPageEditorBlockSpecs);
return;
}
if (scope === 'preview-edgeless') {
context.register(MigratingPreviewEdgelessEditorBlockSpecs);
return;
}
if (scope === 'page' || scope === 'mobile-page') {
context.register(MigratingPageEditorBlockSpecs);
return;
}
if (scope === 'edgeless' || scope === 'mobile-edgeless') {
context.register(MigratingEdgelessEditorBlockSpecs);
return;
}
}
}
@@ -0,0 +1,26 @@
import {
EdgelessBuiltInSpecs,
PageRootBlockSpec,
PreviewEdgelessRootBlockSpec,
PreviewPageRootBlockSpec,
ReadOnlyClipboard,
} from '@blocksuite/affine-block-root';
import type { ExtensionType } from '@blocksuite/store';
export const MigratingEdgelessEditorBlockSpecs: ExtensionType[] = [
EdgelessBuiltInSpecs,
].flat();
export const MigratingPageEditorBlockSpecs: ExtensionType[] = [
PageRootBlockSpec,
].flat();
export const MigratingPreviewEdgelessEditorBlockSpecs: ExtensionType[] = [
PreviewEdgelessRootBlockSpec,
ReadOnlyClipboard,
].flat();
export const MigratingPreviewPageEditorBlockSpecs: ExtensionType[] = [
PreviewPageRootBlockSpec,
ReadOnlyClipboard,
].flat();
@@ -7,14 +7,12 @@ 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';
import { ListStoreExtension } from '@blocksuite/affine-block-list/store';
import { NoteStoreExtension } from '@blocksuite/affine-block-note/store';
import { ParagraphStoreExtension } from '@blocksuite/affine-block-paragraph/store';
import { RootStoreExtension } from '@blocksuite/affine-block-root/store';
import { SurfaceStoreExtension } from '@blocksuite/affine-block-surface/store';
import { SurfaceRefStoreExtension } from '@blocksuite/affine-block-surface-ref/store';
import { TableStoreExtension } from '@blocksuite/affine-block-table/store';
@@ -31,6 +29,8 @@ import { LinkStoreExtension } from '@blocksuite/affine-inline-link/store';
import { InlinePresetStoreExtension } from '@blocksuite/affine-inline-preset/store';
import { ReferenceStoreExtension } from '@blocksuite/affine-inline-reference/store';
import { MigratingStoreExtension } from './migrating-store';
export function getInternalStoreExtensions() {
return [
FoundationStoreExtension,
@@ -44,7 +44,6 @@ export function getInternalStoreExtensions() {
DividerStoreExtension,
EdgelessTextStoreExtension,
EmbedStoreExtension,
EmbedDocStoreExtension,
FrameStoreExtension,
ImageStoreExtension,
LatexStoreExtension,
@@ -54,7 +53,6 @@ export function getInternalStoreExtensions() {
SurfaceRefStoreExtension,
TableStoreExtension,
SurfaceStoreExtension,
RootStoreExtension,
FootnoteStoreExtension,
LinkStoreExtension,
@@ -68,5 +66,7 @@ export function getInternalStoreExtensions() {
ConnectorStoreExtension,
GroupStoreExtension,
TextStoreExtension,
MigratingStoreExtension,
];
}
+3 -12
View File
@@ -7,21 +7,16 @@ 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';
import { ListViewExtension } from '@blocksuite/affine-block-list/view';
import { NoteViewExtension } from '@blocksuite/affine-block-note/view';
import { ParagraphViewExtension } from '@blocksuite/affine-block-paragraph/view';
import { RootViewExtension } from '@blocksuite/affine-block-root/view';
import { SurfaceViewExtension } from '@blocksuite/affine-block-surface/view';
import { SurfaceRefViewExtension } from '@blocksuite/affine-block-surface-ref/view';
import { TableViewExtension } from '@blocksuite/affine-block-table/view';
import { FoundationViewExtension } from '@blocksuite/affine-foundation/view';
import { DocTitleViewExtension } from '@blocksuite/affine-fragment-doc-title/view';
import { FramePanelViewExtension } from '@blocksuite/affine-fragment-frame-panel/view';
import { OutlineViewExtension } from '@blocksuite/affine-fragment-outline/view';
import { BrushViewExtension } from '@blocksuite/affine-gfx-brush/view';
import { ConnectorViewExtension } from '@blocksuite/affine-gfx-connector/view';
import { GroupViewExtension } from '@blocksuite/affine-gfx-group/view';
@@ -52,6 +47,8 @@ import { SlashMenuViewExtension } from '@blocksuite/affine-widget-slash-menu/vie
import { ToolbarViewExtension } from '@blocksuite/affine-widget-toolbar/view';
import { ViewportOverlayViewExtension } from '@blocksuite/affine-widget-viewport-overlay/view';
import { MigratingViewExtension } from './migrating-view';
export function getInternalViewExtensions() {
return [
FoundationViewExtension,
@@ -78,7 +75,6 @@ export function getInternalViewExtensions() {
DividerViewExtension,
EdgelessTextViewExtension,
EmbedViewExtension,
EmbedDocViewExtension,
FrameViewExtension,
ImageViewExtension,
LatexViewExtension,
@@ -88,7 +84,6 @@ export function getInternalViewExtensions() {
SurfaceRefViewExtension,
TableViewExtension,
SurfaceViewExtension,
RootViewExtension,
// Inline
FootnoteViewExtension,
@@ -102,6 +97,7 @@ export function getInternalViewExtensions() {
DragHandleViewExtension,
EdgelessAutoConnectViewExtension,
EdgelessToolbarViewExtension,
MigratingViewExtension,
FrameTitleViewExtension,
KeyboardToolbarViewExtension,
LinkedDocViewExtension,
@@ -112,10 +108,5 @@ export function getInternalViewExtensions() {
ViewportOverlayViewExtension,
EdgelessZoomToolbarViewExtension,
PageDraggingAreaViewExtension,
// Fragment
DocTitleViewExtension,
FramePanelViewExtension,
OutlineViewExtension,
];
}
@@ -1 +0,0 @@
export * from '@blocksuite/affine-fragment-doc-title/view';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-fragment-frame-panel/view';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-fragment-outline/view';
-1
View File
@@ -16,7 +16,6 @@
{ "path": "../blocks/divider" },
{ "path": "../blocks/edgeless-text" },
{ "path": "../blocks/embed" },
{ "path": "../blocks/embed-doc" },
{ "path": "../blocks/frame" },
{ "path": "../blocks/image" },
{ "path": "../blocks/latex" },
@@ -10,6 +10,7 @@
"author": "toeverything",
"license": "MIT",
"dependencies": {
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
@@ -20,11 +21,10 @@
"@blocksuite/icons": "^2.2.12",
"@blocksuite/std": "workspace:*",
"@blocksuite/store": "workspace:*",
"@blocksuite/sync": "workspace:*",
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"file-type": "^20.0.0",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
@@ -1,11 +1,9 @@
import { getEmbedCardIcons } from '@blocksuite/affine-block-embed';
import {
CaptionedBlockComponent,
SelectedStyle,
} from '@blocksuite/affine-components/caption';
import {
getAttachmentFileIcon,
getLoadingIconWith,
} from '@blocksuite/affine-components/icons';
import { getAttachmentFileIcon } from '@blocksuite/affine-components/icons';
import { Peekable } from '@blocksuite/affine-components/peek';
import { toast } from '@blocksuite/affine-components/toast';
import {
@@ -37,9 +35,7 @@ import { when } from 'lit/directives/when.js';
import { AttachmentEmbedProvider } from './embed';
import { styles } from './styles';
import { downloadAttachmentBlob, refreshData } from './utils';
type State = 'loading' | 'uploading' | 'warning' | 'oversize' | 'none';
@Peekable({
enableOn: ({ model }: AttachmentBlockComponent) => {
return !model.doc.readonly && model.props.type.endsWith('pdf');
@@ -106,7 +102,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
}
determineState = (
downloading: boolean,
loading: boolean,
uploading: boolean,
overSize: boolean,
error: boolean
@@ -114,7 +110,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
if (overSize) return 'oversize';
if (error) return 'warning';
if (uploading) return 'uploading';
if (downloading) return 'loading';
if (loading) return 'loading';
return 'none';
};
@@ -299,7 +295,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
const cardStyle = style ?? AttachmentBlockStyles[1];
const theme = this.std.get(ThemeProvider).theme$.value;
const loadingIcon = getLoadingIconWith(theme);
const { LoadingIcon } = getEmbedCardIcons(theme);
const blobState = this.blobState$.value;
const {
@@ -310,8 +306,8 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
} = blobState;
const warning = !overSize && Boolean(errorMessage);
const error = overSize || warning;
const state = this.determineState(downloading, uploading, overSize, error);
const loading = state === 'loading' || state === 'uploading';
const loading = !error && downloading;
const state = this.determineState(loading, uploading, overSize, error);
const classInfo = {
'affine-attachment-card': true,
@@ -321,7 +317,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
};
const icon = loading
? loadingIcon
? LoadingIcon
: error
? WarningIcon()
: AttachmentIcon();
@@ -117,11 +117,6 @@ export const styles = css`
}
}
.affine-attachment-card.loading,
.affine-attachment-card.error {
background: ${unsafeCSSVarV2('layer/background/secondary')};
}
.affine-attachment-card.cubeThick {
flex-direction: column-reverse;
@@ -7,6 +7,7 @@
},
"include": ["./src"],
"references": [
{ "path": "../embed" },
{ "path": "../surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
@@ -15,7 +16,6 @@
{ "path": "../../widgets/slash-menu" },
{ "path": "../../../framework/global" },
{ "path": "../../../framework/std" },
{ "path": "../../../framework/store" },
{ "path": "../../../framework/sync" }
{ "path": "../../../framework/store" }
]
}
@@ -11,7 +11,6 @@
"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:*",
@@ -24,7 +23,7 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
@@ -32,7 +31,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
"vitest": "3.1.2"
"vitest": "3.1.1"
},
"exports": {
".": "./src/index.ts",
@@ -140,16 +140,4 @@ Some text in between
`.trim();
expect(footnoteUrlPreprocessor(input)).toBe(expected);
});
it('should encode partial encoded URLs in footnote definitions', () => {
const input = `
[^ref]: {"type":"url","url":"https://zh.wikipedia.org/zh-hans/%E5%B0%8F%E7%B1%B3SU7"}
[^ref2]: {"type":"url","url":"https://www.dw.com/zh/%E5%B0%8F%E7%B1%B3%E9%A6%96%E6%AC%BE%E6%B1%BD%E8%BD%A6%E5%8F%91%E5%B8%83-su7%E8%B5%B7%E4%BB%B72159%E4%B8%87%E5%85%83/a-68693432"}
`.trim();
const expected = `
[^ref]: {"type":"url","url":"https%3A%2F%2Fzh.wikipedia.org%2Fzh-hans%2F%25E5%25B0%258F%25E7%25B1%25B3SU7"}
[^ref2]: {"type":"url","url":"https%3A%2F%2Fwww.dw.com%2Fzh%2F%25E5%25B0%258F%25E7%25B1%25B3%25E9%25A6%2596%25E6%25AC%25BE%25E6%25B1%25BD%25E8%25BD%25A6%25E5%258F%2591%25E5%25B8%2583-su7%25E8%25B5%25B7%25E4%25BB%25B72159%25E4%25B8%2587%25E5%2585%2583%2Fa-68693432"}
`.trim();
expect(footnoteUrlPreprocessor(input)).toBe(expected);
});
});
@@ -23,17 +23,12 @@ type FootnoteDefinition = {
content: FootNoteReferenceParams;
};
/**
* Check if a URL is already encoded with encodeURIComponent to avoid markdown link parsing
* @example
* https://example.com/path%20with%20spaces should return false
* https://example.com/ should return false
* https%3A%2F%2Fexample.com%2F should return true
*/
function isFullyEncoded(uri: string): boolean {
// Check if a URL is already encoded with encodeURIComponent
function isEncoded(uri: string): boolean {
try {
// Should check if the components of the URI are fully encoded
return uri === encodeURIComponent(decodeURIComponent(uri));
// If decoding produces a different result than the original,
// then the URI contains encoded characters
return uri !== decodeURIComponent(uri);
} catch {
// If decoding fails, the URI contains invalid percent-encoding
return true;
@@ -199,10 +194,10 @@ class FootnoteParser {
// Process URLs in footnote content
private processUrls(footnote: FootnoteDefinition): FootnoteDefinition {
const content = footnote.content;
if (content.url && !isFullyEncoded(content.url)) {
if (content.url && !isEncoded(content.url)) {
content.url = encodeURIComponent(content.url);
}
if (content.favicon && !isFullyEncoded(content.favicon)) {
if (content.favicon && !isEncoded(content.favicon)) {
content.favicon = encodeURIComponent(content.favicon);
}
return footnote;
@@ -2,16 +2,10 @@ import {
CaptionedBlockComponent,
SelectedStyle,
} from '@blocksuite/affine-components/caption';
import type {
BookmarkBlockModel,
LinkPreviewData,
} from '@blocksuite/affine-model';
import {
DocModeProvider,
LinkPreviewerService,
} from '@blocksuite/affine-shared/services';
import type { BookmarkBlockModel } from '@blocksuite/affine-model';
import { DocModeProvider } from '@blocksuite/affine-shared/services';
import { BlockSelection } from '@blocksuite/std';
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
import { computed, type ReadonlySignal } from '@preact/signals-core';
import { html } from 'lit';
import { property, query } from 'lit/decorators.js';
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
@@ -34,62 +28,6 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
protected containerStyleMap!: ReturnType<typeof styleMap>;
/**
* @description Local link preview data
* When the doc is in readonly mode, and the link preview data are not provided (stored in the block model),
* We will use the local link preview data fetched from the link previewer service to render the block.
*/
private readonly _localLinkPreview$ = signal<LinkPreviewData>({
icon: null,
title: null,
description: null,
image: null,
});
/**
* @description Link preview data for actual rendering
* When the doc is not in readonly mode, and the link preview data are provided (stored in the block model),
* We will use the model props to render the block.
* Otherwise, we will use the local link preview data to render the block.
*/
linkPreview$ = computed(() => {
const modelProps = this.model.props;
const local = this._localLinkPreview$.value;
return {
icon: modelProps.icon$.value ?? local.icon ?? null,
title: modelProps.title$.value ?? local.title ?? null,
description: modelProps.description$.value ?? local.description ?? null,
image: modelProps.image$.value ?? local.image ?? null,
};
});
private readonly _updateLocalLinkPreview = () => {
// cancel any inflight request
this._fetchAbortController?.abort();
this._fetchAbortController = new AbortController();
this.loading = true;
this.error = false;
this.std.store
.get(LinkPreviewerService)
.query(this.model.props.url, this._fetchAbortController.signal)
.then(data => {
this._localLinkPreview$.value = {
icon: data.icon ?? null,
title: data.title ?? null,
description: data.description ?? null,
image: data.image ?? null,
};
})
.catch(() => {
this.error = true;
})
.finally(() => {
this.loading = false;
});
};
selectBlock = () => {
const selectionManager = this.std.selection;
const blockSelection = selectionManager.create(BlockSelection, {
@@ -119,30 +57,17 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
);
}
handleClick = (event: MouseEvent) => {
event.stopPropagation();
if (this.model.parent?.flavour !== 'affine:surface' && !this.doc.readonly) {
this.selectBlock();
}
};
handleDoubleClick = (event: MouseEvent) => {
event.stopPropagation();
this.open();
};
private readonly _renderCitationView = () => {
const { url, footnoteIdentifier } = this.model.props;
const { icon, title, description } = this.linkPreview$.value;
const { title, description, url, icon, footnoteIdentifier } =
this.model.props;
return html`
<affine-citation-card
.icon=${icon}
.citationTitle=${title || url}
.citationContent=${description}
.citationIdentifier=${footnoteIdentifier}
.onClickCallback=${this.handleClick}
.onDoubleClickCallback=${this.handleDoubleClick}
.onClickCallback=${this.selectBlock}
.onDoubleClickCallback=${this.open}
.active=${this.selected$.value}
></affine-citation-card>
`;
@@ -172,17 +97,10 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
this.contentEditable = 'false';
if (
(!this.model.props.description && !this.model.props.title) ||
(!this.model.props.image && this.model.props.style === 'vertical')
) {
// When the doc is readonly, and the preview data not provided
// We should fetch the preview data and update the local link preview data
if (!this.model.props.description && !this.model.props.title) {
if (this.doc.readonly) {
this._updateLocalLinkPreview();
return;
}
// Otherwise, we should refresh the data to the model props
this.refreshData();
}
@@ -1,9 +1,9 @@
import { insertEmbedIframeWithUrlCommand } from '@blocksuite/affine-block-embed';
import {
type InsertedLinkType,
insertEmbedIframeWithUrlCommand,
insertEmbedLinkedDocCommand,
type LinkableFlavour,
} from '@blocksuite/affine-block-embed-doc';
} from '@blocksuite/affine-block-embed';
import { QuickSearchProvider } from '@blocksuite/affine-shared/services';
import type { Command } from '@blocksuite/std';
@@ -18,6 +18,20 @@ export class BookmarkCard extends SignalWatcher(
) {
static override styles = styles;
private _handleClick(event: MouseEvent) {
event.stopPropagation();
const model = this.bookmark.model;
if (model.parent?.flavour !== 'affine:surface') {
this.bookmark.selectBlock();
}
}
private _handleDoubleClick(event: MouseEvent) {
event.stopPropagation();
this.bookmark.open();
}
override connectedCallback(): void {
super.connectedCallback();
@@ -35,9 +49,8 @@ export class BookmarkCard extends SignalWatcher(
}
override render() {
const { url, style } = this.bookmark.model.props;
const { icon, title, description, image } =
this.bookmark.linkPreview$.value;
const { icon, title, url, description, image, style } =
this.bookmark.model.props;
const cardClassMap = classMap({
loading: this.loading,
@@ -85,8 +98,8 @@ export class BookmarkCard extends SignalWatcher(
return html`
<div
class="affine-bookmark-card ${cardClassMap}"
@click=${this.bookmark.handleClick}
@dblclick=${this.bookmark.handleDoubleClick}
@click=${this._handleClick}
@dblclick=${this._handleDoubleClick}
>
<div class="affine-bookmark-content">
<div class="affine-bookmark-content-title">
@@ -1,4 +1,3 @@
import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
import {
@@ -6,7 +5,6 @@ import {
SlashMenuConfigIdentifier,
} from '@blocksuite/affine-widget-slash-menu';
import { LinkIcon } from '@blocksuite/icons/lit';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import type { ExtensionType } from '@blocksuite/store';
import { LinkTooltip } from './tooltips';
@@ -35,13 +33,7 @@ const bookmarkSlashMenuConfig: SlashMenuConfig = {
host,
'Links',
'The added link will be displayed as a card view.',
{ mode: 'page', parentModel, index },
({ mode }) => {
if (mode === 'edgeless') {
const gfx = std.get(GfxControllerIdentifier);
gfx.tool.setTool(DefaultTool);
}
}
{ mode: 'page', parentModel, index }
)
.then(() => {
if (model.text?.length === 0) {
@@ -8,7 +8,6 @@
"include": ["./src"],
"references": [
{ "path": "../embed" },
{ "path": "../embed-doc" },
{ "path": "../surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
@@ -25,7 +25,7 @@
"@floating-ui/dom": "^1.6.10",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"@types/mdast": "^4.0.4",
"emoji-mart": "^5.6.0",
"lit": "^3.2.0",
@@ -1,88 +0,0 @@
import { CalloutBlockSchema } from '@blocksuite/affine-model';
import {
BlockMarkdownAdapterExtension,
type BlockMarkdownAdapterMatcher,
getCalloutEmoji,
isCalloutNode,
} from '@blocksuite/affine-shared/adapters';
import { nanoid } from '@blocksuite/store';
// Currently, the callout block children can only be paragraph block or list block
// In mdast, the node types are `paragraph`, `list`, `heading`, `blockquote`
const CALLOUT_BLOCK_CHILDREN_TYPES = new Set([
'paragraph',
'list',
'heading',
'blockquote',
]);
export const calloutBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
flavour: CalloutBlockSchema.model.flavour,
toMatch: o => isCalloutNode(o.node),
fromMatch: o => o.node.flavour === CalloutBlockSchema.model.flavour,
toBlockSnapshot: {
enter: (o, context) => {
if (!o.node.data || !isCalloutNode(o.node)) {
return;
}
// Currently, the callout block children can only be a paragraph or a list
// So we should filter out the other children
o.node.children = o.node.children.filter(child =>
CALLOUT_BLOCK_CHILDREN_TYPES.has(child.type)
);
const { walkerContext } = context;
const calloutEmoji = getCalloutEmoji(o.node);
walkerContext.openNode(
{
type: 'block',
id: nanoid(),
flavour: CalloutBlockSchema.model.flavour,
props: {
emoji: calloutEmoji,
},
children: [],
},
'children'
);
},
leave: (o, context) => {
const { walkerContext } = context;
if (isCalloutNode(o.node)) {
walkerContext.closeNode();
}
},
},
fromBlockSnapshot: {
enter: (o, context) => {
const emoji = o.node.props.emoji as string;
const { walkerContext } = context;
walkerContext
.openNode(
{
type: 'blockquote',
children: [],
},
'children'
)
.openNode({
type: 'paragraph',
children: [
{
type: 'text',
value: `[!${emoji}]`,
},
],
})
.closeNode();
},
leave: (_, context) => {
const { walkerContext } = context;
walkerContext.closeNode();
},
},
};
export const CalloutBlockMarkdownAdapterExtension =
BlockMarkdownAdapterExtension(calloutBlockMarkdownAdapterMatcher);
@@ -3,7 +3,6 @@ import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
import { CalloutBlockMarkdownAdapterExtension } from './adapters/markdown';
import { CalloutKeymapExtension } from './callout-keymap';
import { calloutSlashMenuConfig } from './configs/slash-menu';
@@ -12,5 +11,4 @@ export const CalloutBlockSpec: ExtensionType[] = [
BlockViewExtension('affine:callout', literal`affine-callout`),
CalloutKeymapExtension,
SlashMenuConfigExtension('affine:callout', calloutSlashMenuConfig),
CalloutBlockMarkdownAdapterExtension,
];
@@ -4,14 +4,11 @@ import {
} from '@blocksuite/affine-ext-loader';
import { CalloutBlockSchemaExtension } from '@blocksuite/affine-model';
import { CalloutBlockMarkdownAdapterExtension } from './adapters/markdown';
export class CalloutStoreExtension extends StoreExtensionProvider {
override name = 'affine-callout-block';
override setup(context: StoreExtensionContext) {
super.setup(context);
context.register(CalloutBlockSchemaExtension);
context.register(CalloutBlockMarkdownAdapterExtension);
}
}
+1 -1
View File
@@ -27,7 +27,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
+1 -3
View File
@@ -41,6 +41,7 @@ export class CodeBlockViewExtension extends ViewExtensionProvider {
FlavourExtension('affine:code'),
CodeBlockHighlighter,
BlockViewExtension('affine:code', literal`affine-code`),
codeToolbarWidget,
SlashMenuConfigExtension('affine:code', codeSlashMenuConfig),
CodeKeymapExtension,
...getCodeClipboardExtensions(),
@@ -49,8 +50,5 @@ export class CodeBlockViewExtension extends ViewExtensionProvider {
CodeBlockInlineManagerExtension,
CodeBlockUnitSpecExtension,
]);
if (!this.isMobile(context.scope)) {
context.register(codeToolbarWidget);
}
}
}
@@ -24,7 +24,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
@@ -27,7 +27,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"@types/mdast": "^4.0.4",
"@vanilla-extract/css": "^1.17.0",
"date-fns": "^4.0.0",
@@ -14,19 +14,7 @@ import {
} from '@blocksuite/icons/lit';
import type { BlockModel } from '@blocksuite/store';
import type { TemplateResult } from 'lit';
const icons: Record<string, TemplateResult> = {
text: TextIcon(),
quote: QuoteIcon(),
h1: Heading1Icon(),
h2: Heading2Icon(),
h3: Heading3Icon(),
h4: Heading4Icon(),
h5: Heading5Icon(),
h6: Heading6Icon(),
bulleted: BulletedListIcon(),
numbered: NumberedListIcon(),
todo: CheckBoxCheckLinearIcon(),
};
export const getIcon = (
model: BlockModel & {
props: {
@@ -36,10 +24,27 @@ export const getIcon = (
): TemplateResult => {
if (model.flavour === 'affine:paragraph') {
const type = model.props.type as ParagraphType;
return icons[type] ?? TextIcon();
return (
{
text: TextIcon(),
quote: QuoteIcon(),
h1: Heading1Icon(),
h2: Heading2Icon(),
h3: Heading3Icon(),
h4: Heading4Icon(),
h5: Heading5Icon(),
h6: Heading6Icon(),
} as Record<ParagraphType, TemplateResult>
)[type];
}
if (model.flavour === 'affine:list') {
return icons[model.props.type ?? 'bulleted'] ?? BulletedListIcon();
return (
{
bulleted: BulletedListIcon(),
numbered: NumberedListIcon(),
todo: CheckBoxCheckLinearIcon(),
}[model.props.type ?? 'bulleted'] ?? BulletedListIcon()
);
}
return TextIcon();
};
@@ -1,6 +1,10 @@
import type { MenuOptions } from '@blocksuite/affine-components/context-menu';
import { type DatabaseBlockModel } from '@blocksuite/affine-model';
import { ConfigExtensionFactory } from '@blocksuite/std';
import type { DatabaseViewExtensionOptions } from './view';
export interface DatabaseOptionsConfig {
configure: (model: DatabaseBlockModel, options: MenuOptions) => MenuOptions;
}
export const DatabaseConfigExtension =
ConfigExtensionFactory<DatabaseViewExtensionOptions>('affine:database');
ConfigExtensionFactory<DatabaseOptionsConfig>('affine:database');
@@ -71,15 +71,11 @@ export class DatabaseBlockDataSource extends DataSourceBase {
override featureFlags$: ReadonlySignal<DatabaseFlags> = computed(() => {
const featureFlagService = this.doc.get(FeatureFlagService);
const enableNumberFormat = featureFlagService.getFlag(
const flag = featureFlagService.getFlag(
'enable_database_number_formatting'
);
const enableTableVirtualScroll = featureFlagService.getFlag(
'enable_table_virtual_scroll'
);
return {
enable_number_formatting: enableNumberFormat ?? false,
enable_table_virtual_scroll: enableTableVirtualScroll ?? false,
enable_number_formatting: flag ?? false,
};
});
@@ -46,7 +46,10 @@ import { computed, signal } from '@preact/signals-core';
import { css, html, nothing, unsafeCSS } from 'lit';
import { popSideDetail } from './components/layout.js';
import { DatabaseConfigExtension } from './config.js';
import {
DatabaseConfigExtension,
type DatabaseOptionsConfig,
} from './config.js';
import { HostContextKey } from './context/host-context.js';
import { DatabaseBlockDataSource } from './data-source.js';
import { BlockRenderer } from './detail-panel/block-renderer.js';
@@ -54,7 +57,6 @@ import { NoteRenderer } from './detail-panel/note-renderer.js';
import { DatabaseSelection } from './selection.js';
import { currentViewStorage } from './utils/current-view.js';
import { getSingleDocIdFromText } from './utils/title-doc.js';
import type { DatabaseViewExtensionOptions } from './view';
export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBlockModel> {
static override styles = css`
@@ -343,7 +345,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
return this._dataSource;
}
get optionsConfig(): DatabaseViewExtensionOptions {
get optionsConfig(): DatabaseOptionsConfig {
return {
configure: (_model, options) => options,
...this.std.getOptional(DatabaseConfigExtension.identifier),
@@ -12,7 +12,7 @@ import { BaseCellRenderer } from '@blocksuite/data-view';
import { IS_MAC } from '@blocksuite/global/env';
import { LinkedPageIcon } from '@blocksuite/icons/lit';
import type { BlockSnapshot, DeltaInsert, Text } from '@blocksuite/store';
import { computed, signal } from '@preact/signals-core';
import { signal } from '@preact/signals-core';
import { property } from 'lit/decorators.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { html } from 'lit/static-html.js';
@@ -217,23 +217,17 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
super.firstUpdated(props);
this.richText.value?.updateComplete
.then(() => {
if (this.richText.value) {
this.disposables.addFromEvent(
this.richText.value,
'copy',
this._onCopy
);
this.disposables.addFromEvent(
this.richText.value,
'cut',
this._onCut
);
this.disposables.addFromEvent(
this.richText.value,
'paste',
this._onPaste
);
}
this.disposables.addFromEvent(
this.richText.value,
'copy',
this._onCopy
);
this.disposables.addFromEvent(this.richText.value, 'cut', this._onCut);
this.disposables.addFromEvent(
this.richText.value,
'paste',
this._onPaste
);
})
.catch(console.error);
}
@@ -267,14 +261,7 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
class="${titleRichTextStyle}"
></rich-text>`;
}
icon$ = computed(() => {
const iconColumn = this.view.mainProperties$.value.iconColumn;
if (!iconColumn) return;
const icon = this.view.cellValueGet(this.cell.rowId, iconColumn) as string;
if (!icon) return;
return icon;
});
renderIcon() {
if (!this.showIcon) {
return;
@@ -284,7 +271,10 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
${LinkedPageIcon({})}
</div>`;
}
const icon = this.icon$.value;
const iconColumn = this.view.mainProperties$.value.iconColumn;
if (!iconColumn) return;
const icon = this.view.cellValueGet(this.cell.rowId, iconColumn) as string;
if (!icon) return;
return html` <div class="${headerAreaIconStyle}">${icon}</div>`;
+2 -25
View File
@@ -1,51 +1,28 @@
import type { MenuOptions } from '@blocksuite/affine-components/context-menu';
import {
type ViewExtensionContext,
ViewExtensionProvider,
} from '@blocksuite/affine-ext-loader';
import { DatabaseBlockModel } from '@blocksuite/affine-model';
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
import { literal } from 'lit/static-html.js';
import { z } from 'zod';
import { DatabaseConfigExtension } from './config';
import { databaseSlashMenuConfig } from './configs/slash-menu.js';
import { effects } from './effects';
const optionsSchema = z.object({
configure: z
.function()
.args(z.instanceof(DatabaseBlockModel), z.custom<MenuOptions>())
.returns(z.custom<MenuOptions>()),
});
export type DatabaseViewExtensionOptions = z.infer<typeof optionsSchema>;
export class DatabaseViewExtension extends ViewExtensionProvider<DatabaseViewExtensionOptions> {
export class DatabaseViewExtension extends ViewExtensionProvider {
override name = 'affine-database-block';
override schema = optionsSchema;
override effect() {
super.effect();
effects();
}
override setup(
context: ViewExtensionContext,
options?: DatabaseViewExtensionOptions
) {
override setup(context: ViewExtensionContext) {
super.setup(context);
context.register([
FlavourExtension('affine:database'),
BlockViewExtension('affine:database', literal`affine-database`),
SlashMenuConfigExtension('affine:database', databaseSlashMenuConfig),
]);
if (options) {
context.register(
DatabaseConfigExtension({ configure: options.configure })
);
}
}
}
@@ -20,7 +20,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
@@ -26,7 +26,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
@@ -1,54 +0,0 @@
{
"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"
}
@@ -1,213 +0,0 @@
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;
}
@@ -1,37 +0,0 @@
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;
}
}
@@ -1,18 +0,0 @@
import {
EmbedSyncedDocBlockSchema,
type EmbedSyncedDocModel,
} from '@blocksuite/affine-model';
import { type BlockStdScope, ConfigExtensionFactory } from '@blocksuite/std';
import type { TemplateResult } from 'lit';
export type EmbedSyncedDocConfig = {
edgelessHeader: (context: {
model: EmbedSyncedDocModel;
std: BlockStdScope;
}) => TemplateResult;
};
export const EmbedSyncedDocConfigExtension =
ConfigExtensionFactory<EmbedSyncedDocConfig>(
EmbedSyncedDocBlockSchema.model.flavour
);
@@ -1,6 +0,0 @@
export * from './adapters';
export * from './configs';
export * from './edgeless-clipboard-config';
export * from './embed-synced-doc-block';
export * from './embed-synced-doc-spec';
export { SYNCED_MIN_HEIGHT, SYNCED_MIN_WIDTH } from '@blocksuite/affine-model';
@@ -1,78 +0,0 @@
import {
EmbedSyncedDocBlockSchema,
SYNCED_DEFAULT_MAX_HEIGHT,
SYNCED_MIN_HEIGHT,
} from '@blocksuite/affine-model';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { clamp } from '@blocksuite/global/gfx';
import { LifeCycleWatcher } from '@blocksuite/std';
import { EmbedEdgelessSyncedDocBlockComponent } from './embed-edgeless-synced-doc-block';
export class HeightInitializationExtension extends LifeCycleWatcher {
static override key = 'embed-synced-doc-block-height-initialization';
override mounted() {
super.mounted();
this._disposables.add(
this.std.store.slots.blockUpdated.subscribe(payload => {
if (
payload.type === 'add' &&
payload.isLocal &&
payload.flavour === EmbedSyncedDocBlockSchema.model.flavour &&
payload.model.parent?.flavour === 'affine:surface'
) {
this._initQueue.add(payload.id);
}
})
);
this._disposables.add(
this.std.view.viewUpdated.subscribe(payload => {
if (
payload.type === 'block' &&
payload.method === 'add' &&
this._initQueue.has(payload.id)
) {
this._initQueue.delete(payload.id);
if (!(payload.view instanceof EmbedEdgelessSyncedDocBlockComponent)) {
return;
}
const block = payload.view;
block.contentElement
.then(contentEl => {
if (!contentEl) return;
const resizeObserver = new ResizeObserver(() => {
const headerHeight =
block.headerWrapper?.getBoundingClientRect().height ?? 0;
const contentHeight = contentEl.getBoundingClientRect().height;
const { x, y, w } = block.model.elementBound;
const h = clamp(
(headerHeight + contentHeight) / block.gfx.viewport.zoom,
SYNCED_MIN_HEIGHT,
SYNCED_DEFAULT_MAX_HEIGHT
);
block.model.xywh$.value = `[${x},${y},${w},${h}]`;
resizeObserver.unobserve(contentEl);
});
resizeObserver.observe(contentEl);
})
.catch(console.error);
}
})
);
}
override unmounted(): void {
this._disposables.dispose();
}
private readonly _initQueue = new Set<string>();
private readonly _disposables = new DisposableGroup();
}
@@ -1,2 +0,0 @@
export * from './embed-linked-doc-block';
export * from './embed-synced-doc-block';
@@ -1,25 +0,0 @@
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);
}
}
@@ -1,36 +0,0 @@
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,
]);
}
}
}
@@ -1,23 +0,0 @@
{
"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" }
]
}
+2 -3
View File
@@ -13,7 +13,6 @@
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-gfx-pointer": "workspace:*",
"@blocksuite/affine-inline-reference": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*",
@@ -26,7 +25,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.14",
"@toeverything/theme": "^1.1.12",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
@@ -36,7 +35,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
"vitest": "3.1.2"
"vitest": "3.1.1"
},
"exports": {
".": "./src/index.ts",
@@ -1,5 +1,4 @@
import {
DefaultTool,
EdgelessCRUDIdentifier,
SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface';
@@ -83,7 +82,10 @@ export function insertEmbedCard(
surfaceBlock.model
);
gfx.tool.setTool(DefaultTool);
gfx.tool.setTool(
// @ts-expect-error FIXME: resolve after gfx tool refactor
'default'
);
gfx.selection.set({
elements: [cardId],
editing: false,
@@ -1,22 +1,30 @@
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 type { BlockStdScope } from '@blocksuite/std';
import { 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
@@ -24,6 +32,197 @@ import {
// - 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;
@@ -1,4 +1,5 @@
import {
DarkLoadingIcon,
EmbedCardDarkBannerIcon,
EmbedCardDarkCubeIcon,
EmbedCardDarkHorizontalIcon,
@@ -9,7 +10,7 @@ import {
EmbedCardLightHorizontalIcon,
EmbedCardLightListIcon,
EmbedCardLightVerticalIcon,
getLoadingIconWith,
LightLoadingIcon,
} from '@blocksuite/affine-components/icons';
import { ColorScheme } from '@blocksuite/affine-model';
import type { TemplateResult } from 'lit';
@@ -24,11 +25,9 @@ type EmbedCardIcons = {
};
export function getEmbedCardIcons(theme: ColorScheme): EmbedCardIcons {
const LoadingIcon = getLoadingIconWith(theme);
if (theme === ColorScheme.Light) {
return {
LoadingIcon,
LoadingIcon: LightLoadingIcon,
EmbedCardBannerIcon: EmbedCardLightBannerIcon,
EmbedCardHorizontalIcon: EmbedCardLightHorizontalIcon,
EmbedCardListIcon: EmbedCardLightListIcon,
@@ -37,7 +36,7 @@ export function getEmbedCardIcons(theme: ColorScheme): EmbedCardIcons {
};
} else {
return {
LoadingIcon,
LoadingIcon: DarkLoadingIcon,
EmbedCardBannerIcon: EmbedCardDarkBannerIcon,
EmbedCardHorizontalIcon: EmbedCardDarkHorizontalIcon,
EmbedCardListIcon: EmbedCardDarkListIcon,
@@ -12,8 +12,13 @@ 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';
@@ -55,6 +60,26 @@ 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
@@ -86,6 +111,11 @@ 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;
@@ -1,8 +1,6 @@
import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { FigmaDuotoneIcon } from '@blocksuite/icons/lit';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { FigmaTooltip } from './tooltips';
@@ -31,13 +29,7 @@ export const embedFigmaSlashMenuConfig: SlashMenuConfig = {
host,
'Figma',
'The added Figma link will be displayed as an embed view.',
{ mode: 'page', parentModel, index },
({ mode }) => {
if (mode === 'edgeless') {
const gfx = std.get(GfxControllerIdentifier);
gfx.tool.setTool(DefaultTool);
}
}
{ mode: 'page', parentModel, index }
);
if (model.text?.length === 0) std.store.deleteBlock(model);
})().catch(console.error);
@@ -1,8 +1,6 @@
import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { GithubDuotoneIcon } from '@blocksuite/icons/lit';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { GithubRepoTooltip } from './tooltips';
@@ -31,13 +29,7 @@ export const embedGithubSlashMenuConfig: SlashMenuConfig = {
host,
'GitHub',
'The added GitHub issue or pull request link will be displayed as a card view.',
{ mode: 'page', parentModel, index },
({ mode }) => {
if (mode === 'edgeless') {
const gfx = std.get(GfxControllerIdentifier);
gfx.tool.setTool(DefaultTool);
}
}
{ mode: 'page', parentModel, index }
);
if (model.text?.length === 0) std.store.deleteBlock(model);
})().catch(console.error);
@@ -1,5 +1,4 @@
import {
DefaultTool,
EdgelessCRUDIdentifier,
SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface';
@@ -91,7 +90,10 @@ export const insertEmbedIframeWithUrlCommand: Command<
surfaceBlock.model
);
gfx.tool.setTool(DefaultTool);
gfx.tool.setTool(
// @ts-expect-error FIXME: resolve after gfx tool refactor
'default'
);
gfx.selection.set({
elements: [newBlockId],
@@ -1,7 +1,8 @@
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'
@@ -1,4 +1,3 @@
import { toEdgelessEmbedBlock } from '@blocksuite/affine-block-embed';
import {
EdgelessCRUDIdentifier,
reassociateConnectorsCommand,
@@ -14,6 +13,7 @@ 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(
@@ -1,7 +1,3 @@
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';
@@ -43,7 +39,11 @@ import { when } from 'lit/directives/when.js';
import throttle from 'lodash-es/throttle';
import * as Y from 'yjs';
import { renderLinkedDocInCard } from '../common/render-linked-doc';
import { EmbedBlockComponent } from '../common/embed-block-element.js';
import {
RENDER_CARD_THROTTLE_MS,
renderLinkedDocInCard,
} from '../common/render-linked-doc.js';
import { SyncedDocErrorIcon } from '../embed-synced-doc-block/styles.js';
import { styles } from './styles.js';
import { getEmbedLinkedDocIcons } from './utils.js';
@@ -1,8 +1,7 @@
import { embedNoteContentStyles } from '@blocksuite/affine-block-embed';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { css, html } from 'lit';
unsafeCSSVarV2('layer/background/linkedDocOnEdgeless');
import { embedNoteContentStyles } from '../common/embed-note-content-styles.js';
export const styles = css`
.affine-embed-linked-doc-block {
@@ -12,15 +11,11 @@ export const styles = css`
height: 100%;
border-radius: 8px;
border: 1px solid var(--affine-background-tertiary-color);
background: var(--affine-background-primary-color);
user-select: none;
position: relative;
}
.affine-embed-linked-doc-block.in-canvas {
border: 1px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
background: ${unsafeCSSVarV2('layer/background/linkedDocOnEdgeless')};
}
.affine-embed-linked-doc-content {
flex-grow: 1;
height: 100%;
@@ -1,7 +1,8 @@
import {
DarkLoadingIcon,
EmbedEdgelessIcon,
EmbedPageIcon,
getLoadingIconWith,
LightLoadingIcon,
ReloadIcon,
} from '@blocksuite/affine-components/icons';
import {
@@ -50,11 +51,10 @@ export function getEmbedLinkedDocIcons(
style: (typeof EmbedLinkedDocStyles)[number]
): EmbedCardImages {
const small = style !== 'vertical';
const LoadingIcon = getLoadingIconWith(theme);
if (editorMode === 'page') {
if (theme === ColorScheme.Light) {
return {
LoadingIcon,
LoadingIcon: LightLoadingIcon,
ReloadIcon,
LinkedDocIcon: EmbedPageIcon,
LinkedDocDeletedIcon,
@@ -69,7 +69,7 @@ export function getEmbedLinkedDocIcons(
} else {
return {
ReloadIcon,
LoadingIcon,
LoadingIcon: DarkLoadingIcon,
LinkedDocIcon: EmbedPageIcon,
LinkedDocDeletedIcon,
LinkedDocEmptyBanner: small
@@ -85,7 +85,7 @@ export function getEmbedLinkedDocIcons(
if (theme === ColorScheme.Light) {
return {
ReloadIcon,
LoadingIcon,
LoadingIcon: LightLoadingIcon,
LinkedDocIcon: EmbedEdgelessIcon,
LinkedDocDeletedIcon,
LinkedDocEmptyBanner: small
@@ -99,7 +99,7 @@ export function getEmbedLinkedDocIcons(
} else {
return {
ReloadIcon,
LoadingIcon,
LoadingIcon: DarkLoadingIcon,
LinkedDocIcon: EmbedEdgelessIcon,
LinkedDocDeletedIcon,
LinkedDocEmptyBanner: small
@@ -1,8 +1,6 @@
import { DefaultTool } from '@blocksuite/affine-block-surface';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
import type { SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { LoomLogoDuotoneIcon } from '@blocksuite/icons/lit';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { LoomTooltip } from './tooltips';
@@ -31,13 +29,7 @@ export const embedLoomSlashMenuConfig: SlashMenuConfig = {
host,
'Loom',
'The added Loom video link will be displayed as an embed view.',
{ mode: 'page', parentModel, index },
({ mode }) => {
if (mode === 'edgeless') {
const gfx = std.get(GfxControllerIdentifier);
gfx.tool.setTool(DefaultTool);
}
}
{ mode: 'page', parentModel, index }
);
if (model.text?.length === 0) std.store.deleteBlock(model);
})().catch(console.error);

Some files were not shown because too many files have changed in this diff Show More