mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-17 01:56:27 +08:00
refactor(editor): unify directories naming (#11516)
**Directory Structure Changes** - Renamed multiple block-related directories by removing the "block-" prefix: - `block-attachment` → `attachment` - `block-bookmark` → `bookmark` - `block-callout` → `callout` - `block-code` → `code` - `block-data-view` → `data-view` - `block-database` → `database` - `block-divider` → `divider` - `block-edgeless-text` → `edgeless-text` - `block-embed` → `embed`
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { CodeBlockHtmlAdapterExtension } from './html.js';
|
||||
import { CodeBlockMarkdownAdapterExtensions } from './markdown/index.js';
|
||||
import { CodeBlockNotionHtmlAdapterExtension } from './notion-html.js';
|
||||
import { CodeBlockPlainTextAdapterExtension } from './plain-text.js';
|
||||
|
||||
export const CodeBlockAdapterExtensions: ExtensionType[] = [
|
||||
CodeBlockHtmlAdapterExtension,
|
||||
CodeBlockMarkdownAdapterExtensions,
|
||||
CodeBlockPlainTextAdapterExtension,
|
||||
CodeBlockNotionHtmlAdapterExtension,
|
||||
].flat();
|
||||
@@ -0,0 +1,96 @@
|
||||
import { CodeBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockHtmlAdapterExtension,
|
||||
type BlockHtmlAdapterMatcher,
|
||||
CODE_BLOCK_WRAP_KEY,
|
||||
HastUtils,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import type { DeltaInsert } from '@blocksuite/store';
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
import { bundledLanguagesInfo, codeToHast } from 'shiki';
|
||||
|
||||
export const codeBlockHtmlAdapterMatcher: BlockHtmlAdapterMatcher = {
|
||||
flavour: CodeBlockSchema.model.flavour,
|
||||
toMatch: o => HastUtils.isElement(o.node) && o.node.tagName === 'pre',
|
||||
fromMatch: o => o.node.flavour === 'affine:code',
|
||||
toBlockSnapshot: {
|
||||
enter: (o, context) => {
|
||||
if (!HastUtils.isElement(o.node)) {
|
||||
return;
|
||||
}
|
||||
const code = HastUtils.querySelector(o.node, 'code');
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
|
||||
const codeText =
|
||||
code.children.length === 1 && code.children[0].type === 'text'
|
||||
? code.children[0]
|
||||
: { ...code, tagName: 'div' };
|
||||
let codeLang = Array.isArray(code.properties?.className)
|
||||
? code.properties.className.find(
|
||||
className =>
|
||||
typeof className === 'string' && className.startsWith('code-')
|
||||
)
|
||||
: undefined;
|
||||
codeLang =
|
||||
typeof codeLang === 'string'
|
||||
? codeLang.replace('code-', '')
|
||||
: undefined;
|
||||
|
||||
const { walkerContext, deltaConverter, configs } = context;
|
||||
const wrap = configs.get(CODE_BLOCK_WRAP_KEY) === 'true';
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
type: 'block',
|
||||
id: nanoid(),
|
||||
flavour: 'affine:code',
|
||||
props: {
|
||||
language: codeLang ?? 'Plain Text',
|
||||
wrap,
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: deltaConverter.astToDelta(codeText, {
|
||||
trim: false,
|
||||
pre: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.closeNode();
|
||||
walkerContext.skipAllChildren();
|
||||
},
|
||||
},
|
||||
fromBlockSnapshot: {
|
||||
enter: async (o, context) => {
|
||||
const { walkerContext } = context;
|
||||
const rawLang = o.node.props.language as string | null;
|
||||
const matchedLang = rawLang
|
||||
? (bundledLanguagesInfo.find(
|
||||
info =>
|
||||
info.id === rawLang ||
|
||||
info.name === rawLang ||
|
||||
info.aliases?.includes(rawLang)
|
||||
)?.id ?? 'text')
|
||||
: 'text';
|
||||
|
||||
const text = (o.node.props.text as Record<string, unknown>)
|
||||
.delta as DeltaInsert[];
|
||||
const code = text.map(delta => delta.insert).join('');
|
||||
const hast = await codeToHast(code, {
|
||||
lang: matchedLang,
|
||||
theme: 'light-plus',
|
||||
});
|
||||
|
||||
walkerContext.openNode(hast as never, 'children').closeNode();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const CodeBlockHtmlAdapterExtension = BlockHtmlAdapterExtension(
|
||||
codeBlockHtmlAdapterMatcher
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './html.js';
|
||||
export * from './markdown/index.js';
|
||||
export * from './notion-html.js';
|
||||
export * from './plain-text.js';
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { CodeBlockMarkdownAdapterExtension } from './markdown.js';
|
||||
import { CodeMarkdownPreprocessorExtension } from './preprocessor.js';
|
||||
|
||||
export * from './markdown.js';
|
||||
export * from './preprocessor.js';
|
||||
|
||||
export const CodeBlockMarkdownAdapterExtensions: ExtensionType[] = [
|
||||
CodeMarkdownPreprocessorExtension,
|
||||
CodeBlockMarkdownAdapterExtension,
|
||||
];
|
||||
@@ -0,0 +1,73 @@
|
||||
import { CodeBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockMarkdownAdapterExtension,
|
||||
type BlockMarkdownAdapterMatcher,
|
||||
CODE_BLOCK_WRAP_KEY,
|
||||
type MarkdownAST,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import type { DeltaInsert } from '@blocksuite/store';
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
import type { Code } from 'mdast';
|
||||
|
||||
const isCodeNode = (node: MarkdownAST): node is Code => node.type === 'code';
|
||||
|
||||
export const codeBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
||||
flavour: CodeBlockSchema.model.flavour,
|
||||
toMatch: o => isCodeNode(o.node),
|
||||
fromMatch: o => o.node.flavour === 'affine:code',
|
||||
toBlockSnapshot: {
|
||||
enter: (o, context) => {
|
||||
if (!isCodeNode(o.node)) {
|
||||
return;
|
||||
}
|
||||
const { walkerContext, configs } = context;
|
||||
const wrap = configs.get(CODE_BLOCK_WRAP_KEY) === 'true';
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
type: 'block',
|
||||
id: nanoid(),
|
||||
flavour: 'affine:code',
|
||||
props: {
|
||||
language: o.node.lang ?? 'Plain Text',
|
||||
wrap,
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: [
|
||||
{
|
||||
insert: o.node.value,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.closeNode();
|
||||
},
|
||||
},
|
||||
fromBlockSnapshot: {
|
||||
enter: (o, context) => {
|
||||
const text = (o.node.props.text ?? { delta: [] }) as {
|
||||
delta: DeltaInsert[];
|
||||
};
|
||||
const { walkerContext } = context;
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
type: 'code',
|
||||
lang: (o.node.props.language as string) ?? null,
|
||||
meta: null,
|
||||
value: text.delta.map(delta => delta.insert).join(''),
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.closeNode();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const CodeBlockMarkdownAdapterExtension = BlockMarkdownAdapterExtension(
|
||||
codeBlockMarkdownAdapterMatcher
|
||||
);
|
||||
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
type MarkdownAdapterPreprocessor,
|
||||
MarkdownPreprocessorExtension,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
|
||||
const codePreprocessor: MarkdownAdapterPreprocessor = {
|
||||
name: 'code',
|
||||
levels: ['slice'],
|
||||
preprocess: content => {
|
||||
let codeFence = '';
|
||||
const lines = content
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
if (line.trimStart().startsWith('-')) {
|
||||
return line;
|
||||
}
|
||||
let trimmedLine = line.trimStart();
|
||||
if (!codeFence && trimmedLine.startsWith('```')) {
|
||||
codeFence = trimmedLine.substring(
|
||||
0,
|
||||
trimmedLine.lastIndexOf('```') + 3
|
||||
);
|
||||
if (codeFence.split('').every(c => c === '`')) {
|
||||
return line;
|
||||
}
|
||||
codeFence = '';
|
||||
}
|
||||
if (!codeFence && trimmedLine.startsWith('~~~')) {
|
||||
codeFence = trimmedLine.substring(
|
||||
0,
|
||||
trimmedLine.lastIndexOf('~~~') + 3
|
||||
);
|
||||
if (codeFence.split('').every(c => c === '~')) {
|
||||
return line;
|
||||
}
|
||||
codeFence = '';
|
||||
}
|
||||
if (
|
||||
!!codeFence &&
|
||||
trimmedLine.startsWith(codeFence) &&
|
||||
trimmedLine.lastIndexOf(codeFence) === 0
|
||||
) {
|
||||
codeFence = '';
|
||||
}
|
||||
if (codeFence) {
|
||||
return line;
|
||||
}
|
||||
|
||||
trimmedLine = trimmedLine.trimEnd();
|
||||
if (!trimmedLine.startsWith('<') && !trimmedLine.endsWith('>')) {
|
||||
// check if it is a url link and wrap it with the angle brackets
|
||||
// sometimes the url includes emphasis `_` that will break URL parsing
|
||||
//
|
||||
// eg. /MuawcBMT1Mzvoar09-_66?mode=page&blockIds=rL2_GXbtLU2SsJVfCSmh_
|
||||
// https://www.markdownguide.org/basic-syntax/#urls-and-email-addresses
|
||||
try {
|
||||
const valid =
|
||||
URL.canParse?.(trimmedLine) ?? Boolean(new URL(trimmedLine));
|
||||
if (valid) {
|
||||
return `<${trimmedLine}>`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
return line.replace(/^ /, ' ');
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return lines;
|
||||
},
|
||||
};
|
||||
|
||||
export const CodeMarkdownPreprocessorExtension =
|
||||
MarkdownPreprocessorExtension(codePreprocessor);
|
||||
@@ -0,0 +1,59 @@
|
||||
import { CodeBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockNotionHtmlAdapterExtension,
|
||||
type BlockNotionHtmlAdapterMatcher,
|
||||
CODE_BLOCK_WRAP_KEY,
|
||||
HastUtils,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
|
||||
export const codeBlockNotionHtmlAdapterMatcher: BlockNotionHtmlAdapterMatcher =
|
||||
{
|
||||
flavour: CodeBlockSchema.model.flavour,
|
||||
toMatch: o => HastUtils.isElement(o.node) && o.node.tagName === 'pre',
|
||||
fromMatch: () => false,
|
||||
toBlockSnapshot: {
|
||||
enter: (o, context) => {
|
||||
if (!HastUtils.isElement(o.node)) {
|
||||
return;
|
||||
}
|
||||
const code = HastUtils.querySelector(o.node, 'code');
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
const { walkerContext, deltaConverter, configs } = context;
|
||||
const wrap = configs.get(CODE_BLOCK_WRAP_KEY) === 'true';
|
||||
const codeText =
|
||||
code.children.length === 1 && code.children[0].type === 'text'
|
||||
? code.children[0]
|
||||
: { ...code, tag: 'div' };
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
type: 'block',
|
||||
id: nanoid(),
|
||||
flavour: CodeBlockSchema.model.flavour,
|
||||
props: {
|
||||
language: 'Plain Text',
|
||||
wrap,
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: deltaConverter.astToDelta(codeText, {
|
||||
trim: false,
|
||||
pre: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.closeNode();
|
||||
walkerContext.skipAllChildren();
|
||||
},
|
||||
},
|
||||
fromBlockSnapshot: {},
|
||||
};
|
||||
|
||||
export const CodeBlockNotionHtmlAdapterExtension =
|
||||
BlockNotionHtmlAdapterExtension(codeBlockNotionHtmlAdapterMatcher);
|
||||
@@ -0,0 +1,26 @@
|
||||
import { CodeBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockPlainTextAdapterExtension,
|
||||
type BlockPlainTextAdapterMatcher,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import type { DeltaInsert } from '@blocksuite/store';
|
||||
|
||||
export const codeBlockPlainTextAdapterMatcher: BlockPlainTextAdapterMatcher = {
|
||||
flavour: CodeBlockSchema.model.flavour,
|
||||
toMatch: () => false,
|
||||
fromMatch: o => o.node.flavour === CodeBlockSchema.model.flavour,
|
||||
toBlockSnapshot: {},
|
||||
fromBlockSnapshot: {
|
||||
enter: (o, context) => {
|
||||
const text = (o.node.props.text ?? { delta: [] }) as {
|
||||
delta: DeltaInsert[];
|
||||
};
|
||||
const buffer = text.delta.map(delta => delta.insert).join('');
|
||||
context.textBuffer.content += buffer;
|
||||
context.textBuffer.content += '\n';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const CodeBlockPlainTextAdapterExtension =
|
||||
BlockPlainTextAdapterExtension(codeBlockPlainTextAdapterMatcher);
|
||||
Reference in New Issue
Block a user