feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
/*
* 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 convertBytes = (bytes: number, decimals = 2) => {
if (!bytes) {
return '0 Byte';
}
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
const digit = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
return `${digit} ${sizes[i]}`;
};

View File

@@ -0,0 +1,37 @@
/*
* 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 dayjs from 'dayjs';
export const formatMessageBoxContentTime = (contentTime: number): string => {
if (contentTime < 1) {
return '';
}
// 当天hh:mm跨天mm-dd hh:mm跨年yyyy-mm-dd hh:mm
const now = Date.now();
const today = dayjs(now);
const messageDay = dayjs(contentTime);
if (today.year() !== messageDay.year()) {
return messageDay.format('YYYY-MM-DD HH:mm');
}
if (
today.month() !== messageDay.month() ||
today.date() !== messageDay.date()
) {
return messageDay.format('MM-DD HH:mm');
}
return messageDay.format('HH:mm');
};

View File

@@ -0,0 +1,22 @@
/*
* 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 defaultEnable = (value?: boolean) => {
if (typeof value === 'undefined') {
return true;
}
return value;
};

View File

@@ -0,0 +1,34 @@
/*
* 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 getFileExtensionAndName = (fileName: string) => {
const dotIndex = fileName.lastIndexOf('.');
if (dotIndex < 0) {
return {
nameWithoutExtension: fileName,
extension: '',
};
}
/**
* eg: .docx
*/
const extension = fileName.slice(dotIndex);
const nameWithoutExtension = fileName.slice(0, dotIndex);
return {
extension,
nameWithoutExtension,
};
};

View File

@@ -0,0 +1,64 @@
/*
* 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 getImageDisplayAttribute = (
width: number,
height: number,
contentWidth: number,
) => {
// 图片比例
const imageRatio = width / height;
// 展示宽度
let displayWidth = contentWidth;
// 展示高度
let displayHeight = contentWidth / imageRatio;
// 是否裁切
let isCover = false;
// (小尺寸图)
if (width <= contentWidth && height <= 240) {
displayWidth = width;
displayHeight = height;
} else if (imageRatio > contentWidth / 120) {
displayWidth = contentWidth;
displayHeight = 120;
isCover = true;
// (长竖图)图片宽度:图片高度 <= 0.5
} else if (imageRatio <= 0.5) {
displayWidth = 120;
displayHeight = 240;
isCover = true;
// (等比展示图)
} else if (0.5 <= imageRatio && imageRatio <= contentWidth / 240) {
displayWidth = 240 * imageRatio;
displayHeight = 240;
// (中长横图)
} else if (
contentWidth / 240 <= imageRatio &&
imageRatio <= contentWidth / 240
) {
displayWidth = contentWidth;
displayHeight = contentWidth / imageRatio;
}
return {
displayHeight,
displayWidth,
isCover,
};
};

View File

@@ -0,0 +1,25 @@
/*
* 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 * from './convert-bytes';
export * from './default-enable';
export * from './is-file';
export * from './is-function-call';
export * from './is-image';
export * from './is-suggestion';
export * from './is-text';
export * from './platform';
export * from './safe-json-parse';

View File

@@ -0,0 +1,23 @@
/*
* 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.
*/
/**
* 是否是在苹果平台的Webkit内核浏览器下
* 注这个判断条件不等于是在苹果设备下因为部分苹果设备例如Mac可以运行非原生Webkit引擎的浏览器例如Chromium(Blink)
*/
export const isAppleWebkit = () =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
typeof (window as any).webkitConvertPointFromNodeToPage === 'function';

View File

