feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
/**
|
||||
* 是不是大数字
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
export function isBigNumber(value: unknown): value is BigNumber {
|
||||
return !!(value && value instanceof BigNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* 大数字转字符串
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
export function bigNumbertoString(value: BigNumber): string {
|
||||
return value.toFixed();
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isObject } from 'lodash-es';
|
||||
|
||||
import { LineStatus, type JsonValueType, type Field } from '../types';
|
||||
import { isBigNumber } from './big-number';
|
||||
|
||||
/**
|
||||
* 通过父元素的线条状态推到子元素的线条状态
|
||||
*/
|
||||
const getLineByParent2Child = (pLine: LineStatus): LineStatus => {
|
||||
switch (pLine) {
|
||||
/** 表示父节点也是从父父节点下钻而来,此处的子节点只需要把线延续下去即可 */
|
||||
case LineStatus.Visible:
|
||||
return LineStatus.Half;
|
||||
/** 表示父节点是父父节点的最后一个节点,子节点无需再延续,渲染空白即可 */
|
||||
case LineStatus.Last:
|
||||
return LineStatus.Hidden;
|
||||
/** 其他的情况完全继承父节点的线 */
|
||||
default:
|
||||
return pLine;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 将 object 解析成可以循环渲染的 fields
|
||||
* 1. 若 object 非复杂类型,则返回长度为 1 的 fields 只渲染一项
|
||||
* 2. 若 object = {},则返回长度为 0 的 fields,渲染层需要做好兜底
|
||||
*/
|
||||
const generateFields = (object: JsonValueType): Field[] => {
|
||||
/** 若 object 非复杂类型 */
|
||||
if (!isObject(object) || isBigNumber(object)) {
|
||||
return [
|
||||
{
|
||||
path: [],
|
||||
lines: [],
|
||||
value: object,
|
||||
isObj: false,
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 递归计算时缓存一下计算好的线,没别的意义,降低一些时间复杂度 */
|
||||
const lineMap = new Map<string[], LineStatus[]>();
|
||||
|
||||
/** 递归解析 object 为 fields */
|
||||
const dfs = ($object: object, $parentPath: string[] = []): Field[] => {
|
||||
// 如果不是对象,直接返回空数组,兜底异常情况
|
||||
if (!isObject($object)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 如果是大数字,直接返回空数组
|
||||
if (isBigNumber($object)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const parentLines = lineMap.get($parentPath) || [];
|
||||
|
||||
const keys = Object.keys($object);
|
||||
|
||||
return keys.map((key, idx) => {
|
||||
const value = $object[key];
|
||||
const path = $parentPath.concat(key);
|
||||
const last = idx === keys.length - 1;
|
||||
/**
|
||||
* 根据父节点的线推导子节点的线
|
||||
*/
|
||||
const lines = parentLines
|
||||
.map<LineStatus>(getLineByParent2Child)
|
||||
/**
|
||||
* 最后拼接上子节点自己的线,最后一个节点和普通的节点有样式区分
|
||||
*/
|
||||
.concat(last ? LineStatus.Last : LineStatus.Visible);
|
||||
lineMap.set(path, lines);
|
||||
return {
|
||||
path,
|
||||
lines,
|
||||
value,
|
||||
children: dfs(value, path),
|
||||
isObj: isObject(value) && !isBigNumber(value),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
return dfs(object);
|
||||
};
|
||||
|
||||
export { generateFields };
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// 对象中的文本,避免字符被转译
|
||||
export const generateStrAvoidEscape = (str: string) => {
|
||||
const characters = {
|
||||
'\\': '\\\\',
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\t': '\\t',
|
||||
};
|
||||
|
||||
let next = '';
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str[i];
|
||||
next += characters[char] || char;
|
||||
}
|
||||
|
||||
return next;
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { isString } from 'lodash-es';
|
||||
import { Typography } from '@coze-arch/bot-semi';
|
||||
|
||||
import { generateStrAvoidEscape } from './generate-str-avoid-escape';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export const generateStr2Link = (str: string, avoidEscape?: boolean) => {
|
||||
if (str === '') {
|
||||
return [''];
|
||||
}
|
||||
|
||||
if (avoidEscape) {
|
||||
str = generateStrAvoidEscape(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更严格的 url 匹配规则,防止过度匹配
|
||||
* 协议:http、https
|
||||
* 域名:允许使用 -、a-z、A-Z、0-9,其中 - 不能开头,每一级域名长度不会超过 63
|
||||
* 端口:支持带端口 0 - 65535
|
||||
* URL:严格型不匹配中文等转译前的文字,否则一旦命中将会识别整段字符串
|
||||
*/
|
||||
const urlReg = new RegExp(
|
||||
'http(s)?://' +
|
||||
'[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+' +
|
||||
'(:[0-9]{1,5})?' +
|
||||
'[-a-zA-Z0-9()@:%_\\+.~#?&//=]*',
|
||||
'g',
|
||||
);
|
||||
const matches = [...str.matchAll(urlReg)];
|
||||
/**
|
||||
* 切割字符串,url 嵌套为 link 的样式,切割步骤:
|
||||
* 1. 匹配字符串中所有的 url
|
||||
* 2. 倒序 matches,从末尾开始切,原因是 match.index 是从头开始计数,从头切增加计算量
|
||||
* 3. 每一个 match 切两刀成三段,头尾是普通字符串,中间为 url
|
||||
* 4. 按照 end、url、start 的顺序 push 到栈中,下次 match 会直接取 start 继续切
|
||||
* 5. 切割完成后做一次倒序
|
||||
*/
|
||||
return matches
|
||||
.reverse()
|
||||
.reduce<ReactNode[]>(
|
||||
(nodes, match) => {
|
||||
const lastNode = nodes.pop();
|
||||
if (!isString(lastNode)) {
|
||||
return nodes.concat(lastNode);
|
||||
}
|
||||
const startIdx = match.index || 0;
|
||||
const endIdx = startIdx + match[0].length;
|
||||
const startStr = lastNode.slice(0, startIdx);
|
||||
const endStr = lastNode.slice(endIdx);
|
||||
return nodes.concat(
|
||||
endStr,
|
||||
<Text link={{ href: match[0], target: '_blank' }}>{match[0]}</Text>,
|
||||
startStr,
|
||||
);
|
||||
},
|
||||
[str],
|
||||
)
|
||||
.reverse();
|
||||
};
|
||||
19
frontend/packages/components/json-viewer/src/utils/index.ts
Normal file
19
frontend/packages/components/json-viewer/src/utils/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { generateFields } from './generate-field';
|
||||
export { generateStr2Link } from './generate-str-to-link';
|
||||
export { generateStrAvoidEscape } from './generate-str-avoid-escape';
|
||||
Reference in New Issue
Block a user