chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -22,12 +22,12 @@ describe('compareNodePosition', () => {
|
||||
let siblingNode: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
// 在每个测试之前创建新的 DOM 结构
|
||||
// Create a new DOM structure before each test
|
||||
parentNode = document.createElement('div');
|
||||
childNode = document.createElement('span');
|
||||
siblingNode = document.createElement('p');
|
||||
parentNode.appendChild(childNode); // childNode 是 parentNode 的子节点
|
||||
parentNode.appendChild(siblingNode); // siblingNode 是 childNode 的同级节点
|
||||
parentNode.appendChild(childNode); // childNode is a sub-node of parentNode
|
||||
parentNode.appendChild(siblingNode); // siblingNode is a sibling of childNode
|
||||
});
|
||||
|
||||
it('should return "before" if nodeA is before nodeB', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { findAncestorNodeByTagName } from '../src/utils/helper/find-ancestor-node-by-tag-name';
|
||||
|
||||
describe('findAncestorNodeByTagName', () => {
|
||||
// 设置 DOM 环境
|
||||
// Setting up the DOM environment
|
||||
document.body.innerHTML = `
|
||||
<div id="ancestor">
|
||||
<div id="parent">
|
||||
@@ -41,7 +41,7 @@ describe('findAncestorNodeByTagName', () => {
|
||||
|
||||
it('should return the node itself if it matches the tag name', () => {
|
||||
const result = findAncestorNodeByTagName(child, 'div');
|
||||
expect(result).toBe(parent); // 因为 child 的直接父级 parent 也是 div
|
||||
expect(result).toBe(parent); // Because the child's immediate parent is also a div
|
||||
});
|
||||
|
||||
it('should return null if the input node is null', () => {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { findLastChildNode } from '../src/utils/helper/find-last-child-node';
|
||||
|
||||
describe('findLastChildNode', () => {
|
||||
it('should return the last child node of a nested node structure', () => {
|
||||
// 创建一个嵌套的节点结构
|
||||
// Create a nested node structure
|
||||
const parentNode = document.createElement('div');
|
||||
const childNode1 = document.createElement('span');
|
||||
const childNode2 = document.createElement('p');
|
||||
@@ -28,21 +28,21 @@ describe('findLastChildNode', () => {
|
||||
childNode1.appendChild(childNode2);
|
||||
childNode2.appendChild(lastChildNode);
|
||||
|
||||
// 调用 findLastChildNode 函数
|
||||
// Call the findLastChildNode function
|
||||
const result = findLastChildNode(parentNode);
|
||||
|
||||
// 验证结果是否为最深层的子节点
|
||||
// Verify that the result is the deepest sub-node
|
||||
expect(result).toBe(lastChildNode);
|
||||
});
|
||||
|
||||
it('should return the node itself if it has no children', () => {
|
||||
// 创建一个没有子节点的节点
|
||||
// Create a node without a sub-node
|
||||
const singleNode = document.createElement('div');
|
||||
|
||||
// 调用 findLastChildNode 函数
|
||||
// Call the findLastChildNode function
|
||||
const result = findLastChildNode(singleNode);
|
||||
|
||||
// 验证结果是否为节点本身
|
||||
// Verify whether the result is the node itself
|
||||
expect(result).toBe(singleNode);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { findLastSiblingNode } from '../src/utils/helper/find-last-sibling-node';
|
||||
|
||||
describe('findLastSiblingNode', () => {
|
||||
// 设置 DOM 环境
|
||||
// Setting up the DOM environment
|
||||
document.body.innerHTML = `
|
||||
<div id="ancestor">
|
||||
<div id="sibling1" data-scope="valid"></div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { findNearestAnchor } from '../src/utils/helper/find-nearest-link-node';
|
||||
|
||||
describe('findNearestAnchor', () => {
|
||||
// 设置 DOM 环境
|
||||
// Setting up the DOM environment
|
||||
document.body.innerHTML = `
|
||||
<div>
|
||||
<a id="anchor1" href="#">
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('getAllChildNodesInNode', () => {
|
||||
let root: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
// 在每个测试用例之前设置 DOM 结构
|
||||
// Setting up the DOM structure before each test case
|
||||
document.body.innerHTML = `
|
||||
<div id="root">
|
||||
<span>Text 1</span>
|
||||
@@ -46,8 +46,8 @@ describe('getAllChildNodesInNode', () => {
|
||||
|
||||
it('should correctly handle text and element nodes', () => {
|
||||
const nodes = getAllChildNodesInNode(root);
|
||||
// 检查返回的节点类型是否正确
|
||||
expect(nodes.some(node => node.nodeType === Node.TEXT_NODE)).toBe(true); // 至少有一个文本节点
|
||||
expect(nodes.some(node => node.nodeType === Node.ELEMENT_NODE)).toBe(true); // 至少有一个元素节点
|
||||
// Check if the returned node type is correct
|
||||
expect(nodes.some(node => node.nodeType === Node.TEXT_NODE)).toBe(true); // At least one text node
|
||||
expect(nodes.some(node => node.nodeType === Node.ELEMENT_NODE)).toBe(true); // There is at least one element node
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('getAllNodesInRange', () => {
|
||||
let root: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
// 在每个测试用例之前设置 DOM 结构
|
||||
// Setting up the DOM structure before each test case
|
||||
document.body.innerHTML = `
|
||||
<div id="root">
|
||||
<span>Text 1</span>
|
||||
@@ -35,11 +35,11 @@ describe('getAllNodesInRange', () => {
|
||||
|
||||
it('should return all nodes within a range, including text and element nodes', () => {
|
||||
const range = new Range();
|
||||
range.setStart(root, 0); // 设置范围开始于 root 的第一个子节点
|
||||
range.setEnd(root, 3); // 设置范围结束于 root 的最后一个子节点
|
||||
range.setStart(root, 0); // Setup scope starts at the first sub-node of root
|
||||
range.setEnd(root, 3); // The setting range ends at the last sub-node of root.
|
||||
|
||||
const nodes = getAllNodesInRange(range);
|
||||
// 预期包含:span, div, Text 3
|
||||
// Expected to include: span, div, Text 3
|
||||
expect(nodes.length).toBe(5);
|
||||
expect((nodes[0] as Node).nodeType).toBe(Node.ELEMENT_NODE); // span
|
||||
expect((nodes[1] as Node).nodeType).toBe(Node.TEXT_NODE); // text
|
||||
@@ -50,8 +50,8 @@ describe('getAllNodesInRange', () => {
|
||||
|
||||
it('should return an empty array if the range is collapsed', () => {
|
||||
const range = document.createRange();
|
||||
range.setStart(root, 1); // 设置范围的开始和结束都在同一位置
|
||||
range.setEnd(root, 1); // 这将创建一个折叠的范围,即没有包含任何节点
|
||||
range.setStart(root, 1); // Set the start and end of the range at the same location
|
||||
range.setEnd(root, 1); // This will create a collapsed scope that contains no nodes
|
||||
|
||||
const nodes = getAllNodesInRange(range);
|
||||
expect(nodes).toEqual([root]);
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('getRangeDirection', () => {
|
||||
it('should return "none" when the range start and end are the same', () => {
|
||||
const range = document.createRange();
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div); // 确保节点在 DOM 中
|
||||
document.body.appendChild(div); // Make sure the node is in the DOM
|
||||
range.setStart(div, 0);
|
||||
range.setEnd(div, 0);
|
||||
|
||||
@@ -30,11 +30,11 @@ describe('getRangeDirection', () => {
|
||||
|
||||
it('should return "forward" when the range is selected forwards', () => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div); // 确保节点在 DOM 中
|
||||
document.body.appendChild(div); // Make sure the node is in the DOM
|
||||
div.textContent = 'Test content';
|
||||
const range = document.createRange();
|
||||
range.setStart(div.firstChild as Node, 0);
|
||||
range.setEnd(div.firstChild as Node, 4); // 选择了 "Test"
|
||||
range.setEnd(div.firstChild as Node, 4); // Select "Test"
|
||||
|
||||
const direction = getRangeDirection(range);
|
||||
expect(direction).toBe('forward');
|
||||
|
||||
@@ -33,23 +33,23 @@ const TIMEOUT = 100;
|
||||
|
||||
interface GrabParams {
|
||||
/**
|
||||
* 选择目标容器的 Ref
|
||||
* Select the Ref of the target container
|
||||
*/
|
||||
contentRef: MutableRefObject<HTMLDivElement | null>;
|
||||
/**
|
||||
* 浮动菜单的 Ref
|
||||
* Floating Menu Ref
|
||||
*/
|
||||
floatMenuRef: MutableRefObject<HTMLDivElement | null> | undefined;
|
||||
/**
|
||||
* 选择事件的回调
|
||||
* Select the callback for the event
|
||||
*/
|
||||
onSelectChange: (selectionData: SelectionData | null) => void;
|
||||
/**
|
||||
* 位置信息的回调
|
||||
* Callback of location information
|
||||
*/
|
||||
onPositionChange: (position: GrabPosition | null) => void;
|
||||
/**
|
||||
* Resize/Scroll/Wheel 是节流的时间
|
||||
* Resize/Scroll/Wheel is the time of throttling
|
||||
*/
|
||||
resizeThrottleTime?: number;
|
||||
}
|
||||
@@ -64,32 +64,32 @@ export const useGrab = ({
|
||||
const timeoutRef = useRef<number>();
|
||||
|
||||
/**
|
||||
* 选区对象存放(用于hooks内部流转状态用)
|
||||
* Selection object storage (for internal flow state of hooks)
|
||||
*/
|
||||
const selection = useRef<Selection | null>(null);
|
||||
|
||||
/**
|
||||
* 选区最终计算结果的数据
|
||||
* Data on the final calculation result of the constituency
|
||||
*/
|
||||
const selectionData = useRef<SelectionData | null>(null);
|
||||
|
||||
/**
|
||||
* 是否在 Scrolling 中
|
||||
* In Scrolling
|
||||
*/
|
||||
const [isScrolling, setIsScrolling] = useState(false);
|
||||
|
||||
/**
|
||||
* Scrolling 计时器
|
||||
* Scrolling timer
|
||||
*/
|
||||
const scrollingTimer = useRef<number | null>(null);
|
||||
|
||||
/**
|
||||
* 是否有 SelectionData (优化挂载逻辑用)
|
||||
* Is there SelectionData (for optimizing mount logic)
|
||||
*/
|
||||
const hasSelectionData = useRef(false);
|
||||
|
||||
/**
|
||||
* 清除内部数据 + 触发回调
|
||||
* Clear internal data + trigger callback
|
||||
*/
|
||||
const clearSelection = () => {
|
||||
onSelectChange(null);
|
||||
@@ -104,15 +104,15 @@ export const useGrab = ({
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理屏幕发生变化 Scroll + Resize + Wheel + SelectionChange(移动设备)
|
||||
* Handle screen changes Scroll + Resize + Wheel + SelectionChange (mobile device)
|
||||
*/
|
||||
const handleScreenChange = () => {
|
||||
// 获取选区
|
||||
// Get Constituency
|
||||
const innerSelection = window.getSelection();
|
||||
|
||||
const { direction = Direction.Unknown } = selectionData.current ?? {};
|
||||
|
||||
// 如果选区为空,则返回
|
||||
// If the selection is empty, return
|
||||
if (!innerSelection) {
|
||||
onSelectChange(null);
|
||||
return;
|
||||
@@ -125,19 +125,19 @@ export const useGrab = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认使用获取选区最后一行的位置信息 (既Forward的情况)
|
||||
// Default use to get the location information of the last line of the selection (both Forward cases)
|
||||
const rangeRect = Array.from(rectData.rangeRects).at(
|
||||
direction === Direction.Backward ? 0 : -1,
|
||||
);
|
||||
|
||||
// 如果最后一行选区信息不正确则返回
|
||||
// Returns if the last line of selection information is incorrect
|
||||
if (!rangeRect) {
|
||||
onPositionChange(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let [x, y] = [0, 0];
|
||||
// 判断如果选区是从前往后选择,则展示在最后一行的末尾,否则展示在开头
|
||||
// If the selection is selected from front to back, it is displayed at the end of the last line, otherwise it is displayed at the beginning
|
||||
if (direction === Direction.Backward) {
|
||||
x = rangeRect.left;
|
||||
y = rangeRect.top + rangeRect.height;
|
||||
@@ -147,7 +147,7 @@ export const useGrab = ({
|
||||
}
|
||||
|
||||
/**
|
||||
* 加了一个避让屏幕的逻辑
|
||||
* Added a logic to avoid the screen
|
||||
*/
|
||||
const position = {
|
||||
x: x > screen.width - MAX_WIDTH ? x - MAX_WIDTH : x,
|
||||
@@ -161,7 +161,7 @@ export const useGrab = ({
|
||||
};
|
||||
|
||||
/**
|
||||
* 智能处理屏幕发生变化 有一个计时器 + 滚动告知的逻辑
|
||||
* Smart processing screen changes, there is a timer + scroll notification logic
|
||||
*/
|
||||
const handleSmartScreenChange = useEventCallback(() => {
|
||||
if (scrollingTimer.current) {
|
||||
@@ -176,7 +176,7 @@ export const useGrab = ({
|
||||
});
|
||||
|
||||
/**
|
||||
* 处理获取选区的逻辑
|
||||
* Handling the logic of getting the selection
|
||||
*/
|
||||
const handleGetSelection = () => {
|
||||
if (!contentRef.current) {
|
||||
@@ -184,11 +184,11 @@ export const useGrab = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取选区
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- 内部变量
|
||||
// Get Constituency
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- internal variable
|
||||
const _selection = window.getSelection();
|
||||
|
||||
// 如果选区为空,则返回
|
||||
// If the selection is empty, return
|
||||
if (!_selection) {
|
||||
onSelectChange(null);
|
||||
return;
|
||||
@@ -196,19 +196,19 @@ export const useGrab = ({
|
||||
|
||||
selection.current = _selection;
|
||||
|
||||
// 获取选区数据
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- 内部变量
|
||||
// Get constituency data
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- internal variable
|
||||
const _selectionData = getSelectionData({
|
||||
selection: _selection,
|
||||
});
|
||||
|
||||
// 选区如果为空,则隐藏浮层Button
|
||||
// Hide Floating Button if selection is empty
|
||||
if (!_selectionData || !_selectionData.nodesAncestorIsMessageBox) {
|
||||
onSelectChange(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置展示和位置信息
|
||||
// Set display and location information
|
||||
selectionData.current = _selectionData;
|
||||
hasSelectionData.current = Boolean(_selectionData);
|
||||
|
||||
@@ -217,7 +217,7 @@ export const useGrab = ({
|
||||
};
|
||||
|
||||
/**
|
||||
* 鼠标抬起的动作
|
||||
* The action of raising the mouse
|
||||
*/
|
||||
const handleMouseUp = useEventCallback(() => {
|
||||
clearTimeout(timeoutRef.current);
|
||||
@@ -225,7 +225,7 @@ export const useGrab = ({
|
||||
});
|
||||
|
||||
/**
|
||||
* 键盘按下的动作
|
||||
* The action of keyboard pressing
|
||||
*/
|
||||
const handleKeyDown = useEventCallback((e: KeyboardEvent) => {
|
||||
const forbiddenKeyboardSelect = () => {
|
||||
@@ -244,10 +244,10 @@ export const useGrab = ({
|
||||
});
|
||||
|
||||
/**
|
||||
* 鼠标按下的动作
|
||||
* Mouse press.
|
||||
*/
|
||||
const handleMouseDown = useEventCallback((e: MouseEvent) => {
|
||||
// 检查是否有选区,且点击事件的目标不在选区内
|
||||
// Check if there is a constituency, and the target of the click event is not in the constituency
|
||||
|
||||
if (!contentRef.current || !floatMenuRef || !floatMenuRef?.current) {
|
||||
return;
|
||||
@@ -275,7 +275,7 @@ export const useGrab = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 监听鼠标down事件
|
||||
// Listen for mouse down events
|
||||
window.addEventListener('mousedown', handleMouseDown);
|
||||
|
||||
return () => {
|
||||
@@ -283,7 +283,7 @@ export const useGrab = ({
|
||||
};
|
||||
}, [hasSelectionData.current]);
|
||||
|
||||
// 当visible时,挂载监听事件,优化监听
|
||||
// When visible, mount the listening event to optimize listening
|
||||
useEffect(() => {
|
||||
if (!hasSelectionData.current) {
|
||||
return;
|
||||
@@ -307,7 +307,7 @@ export const useGrab = ({
|
||||
};
|
||||
}, [hasSelectionData.current]);
|
||||
|
||||
// target上挂载监听
|
||||
// mount monitor on target
|
||||
useEffect(() => {
|
||||
const target = contentRef.current;
|
||||
|
||||
@@ -315,7 +315,7 @@ export const useGrab = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 监听选择相关的鼠标抬起事件
|
||||
// Monitor selection-related mouse lift events
|
||||
target.addEventListener('pointerup', handleMouseUp);
|
||||
|
||||
if (isTouchDevice()) {
|
||||
@@ -330,15 +330,15 @@ export const useGrab = ({
|
||||
|
||||
return {
|
||||
/**
|
||||
* 清除内置状态和选区
|
||||
* Clear built-in state and selection
|
||||
*/
|
||||
clearSelection,
|
||||
/**
|
||||
* 是否在滚动中
|
||||
* Is it scrolling?
|
||||
*/
|
||||
isScrolling,
|
||||
/**
|
||||
* 重新计算选区位置
|
||||
* Recalculate the selection position
|
||||
*/
|
||||
computePosition: handleSmartScreenChange,
|
||||
};
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
import { getPictureNodeUrl } from './get-picture-node-url';
|
||||
|
||||
/**
|
||||
* 获取 TagName 为 Picture 的节点的有效节点
|
||||
* @param childNodes NodeListOf<Node> 子节点列表
|
||||
* Get a valid node for the node whose TagName is Picture
|
||||
* @param childNodes NodeListOf < Node > sub-node list
|
||||
* @returns Node | null
|
||||
*/
|
||||
export const findPictureValidChildNode = (childNodes: NodeListOf<Node>) =>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取图片 Node 中的 Url
|
||||
* @param node Node 任意Node
|
||||
* Get URLs in image Node
|
||||
* @param node Node
|
||||
* @returns string
|
||||
*/
|
||||
export const getPictureNodeUrl = (node: Node) => {
|
||||
|
||||
@@ -50,28 +50,28 @@ export const getSelectionData = ({
|
||||
const direction = getSelectionDirection(selection);
|
||||
|
||||
/**
|
||||
* 通过获取特定标识 判断是否是可以划选的元素
|
||||
* Determine whether it is an element that can be selected by getting a specific identifier
|
||||
*/
|
||||
const ancestorNodeWithAttribute = getAncestorAttributeNode(
|
||||
range.commonAncestorContainer.parentNode,
|
||||
CONTENT_ATTRIBUTE_NAME,
|
||||
);
|
||||
|
||||
// 特定标识
|
||||
// specific logo
|
||||
const ancestorAttributeValue =
|
||||
ancestorNodeWithAttribute?.attributes.getNamedItem(CONTENT_ATTRIBUTE_NAME)
|
||||
?.value ?? null;
|
||||
|
||||
// 信息来源
|
||||
// source of information
|
||||
const messageSource = ancestorNodeWithAttribute?.attributes.getNamedItem(
|
||||
MESSAGE_SOURCE_ATTRIBUTE_NAME,
|
||||
)?.value;
|
||||
|
||||
if (!hasFix) {
|
||||
// 尝试修复选区
|
||||
// Try to fix the selection
|
||||
const needFix = shouldRefineRange(range);
|
||||
|
||||
// 如果修复过,则重新获取执行并返回
|
||||
// If repaired, retrieve the execution and return
|
||||
if (needFix) {
|
||||
const isFix = refineRange({ range });
|
||||
|
||||
@@ -92,7 +92,7 @@ export const getSelectionData = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 格式化的选区NodeList
|
||||
// Formatted Selection NodeList
|
||||
const normalizeSelectionNodeList = getNormalizeNodeList(
|
||||
documentFragment.childNodes,
|
||||
);
|
||||
@@ -101,15 +101,15 @@ export const getSelectionData = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 人性化文本内容
|
||||
// user-friendly text content
|
||||
const humanizedContentText = getHumanizedContentText(
|
||||
normalizeSelectionNodeList,
|
||||
);
|
||||
|
||||
// 原始文本内容
|
||||
// raw text content
|
||||
const originContentText = getOriginContentText(normalizeSelectionNodeList);
|
||||
|
||||
// 如果修复选区成功了,那么他们的组件
|
||||
// If the repair selection is successful, then their components
|
||||
|
||||
return {
|
||||
humanizedContentText,
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 判断节点包含关系
|
||||
* 参考文档「https://developer.mozilla.org/zh-CN/docs/Web/API/Node/compareDocumentPosition」
|
||||
* Determine the node inclusion relationship
|
||||
* Reference document "https://developer.mozilla.org/zh-CN/docs/Web/API/Node/compareDocumentPosition"
|
||||
* @param nodeA
|
||||
* @param nodeB
|
||||
*
|
||||
@@ -24,16 +24,16 @@
|
||||
export const compareNodePosition = (nodeA: Node, nodeB: Node) => {
|
||||
const comparison = nodeA.compareDocumentPosition(nodeB);
|
||||
|
||||
// 之所以条件跟返回是反的,请参考官方文档,包含关系展示的是B - A的关系
|
||||
// The reason why the condition is inverse to return, please refer to the official documentation, including the relationship showing the B-A relationship
|
||||
if (comparison & Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
||||
return 'contains'; // nodeA 包含 nodeB
|
||||
return 'contains'; // NodeA contains nodeB
|
||||
} else if (comparison & Node.DOCUMENT_POSITION_CONTAINS) {
|
||||
return 'containedBy'; // nodeA 被 nodeB 包含
|
||||
return 'containedBy'; // NodeA is contained by nodeB
|
||||
} else if (comparison & Node.DOCUMENT_POSITION_FOLLOWING) {
|
||||
return 'before'; // nodeA 在 nodeB 之前
|
||||
return 'before'; // nodeA before nodeB
|
||||
} else if (comparison & Node.DOCUMENT_POSITION_PRECEDING) {
|
||||
return 'after'; // nodeA 在 nodeB 之后
|
||||
return 'after'; // NodeA after nodeB
|
||||
}
|
||||
|
||||
return 'none'; // 节点是相同的或者没有可比较的关系
|
||||
return 'none'; // Nodes are the same or have no comparable relationship
|
||||
};
|
||||
|
||||
@@ -15,31 +15,31 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 通过 TagName 寻找祖先节点(包括自身)
|
||||
* Find ancestor nodes (including itself) by TagName
|
||||
* @param node Node | null
|
||||
* @param tagName 目标 tagName
|
||||
* @param tagName Target tagName
|
||||
* @returns Node | null
|
||||
*/
|
||||
export const findAncestorNodeByTagName = (
|
||||
node: Node | null,
|
||||
tagName: string,
|
||||
): Element | null => {
|
||||
// 将标签名转换为大写,因为 DOM 中的标签名通常是大写的
|
||||
// Convert the tag signature to uppercase, as tag signatures in the DOM are usually uppercase
|
||||
const upperTagName = tagName.toUpperCase();
|
||||
|
||||
// 遍历节点的祖先节点直到找到匹配的标签名或到达根节点
|
||||
// Traverse the node's ancestors until a matching tag is found or the root node is reached
|
||||
while (node) {
|
||||
// 确保当前节点是元素节点,并且标签名匹配
|
||||
// Make sure that the current node is an element node and that the tag signatures match
|
||||
if (
|
||||
node.nodeType === Node.ELEMENT_NODE &&
|
||||
(node as Element).tagName === upperTagName
|
||||
) {
|
||||
return node as Element;
|
||||
}
|
||||
// 移动到父节点
|
||||
// Move to Parent Node
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
// 如果没有找到符合条件的祖先节点,返回 null
|
||||
// If no eligible ancestor is found, return null.
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 寻找某节点最后一个子节点
|
||||
* Find the last sub-node of a node
|
||||
* @param node Node
|
||||
* @returns Node
|
||||
*/
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
import { getAncestorAttributeValue } from '../get-ancestor-attribute-value';
|
||||
|
||||
/**
|
||||
* 寻找某节点的最后一个兄弟节点
|
||||
* @param node 寻找节点
|
||||
* Find the last sibling of a node
|
||||
* @param node find node
|
||||
* @returns
|
||||
*/
|
||||
export const findLastSiblingNode = ({
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
export const findNearestAnchor = (
|
||||
node: Node | null,
|
||||
): HTMLAnchorElement | null => {
|
||||
// 从当前节点开始向上遍历
|
||||
// Traverse up from the current node
|
||||
while (node) {
|
||||
// 如果当前节点是元素节点并且是<a>标签
|
||||
// If the current node is an element node and is a < a > tag
|
||||
if (node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'A') {
|
||||
// 返回这个<a>标签
|
||||
// Return this < a > tag
|
||||
return node as HTMLAnchorElement;
|
||||
}
|
||||
// 向上移动到父节点
|
||||
// Move up to the parent node
|
||||
node = node.parentNode;
|
||||
}
|
||||
// 如果遍历到根节点还没有找到<a>标签,返回null
|
||||
// If the < a > tag is not found at the root node, return null.
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -30,10 +30,10 @@ export const findNotContainsPreviousSibling = (
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取两个节点之间的关系
|
||||
// Get the relationship between two nodes
|
||||
const relationship = compareNodePosition(sibling, node);
|
||||
|
||||
// 如果两个节点之间没有包含关系,则返回当前兄弟节点
|
||||
// If there is no containing relationship between the two nodes, the current sibling is returned
|
||||
if (!['containedBy', 'contains'].includes(relationship)) {
|
||||
return sibling;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// 辅助函数,用于获取选区内的所有节点
|
||||
// Helper function to obtain all nodes in the selection
|
||||
export const getAllChildNodesInNode = (node: Node): Node[] => {
|
||||
const nodes: Node[] = [];
|
||||
const treeWalker = document.createTreeWalker(node, NodeFilter.SHOW_ALL, {
|
||||
@@ -24,7 +24,7 @@ export const getAllChildNodesInNode = (node: Node): Node[] => {
|
||||
: NodeFilter.FILTER_REJECT,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line prefer-destructuring -- 符合预期,因为要改数据并且允许为空
|
||||
// eslint-disable-next-line prefer-destructuring -- as expected, because the data is to be changed and allowed to be empty
|
||||
let currentNode: Node | null = treeWalker.currentNode;
|
||||
|
||||
while (currentNode) {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// 辅助函数,用于获取选区内的所有节点
|
||||
// Helper function to obtain all nodes in the selection
|
||||
export const getAllNodesInRange = (range: Range): Node[] => {
|
||||
const nodes: Node[] = [];
|
||||
const treeWalker = document.createTreeWalker(
|
||||
@@ -28,7 +28,7 @@ export const getAllNodesInRange = (range: Range): Node[] => {
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line prefer-destructuring -- 符合预期,因为要改数据并且允许为空
|
||||
// eslint-disable-next-line prefer-destructuring -- as expected, because the data is to be changed and allowed to be empty
|
||||
let currentNode: Node | null = treeWalker.currentNode;
|
||||
|
||||
while (currentNode) {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const getRangeDirection = (range: Range) => {
|
||||
const position = range.compareBoundaryPoints(Range.START_TO_END, range);
|
||||
|
||||
if (position === 0) {
|
||||
return 'none'; // 选区起点和终点相同,即没有选择文本
|
||||
return 'none'; // The starting and ending points of the selection are the same, i.e. no text is selected
|
||||
}
|
||||
|
||||
return position === -1 ? 'backward' : 'forward';
|
||||
|
||||
@@ -18,27 +18,27 @@ import { Direction } from '../../types/selection';
|
||||
import { compareNodePosition } from './compare-node-position';
|
||||
|
||||
export const getSelectionDirection = (selection: Selection): Direction => {
|
||||
// 确保有选区存在
|
||||
// Make sure there are constituencies
|
||||
if (!selection || selection.isCollapsed) {
|
||||
return Direction.Unknown; // 没有选区或选区未展开
|
||||
return Direction.Unknown; // No constituencies or constituencies not expanded
|
||||
}
|
||||
|
||||
const { anchorNode } = selection;
|
||||
const { focusNode } = selection;
|
||||
|
||||
// 确保 anchorNode 和 focusNode 都不为 null
|
||||
// Make sure that neither anchorNode nor focusNode is null
|
||||
if (!anchorNode || !focusNode) {
|
||||
return Direction.Unknown; // 无法确定方向
|
||||
return Direction.Unknown; // Unable to determine direction
|
||||
}
|
||||
|
||||
const { anchorOffset } = selection;
|
||||
const { focusOffset } = selection;
|
||||
// 比较 anchor 和 focus 的位置
|
||||
// Compare anchor and focus positions
|
||||
if (anchorNode === focusNode) {
|
||||
// 如果 anchor 和 focus 在同一个节点,通过偏移量判断方向
|
||||
// If the anchor and focus are on the same node, determine the direction by the offset
|
||||
return anchorOffset <= focusOffset ? Direction.Forward : Direction.Backward;
|
||||
} else {
|
||||
// 如果不在同一个节点,使用 Document Position 来判断
|
||||
// If not in the same node, use Document Position to determine
|
||||
const position = compareNodePosition(anchorNode, focusNode);
|
||||
|
||||
if (position === 'before') {
|
||||
@@ -48,6 +48,6 @@ export const getSelectionDirection = (selection: Selection): Direction => {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果无法确定方向,返回 'unknown'
|
||||
// If the direction cannot be determined, return'unknown'
|
||||
return Direction.Unknown;
|
||||
};
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
export const hasVisibleSelection = (range: Range): boolean => {
|
||||
// 克隆Range内的所有节点
|
||||
// Clone all nodes within the Range
|
||||
const documentFragment = range.cloneContents();
|
||||
const textNodes: Text[] = [];
|
||||
|
||||
// 递归函数来收集所有文本节点
|
||||
// Recursive function to collect all text nodes
|
||||
function collectTextNodes(node: Node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
textNodes.push(node as Text);
|
||||
@@ -28,9 +28,9 @@ export const hasVisibleSelection = (range: Range): boolean => {
|
||||
}
|
||||
}
|
||||
|
||||
// 从文档片段的根节点开始收集文本节点
|
||||
// Collect text nodes from the root node of the document fragment
|
||||
collectTextNodes(documentFragment);
|
||||
|
||||
// 检查收集到的文本节点中是否有非空白的文本
|
||||
// Check for non-blank text in the collected text nodes
|
||||
return textNodes.some(textNode => /\S/.test(textNode.textContent || ''));
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ import { isGrabLink } from './is-grab-link';
|
||||
import { isGrabImage } from './is-grab-image';
|
||||
|
||||
/**
|
||||
* 获取人性化文本内容
|
||||
* Access to user-friendly text content
|
||||
*/
|
||||
export const getHumanizedContentText = (normalizeNodeList: GrabNode[]) => {
|
||||
let content = '';
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from '../../types/node';
|
||||
|
||||
/**
|
||||
* 获取格式化的 NodeList
|
||||
* Get formatted NodeList
|
||||
* @param childNodeList NodeListOf<Node>
|
||||
*/
|
||||
export const getNormalizeNodeList = (childNodeList: NodeListOf<Node>) => {
|
||||
@@ -55,7 +55,7 @@ export const getNormalizeNodeList = (childNodeList: NodeListOf<Node>) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成 Grab Node
|
||||
* Generating Grab Nodes
|
||||
* @param node Node | null
|
||||
*/
|
||||
export const generateGrabNode = (node: Node | null) => {
|
||||
@@ -65,19 +65,19 @@ export const generateGrabNode = (node: Node | null) => {
|
||||
|
||||
const isTable = ['TH', 'TD'].includes(node.nodeName.toUpperCase());
|
||||
|
||||
// 文本节点
|
||||
// text node
|
||||
if (node.nodeType === node.TEXT_NODE || isTable) {
|
||||
return generateGrabText(node, isTable);
|
||||
}
|
||||
|
||||
// 元素节点
|
||||
// element node
|
||||
if (node.nodeType === node.ELEMENT_NODE && node instanceof Element) {
|
||||
return generateGrabElement(node);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成 Text 节点
|
||||
* Generate Text Node
|
||||
* @param node Node | null
|
||||
*/
|
||||
export const generateGrabText = (node: Node | null, isTable?: boolean) => {
|
||||
@@ -97,7 +97,7 @@ export const generateGrabText = (node: Node | null, isTable?: boolean) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成 Element 节点
|
||||
* Generating Element Node
|
||||
* @param node Element | null
|
||||
*/
|
||||
export const generateGrabElement = (node: Element | null) => {
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取格式化的 SelectionNodeList 数据
|
||||
* 获取喂给大模型的文本内容
|
||||
* Get formatted SelectionNodeList data
|
||||
* Get the text content fed to the large model
|
||||
*/
|
||||
import { type GrabNode } from '../../types/node';
|
||||
import { isGrabTextNode } from './is-grab-text-node';
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { parseMarkdownHelper } from '@coze-common/chat-area-utils';
|
||||
import { parseMarkdown } from '@coze-arch/bot-md-box-adapter/lazy';
|
||||
|
||||
@@ -22,7 +22,7 @@ import { GrabElementType, type GrabNode } from '../types/node';
|
||||
const { isImage, isLink, isParent, isText } = parseMarkdownHelper;
|
||||
|
||||
/**
|
||||
* 获取GrabNode节点
|
||||
* Get a GrabNode node
|
||||
* @param markdown string
|
||||
* @returns GrabNode[]
|
||||
*/
|
||||
@@ -33,14 +33,14 @@ export const parseMarkdownToGrabNode = (markdown: string) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 从Markdown的AST解析成GrabNode节点
|
||||
* Parsing from Markdown's AST to a GrabNode node
|
||||
* @param ast markdown ast by parseMarkdown (md-box)
|
||||
* @returns GrabNode[]
|
||||
*/
|
||||
export const getGrabNodeFromAst = (ast: unknown): GrabNode[] => {
|
||||
const normalizedNodeList: GrabNode[] = [];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 符合预期
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- as expected
|
||||
const traverseAst = (_ast: any) => {
|
||||
if (isText(_ast)) {
|
||||
normalizedNodeList.push({
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
import { findPictureValidChildNode } from '../find-picture-valid-child-node';
|
||||
|
||||
/**
|
||||
* 处理特殊 Node 节点数据
|
||||
* Processing special Node data
|
||||
* @param node Node
|
||||
* @returns node | undefined
|
||||
*/
|
||||
export const processSpecialNode = (node: Node) => {
|
||||
// 针对picture类型的特殊优化
|
||||
// Special optimization for picture types
|
||||
if (node.nodeName.toUpperCase() === 'PICTURE') {
|
||||
const pictureNode = findPictureValidChildNode(node.childNodes);
|
||||
|
||||
@@ -31,12 +31,12 @@ export const processSpecialNode = (node: Node) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 针对链接的特殊优化
|
||||
// Special optimization for links
|
||||
if (node.nodeName.toUpperCase() === 'A') {
|
||||
return node;
|
||||
}
|
||||
|
||||
// 针对表格的特殊优化
|
||||
// Special optimization for tables
|
||||
if (['TH', 'TD'].includes(node.nodeName.toUpperCase())) {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -28,30 +28,30 @@ export const fixEndEmpty = ({
|
||||
endNode: Node;
|
||||
endOffset: number;
|
||||
}): boolean => {
|
||||
// 检查是否需要修复:结束节点和开始节点不同且结束偏移量为0
|
||||
// Check if it needs to be fixed: the end node and the start node are different and the end offset is 0
|
||||
if (startNode === endNode || endOffset !== 0) {
|
||||
return false; // 不需要修复
|
||||
return false; // No repair required.
|
||||
}
|
||||
|
||||
// 初始化当前节点为结束节点的前一个兄弟节点
|
||||
// Initializes the current node to the previous sibling of the end node
|
||||
let currentNode: Node | null = findNotContainsPreviousSibling(endNode);
|
||||
|
||||
// 寻找一个有效的非空前一个兄弟节点
|
||||
// Find a valid non-unprecedented sibling node
|
||||
while (currentNode) {
|
||||
if (currentNode.nodeType === Node.TEXT_NODE) {
|
||||
// 如果是文本节点,检查是否非空
|
||||
// If it is a text node, check if it is not empty
|
||||
const textContent = currentNode.textContent?.trim();
|
||||
if (textContent && currentNode.textContent?.length) {
|
||||
// 非空,修复选区结束位置
|
||||
// Not empty, fix the end of the selection
|
||||
range.setEnd(currentNode, currentNode.textContent.length);
|
||||
return true;
|
||||
}
|
||||
} else if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
||||
// 如果是元素节点,检查是否有可见内容
|
||||
// If it is an element node, check if there is any visible content
|
||||
const textContent = currentNode.textContent?.trim();
|
||||
if (textContent) {
|
||||
// 有可见内容,尝试更精确地设置结束位置
|
||||
// 如果元素内部有文本节点,尝试定位到最后一个文本节点
|
||||
// With visible content, try to set the end position more precisely
|
||||
// If there is a text node inside the element, try to navigate to the last text node
|
||||
let lastTextNode: Node | null = null;
|
||||
|
||||
const allChildNodes = getAllChildNodesInNode(currentNode);
|
||||
@@ -68,10 +68,10 @@ export const fixEndEmpty = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果当前节点为空或不满足条件,继续向前/向上遍历
|
||||
// If the current node is empty or does not meet the conditions, continue to traverse forward/upward
|
||||
currentNode = findNotContainsPreviousSibling(currentNode);
|
||||
}
|
||||
|
||||
// 如果遍历完所有前一个兄弟节点都没有找到合适的节点,返回false
|
||||
// If no suitable node is found after traversing all previous siblings, return false.
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ export const fixEndNode = ({
|
||||
let endNode: Node | null = range.endContainer;
|
||||
let { endOffset } = range;
|
||||
|
||||
// 确保结束节点符合条件
|
||||
// Make sure the end node meets the conditions
|
||||
while (
|
||||
endNode &&
|
||||
!(
|
||||
@@ -39,14 +39,14 @@ export const fixEndNode = ({
|
||||
) {
|
||||
if (endNode.nextSibling) {
|
||||
endNode = endNode.nextSibling;
|
||||
endOffset = 0; // 从下一个兄弟节点的开始位置开始
|
||||
endOffset = 0; // Start from the starting position of the next sibling node
|
||||
} else if (endNode.parentNode && endNode.parentNode !== document) {
|
||||
endNode = endNode.parentNode;
|
||||
endOffset = endNode
|
||||
? findLastChildNode(endNode).textContent?.length ?? 0
|
||||
: 0; // 从父节点的最后位置开始
|
||||
: 0; // Start from the last position of the parent node
|
||||
} else {
|
||||
// 没有符合条件的结束节点
|
||||
// No eligible end nodes
|
||||
endNode = null;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ export const fixLink = (range: Range, startNode: Node, endNode: Node) => {
|
||||
const endAnchor = findNearestAnchor(endNode);
|
||||
|
||||
let isFix = false;
|
||||
// 如果起始节点在链接内,将选区的起点设置为链接的开始
|
||||
// If the starting node is within the link, set the starting point of the selection to the start of the link
|
||||
if (startAnchor) {
|
||||
range.setStartBefore(startAnchor);
|
||||
isFix = true;
|
||||
}
|
||||
|
||||
// 如果结束节点在链接内,将选区的终点设置为链接的结束
|
||||
// If the end node is within the link, set the end of the selection to the end of the link
|
||||
if (endAnchor) {
|
||||
range.setEndAfter(endAnchor);
|
||||
isFix = true;
|
||||
@@ -37,8 +37,8 @@ export const fixLink = (range: Range, startNode: Node, endNode: Node) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 1. 链接[A ...文字... 链]接B
|
||||
* 2. 链接A ...文[字... 链]接B
|
||||
* 3. ...文[字 链]接B
|
||||
* 4. 链[接]B
|
||||
* 1. Link [A... text... link] to B
|
||||
* 2. Link A... text [word... link] to B
|
||||
* 3.... Text [word, chain] to B
|
||||
* 4. Chain [link] B
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@ export const fixStartNode = ({
|
||||
let startNode: Node | null = range.startContainer;
|
||||
let { startOffset } = range;
|
||||
|
||||
// 确保起始节点符合条件
|
||||
// Make sure the starting node meets the requirements
|
||||
while (
|
||||
startNode &&
|
||||
!(
|
||||
@@ -38,12 +38,12 @@ export const fixStartNode = ({
|
||||
) {
|
||||
if (startNode.previousSibling) {
|
||||
startNode = startNode.previousSibling;
|
||||
startOffset = 0; // 从前一个兄弟节点的开始位置开始
|
||||
startOffset = 0; // From the starting position of the previous sibling node
|
||||
} else if (startNode.parentNode && startNode.parentNode !== document) {
|
||||
startNode = startNode.parentNode;
|
||||
startOffset = 0; // 从父节点的开始位置开始
|
||||
startOffset = 0; // Start at the beginning of the parent node
|
||||
} else {
|
||||
// 没有符合条件的起始节点
|
||||
// No eligible starting nodes
|
||||
startNode = null;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 修复选区的实际函数
|
||||
* @param range Range 选区
|
||||
* Fix the actual function of the selection
|
||||
* @param range Range
|
||||
*/
|
||||
import { findLastSiblingNode } from '../helper/find-last-sibling-node';
|
||||
import { findLastChildNode } from '../helper/find-last-child-node';
|
||||
@@ -31,7 +31,7 @@ import { fixEndEmpty } from './fix-end-empty';
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
export const refineRange = ({ range }: { range: Range }): boolean => {
|
||||
// 初期算法只用StartNode当作选区的开始节点
|
||||
// The initial algorithm only uses StartNode as the starting node of the selection
|
||||
const targetAttributeValue = getAncestorAttributeValue(
|
||||
range.startContainer,
|
||||
CONTENT_ATTRIBUTE_NAME,
|
||||
@@ -53,43 +53,43 @@ export const refineRange = ({ range }: { range: Range }): boolean => {
|
||||
targetAttributeValue,
|
||||
});
|
||||
|
||||
// 如果找到了起始节点,但是没找到结束节点,那么继续尝试使用开始节点的最后一个兄弟元素修复选区
|
||||
// If the start node is found, but the end node is not found, then continue to try to fix the selection using the last sibling of the start node
|
||||
if (startNode && !endNode) {
|
||||
const { parentNode } = startNode;
|
||||
let lastSibling: Node | null = null;
|
||||
|
||||
// 尝试找到最近的<li>或<a>标签
|
||||
// Try to find the nearest < li > or < a > tag
|
||||
const liParentNode = findAncestorNodeByTagName(parentNode, 'LI');
|
||||
const aParentNode = liParentNode
|
||||
? findAncestorNodeByTagName(liParentNode, 'A')
|
||||
: findAncestorNodeByTagName(parentNode, 'A');
|
||||
// 找到最近的<div>标签
|
||||
// Find the nearest < div > tag
|
||||
const divParentNode = findAncestorNodeByTagName(parentNode, 'DIV');
|
||||
|
||||
// 根据找到的节点类型决定如何查找最后一个兄弟节点
|
||||
// Determine how to find the last sibling based on the type of node found
|
||||
if (aParentNode) {
|
||||
// 如果找到了<a>,使用它的父节点
|
||||
// If < a > is found, use its parent node
|
||||
lastSibling = findLastSiblingNode({
|
||||
node: aParentNode,
|
||||
scopeAncestorAttributeName: CONTENT_ATTRIBUTE_NAME,
|
||||
targetAttributeValue,
|
||||
});
|
||||
} else if (liParentNode) {
|
||||
// 如果找到了<li>,使用它的父节点
|
||||
// If a < li > is found, use its parent node
|
||||
lastSibling = findLastSiblingNode({
|
||||
node: liParentNode,
|
||||
scopeAncestorAttributeName: CONTENT_ATTRIBUTE_NAME,
|
||||
targetAttributeValue,
|
||||
});
|
||||
} else if (divParentNode) {
|
||||
// 如果找到了<div>,使用他的父节点(例如代码块的情况)
|
||||
// If a < div > is found, use its parent node (e.g. in the case of a code block).
|
||||
lastSibling = findLastSiblingNode({
|
||||
node: divParentNode,
|
||||
scopeAncestorAttributeName: CONTENT_ATTRIBUTE_NAME,
|
||||
targetAttributeValue,
|
||||
});
|
||||
} else {
|
||||
// 否则,使用起始节点
|
||||
// Otherwise, use the starting node
|
||||
lastSibling = findLastSiblingNode({
|
||||
node: startNode,
|
||||
scopeAncestorAttributeName: CONTENT_ATTRIBUTE_NAME,
|
||||
@@ -97,7 +97,7 @@ export const refineRange = ({ range }: { range: Range }): boolean => {
|
||||
});
|
||||
}
|
||||
|
||||
// 如果起始节点和找到的兄弟节点一样,那么就用其实节点的父元素去找他最后的一个节点
|
||||
// If the starting node is the same as the found sibling, then use the parent element of the actual node to find its last node
|
||||
if (startNode === lastSibling) {
|
||||
lastSibling = findLastSiblingNode({
|
||||
node: parentNode,
|
||||
@@ -115,7 +115,7 @@ export const refineRange = ({ range }: { range: Range }): boolean => {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果起始节点和结束节点都找到了,修正选区
|
||||
// If both the start and end nodes are found, correct the selection
|
||||
if (startNode && endNode) {
|
||||
const relation = compareNodePosition(startNode, endNode);
|
||||
|
||||
|
||||
@@ -20,21 +20,21 @@ import { findAncestorNodeByTagName } from './helper/find-ancestor-node-by-tag-na
|
||||
import { getAncestorAttributeValue } from './get-ancestor-attribute-value';
|
||||
|
||||
export const shouldRefineRange = (range: Range): boolean => {
|
||||
// 获取选区的所有节点
|
||||
// Get all nodes of the selection
|
||||
const nodes = getAllNodesInRange(range);
|
||||
|
||||
let validNodeLength = 0;
|
||||
|
||||
let hasNodeInLink = false;
|
||||
|
||||
// 遍历所有节点,检查它们的祖先是否都有特定类名属性
|
||||
// Traverse all nodes to check if their ancestors have a specific class name attribute
|
||||
for (const node of nodes) {
|
||||
const attributeValue = getAncestorAttributeValue(
|
||||
node,
|
||||
CONTENT_ATTRIBUTE_NAME,
|
||||
);
|
||||
|
||||
// 如果不存在才需要覆盖 hasNodeInLink,确保找到有节点在链接中
|
||||
// If it doesn't exist, you need to overwrite hasNodeInLink and make sure to find a node in the link.
|
||||
if (!hasNodeInLink) {
|
||||
hasNodeInLink = Boolean(findAncestorNodeByTagName(node, 'A'));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user