mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-02 01:49:51 +08:00
fe5f0f62ec
This PR performs a significant architectural refactoring by extracting rich text functionality into a dedicated package. Here are the key changes: 1. **New Package Creation** - Created a new package `@blocksuite/affine-rich-text` to house rich text related functionality - Moved rich text components, utilities, and types from `@blocksuite/affine-components` to this new package 2. **Dependency Updates** - Updated multiple block packages to include the new `@blocksuite/affine-rich-text` as a direct dependency: - block-callout - block-code - block-database - block-edgeless-text - block-embed - block-list - block-note - block-paragraph 3. **Import Path Updates** - Refactored all imports that previously referenced rich text functionality from `@blocksuite/affine-components/rich-text` to now use `@blocksuite/affine-rich-text` - Updated imports for components like: - DefaultInlineManagerExtension - RichText types and interfaces - Text manipulation utilities (focusTextModel, textKeymap, etc.) - Reference node components and providers 4. **Build Configuration Updates** - Added references to the new rich text package in the `tsconfig.json` files of all affected packages - Maintained workspace dependencies using the `workspace:*` version specifier The primary motivation appears to be: 1. Better separation of concerns by isolating rich text functionality 2. Improved maintainability through more modular package structure 3. Clearer dependencies between packages 4. Potential for better tree-shaking and bundle optimization This is primarily an architectural improvement that should make the codebase more maintainable and better organized.
117 lines
3.3 KiB
TypeScript
117 lines
3.3 KiB
TypeScript
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
|
import { isStrictUrl } from '@blocksuite/affine-shared/utils';
|
|
import type {
|
|
BeforeinputHookCtx,
|
|
CompositionEndHookCtx,
|
|
HookContext,
|
|
} from '@blocksuite/inline';
|
|
|
|
const EDGE_IGNORED_ATTRIBUTES = ['code', 'link'] as const;
|
|
const GLOBAL_IGNORED_ATTRIBUTES = [] as const;
|
|
|
|
const autoIdentifyLink = (ctx: HookContext<AffineTextAttributes>) => {
|
|
// auto identify link only on pressing space
|
|
if (ctx.data !== ' ') {
|
|
return;
|
|
}
|
|
|
|
// space is typed at the end of link, remove the link attribute on typed space
|
|
if (ctx.attributes?.link) {
|
|
if (ctx.inlineRange.index === ctx.inlineEditor.yText.length) {
|
|
delete ctx.attributes['link'];
|
|
}
|
|
return;
|
|
}
|
|
|
|
const lineInfo = ctx.inlineEditor.getLine(ctx.inlineRange.index);
|
|
if (!lineInfo) {
|
|
return;
|
|
}
|
|
const { line, lineIndex, rangeIndexRelatedToLine } = lineInfo;
|
|
|
|
if (lineIndex !== 0) {
|
|
return;
|
|
}
|
|
|
|
const verifyData = line.vTextContent
|
|
.slice(0, rangeIndexRelatedToLine)
|
|
.split(' ');
|
|
|
|
const verifyStr = verifyData[verifyData.length - 1];
|
|
|
|
const isUrl = isStrictUrl(verifyStr);
|
|
|
|
if (!isUrl) {
|
|
return;
|
|
}
|
|
|
|
const startIndex = ctx.inlineRange.index - verifyStr.length;
|
|
|
|
ctx.inlineEditor.formatText(
|
|
{
|
|
index: startIndex,
|
|
length: verifyStr.length,
|
|
},
|
|
{
|
|
link: verifyStr,
|
|
}
|
|
);
|
|
};
|
|
|
|
function handleExtendedAttributes(
|
|
ctx:
|
|
| BeforeinputHookCtx<AffineTextAttributes>
|
|
| CompositionEndHookCtx<AffineTextAttributes>
|
|
) {
|
|
const { data, inlineEditor, inlineRange } = ctx;
|
|
const deltas = inlineEditor.getDeltasByInlineRange(inlineRange);
|
|
// eslint-disable-next-line sonarjs/no-collapsible-if
|
|
if (data && data.length > 0 && data !== '\n') {
|
|
if (
|
|
// cursor is in the between of two deltas
|
|
(deltas.length > 1 ||
|
|
// cursor is in the end of line or in the middle of a delta
|
|
(deltas.length === 1 && inlineRange.index !== 0)) &&
|
|
!inlineEditor.isEmbed(deltas[0][0]) // embeds should not be extended
|
|
) {
|
|
// each new text inserted by inline editor will not contain any attributes,
|
|
// but we want to keep the attributes of previous text or current text where the cursor is in
|
|
// here are two cases:
|
|
// 1. aaa**b|bb**ccc --input 'd'--> aaa**bdbb**ccc, d should extend the bold attribute
|
|
// 2. aaa**bbb|**ccc --input 'd'--> aaa**bbbd**ccc, d should extend the bold attribute
|
|
const { attributes } = deltas[0][0];
|
|
if (
|
|
deltas.length !== 1 ||
|
|
inlineRange.index === inlineEditor.yText.length
|
|
) {
|
|
// `EDGE_IGNORED_ATTRIBUTES` is which attributes should be ignored in case 2
|
|
EDGE_IGNORED_ATTRIBUTES.forEach(attr => {
|
|
delete attributes?.[attr];
|
|
});
|
|
}
|
|
|
|
// `GLOBAL_IGNORED_ATTRIBUTES` is which attributes should be ignored in case 1, 2
|
|
GLOBAL_IGNORED_ATTRIBUTES.forEach(attr => {
|
|
delete attributes?.[attr];
|
|
});
|
|
|
|
ctx.attributes = attributes ?? {};
|
|
}
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
export const onVBeforeinput = (
|
|
ctx: BeforeinputHookCtx<AffineTextAttributes>
|
|
) => {
|
|
handleExtendedAttributes(ctx);
|
|
autoIdentifyLink(ctx);
|
|
};
|
|
|
|
export const onVCompositionEnd = (
|
|
ctx: CompositionEndHookCtx<AffineTextAttributes>
|
|
) => {
|
|
handleExtendedAttributes(ctx);
|
|
};
|