@@ -0,0 +1,22 @@
/*
* 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 IFileContent } from '@coze-common/chat-uikit-shared';
export const isFile = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any,
): value is IFileContent => value && 'file_list' in value;

View File

@@ -0,0 +1,26 @@
/*
* 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 IFunctionCallContent,
type IMessage,
} from '@coze-common/chat-uikit-shared';
export const isFunctionCall = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any,
message: IMessage,
): value is IFunctionCallContent => value && message.type === 'function_call';

View File

@@ -0,0 +1,21 @@
/*
* 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 IImageContent } from '@coze-common/chat-uikit-shared';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isImage = (value: any): value is IImageContent =>
value && 'image_list' in value;

View File

@@ -0,0 +1,20 @@
/*
* 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 ISuggestionContent } from '@coze-common/chat-uikit-shared';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isSuggestion = (value: any): value is ISuggestionContent =>
value && Array.isArray(value);

View 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.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isText = (value: any): value is string =>
value && typeof value === 'string';

View File

@@ -0,0 +1,97 @@
/*
* 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 { type Reporter } from '@coze-arch/logger';
import { UiKitReportEvents } from '../report-event';
import { type StoreStruct } from './type';
const PERSIST_CACHE_KEY = 'cache:@coze-common/chat-area';
const cachedReadWholeStore = (() => {
let cached: Partial<StoreStruct> | null;
return (reporter: Reporter) => {
if (!cached) {
cached = readFromCache(reporter);
}
return cached;
};
})();
const getPlaceholderStruct = (): Partial<StoreStruct> => ({});
export type ReadLocalStoreValue = <K extends keyof StoreStruct>(
name: K,
fallbackValue: StoreStruct[K],
) => StoreStruct[K];
export type WriteLocalStoreValue = <K extends keyof StoreStruct>(
name: K,
value: StoreStruct[K],
) => void;
export const getReadLocalStoreValue =
(reporter: Reporter): ReadLocalStoreValue =>
(name, fallbackValue) => {
const readStruct = cachedReadWholeStore(reporter);
if (!readStruct) {
return fallbackValue;
}
return readStruct[name] ?? fallbackValue;
};
export const getWriteLocalStoreValue =
(reporter: Reporter): WriteLocalStoreValue =>
(name, value) => {
const readStruct = cachedReadWholeStore(reporter);
const writeStruct = readStruct || getPlaceholderStruct();
writeStruct[name] = value;
saveToCache(reporter, writeStruct);
};
const readFromCache = (reporter: Reporter): Partial<StoreStruct> | null => {
try {
const content = window.localStorage.getItem(PERSIST_CACHE_KEY);
if (!content) {
return null;
}
const res = JSON.parse(content);
if (isObject(res)) {
return res as StoreStruct;
}
return null;
} catch (e) {
reporter.errorEvent({
eventName: UiKitReportEvents.FailReadLocalStorage,
error: e,
});
return null;
}
};
const saveToCache = (reporter: Reporter, struct: Partial<StoreStruct>) => {
try {
if (isObject(struct)) {
const content = JSON.stringify(struct);
window.localStorage.setItem(PERSIST_CACHE_KEY, content);
}
} catch (e) {
reporter.errorEvent({
eventName: UiKitReportEvents.FailWriteLocalStorage,
error: e,
});
}
};

View File

@@ -0,0 +1,30 @@
/*
* 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 interface StoreStruct {
coze_home_mention_tip_showed: boolean;
coze_home_favorite_list_display: boolean;
coze_home_favorite_list_filter: 'all' | 'byMe';
}
export type LocalCacheKey = keyof StoreStruct;
type StoreStructRange = Record<keyof StoreStruct, boolean | string | number>;
const storeStructRangeCheck = (range: StoreStructRange) => 0;
declare const voidStruct: StoreStruct;
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- 类型测试
const dryRun = () => storeStructRangeCheck(voidStruct);

View File

@@ -0,0 +1,57 @@
/*
* 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 { ContentType, type Message } from '@coze-common/chat-core';
export const makeFakeImageMessage = ({
originMessage,
key,
url,
width,
height,
}: {
originMessage: Message<ContentType>;
key: string;
url: string;
width: number;
height: number;
}) => {
const contentObj = {
image_list: [
{
key,
image_ori: {
url,
width,
height,
},
image_thumb: {
url,
width,
height,
},
},
],
};
const imageMessage: Message<ContentType.Image> = {
...originMessage,
content_obj: contentObj,
content: JSON.stringify(contentObj),
content_type: ContentType.Image,
};
return imageMessage;
};

View File

@@ -0,0 +1,64 @@
/*
* 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 {
ContentType,
type ImageModel,
type ImageMixItem,
type TextMixItem,
type FileModel,
type FileMixItem,
} from '@coze-common/chat-core/message/types';
export const isMultimodalContentListLike = (
value: unknown,
): value is { item_list: unknown[] } =>
isObject(value) && 'item_list' in value && Array.isArray(value.item_list);
export const isTextMixItem = (value: unknown): value is TextMixItem =>
isObject(value) &&
'type' in value &&
'text' in value &&
value.type === ContentType.Text;
export const isImageModel = (value: unknown): value is ImageModel =>
isObject(value) &&
'key' in value &&
'image_thumb' in value &&
'image_ori' in value;
export const isImageMixItem = (value: unknown): value is ImageMixItem =>
isObject(value) &&
'type' in value &&
'image' in value &&
isImageModel(value.image) &&
value.type === ContentType.Image;
export const isFileModel = (value: unknown): value is FileModel =>
isObject(value) &&
'file_key' in value &&
'file_name' in value &&
'file_type' in value &&
'file_size' in value &&
'file_url' in value;
export const isFileMixItem = (value: unknown): value is FileMixItem =>
isObject(value) &&
'type' in value &&
'file' in value &&
isFileModel(value.file) &&
value.type === ContentType.File;

View File

@@ -0,0 +1,72 @@
/*
* 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 Browser from 'bowser';
let getIsMobileCache: boolean | undefined;
/**
* 是否是移动设备
* 注ipad 不是移动设备
*/
const isMobile = () =>
Browser.getParser(navigator.userAgent)
.getPlatformType(true)
.includes('mobile');
export const getIsMobile = () => {
if (typeof getIsMobileCache === 'undefined') {
getIsMobileCache = isMobile();
}
return getIsMobileCache;
};
let getIsIPhoneOrIPadCache: boolean | undefined;
/**
* gpt-4 提供的代码
*/
export const getIsIPhoneOrIPad = () => {
if (typeof getIsIPhoneOrIPadCache === 'undefined') {
const { userAgent } = navigator;
const isAppleDevice = /iPad|iPhone|iPod/.test(userAgent);
const isIPadOS =
userAgent.includes('Macintosh') &&
'ontouchstart' in document.documentElement;
getIsIPhoneOrIPadCache = isAppleDevice || isIPadOS;
}
return getIsIPhoneOrIPadCache;
};
let getIsIPadCache: boolean | undefined;
/**
* gpt-4 提供的代码
*/
export const getIsIPad = () => {
if (typeof getIsIPadCache === 'undefined') {
const { userAgent } = navigator;
const isIPadDevice = /iPad/.test(userAgent);
const isIPadOS =
userAgent.includes('Macintosh') &&
'ontouchstart' in document.documentElement;
getIsIPadCache = isIPadDevice || isIPadOS;
}
return getIsIPadCache;
};
export const getIsMobileOrIPad = () => getIsMobile() || getIsIPhoneOrIPad();

View File

@@ -0,0 +1,20 @@
/*
* 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 enum UiKitReportEvents {
FailReadLocalStorage = 'uikit_FailReadLocalStorage',
FailWriteLocalStorage = 'uikit_FailWriteLocalStorage',
}

View File

@@ -0,0 +1,31 @@
/*
* 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.
*/
/**
* @deprecated 非常非常坏,尽快换为 typeSafeJsonParse
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const safeJSONParse: (v: any, emptyValue?: any) => any = (
v,
emptyValue,
) => {
try {
const json = JSON.parse(v);
return json;
} catch (e) {
return emptyValue ?? void 0;
}
};