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,181 @@
/*
* 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 { useEffect, useState } from 'react';
import { nanoid } from 'nanoid';
import { transSliceContentOutput } from '@coze-data/knowledge-modal-base';
import {
DocumentEditor,
useInitEditor,
EditorToolbar,
type Chunk,
} from '@coze-data/knowledge-common-components/text-knowledge-editor';
import { I18n } from '@coze-arch/i18n';
import { Form, Input, Modal, Toast } from '@coze-arch/coze-design';
import { MemoryApi } from '@coze-arch/bot-api';
import { useGetWebInfo } from './hooks/get-web-info';
import { editorToolbarActionRegistry } from './editor-toolbar-actions-contributes';
import { editorContextActionRegistry } from './editor-context-actions-contributes';
import styles from './index.module.less';
const MAX_DOC_NAME_LEN = 100;
export interface UseBrowseDetailModalReturnValue {
open: () => void;
node: JSX.Element;
}
export interface ViewOnlinePageDetailProps {
id?: string;
url?: string;
content?: string;
title?: string;
}
export interface BrowseUrlModalProps {
name: string;
webID?: string;
onSubmit: (name: string, content?: string) => void;
onCancel?: () => void;
}
export const BrowseUrlModal = ({
name,
webID,
onSubmit,
onCancel,
}: BrowseUrlModalProps) => {
const [docName, setDocName] = useState<string>(name);
const { data: pageList, runAsync, mutate } = useGetWebInfo();
const [initChunk, setInitChunk] = useState<Chunk>({
text_knowledge_editor_chunk_uuid: nanoid(),
content: '',
});
const { editor } = useInitEditor({
chunk: initChunk,
editorProps: {
attributes: {
class: 'h-[360px] overflow-y-auto',
},
},
onChange: v => {
mutate(
([] as ViewOnlinePageDetailProps[]).concat({
...pageList?.[0],
content: v.content ?? '',
}),
);
},
});
useEffect(() => {
setDocName(name);
}, [name]);
useEffect(() => {
if (webID) {
runAsync(webID).then(data => {
setInitChunk({
text_knowledge_editor_chunk_uuid: nanoid(),
content: data[0].content ?? '',
});
});
}
}, [webID, runAsync, editor]);
return (
<Modal
title={I18n.t('knowledge_insert_img_001')}
width={792}
visible
cancelText={I18n.t('Cancel')}
okText={I18n.t('datasets_segment_detailModel_save')}
maskClosable={false}
onOk={async () => {
const pageInfo = pageList?.[0];
const content = transSliceContentOutput(pageInfo.content as string);
await MemoryApi.SubmitWebContentV2({
web_id: pageInfo.id,
content,
});
Toast.success({
content: I18n.t('datasets_url_saveSuccess'),
showClose: false,
});
onSubmit?.(docName, content);
onCancel?.();
}}
onCancel={() => {
onCancel?.();
}}
>
<Form<Record<string, unknown>> layout="vertical" showValidateIcon={false}>
<Form.Slot
label={{ text: I18n.t('knowledge_upload_text_custom_doc_name') }}
>
<Input
className={styles['doc-name-input']}
value={docName}
onChange={(v: string) => setDocName(v)}
maxLength={MAX_DOC_NAME_LEN}
placeholder={I18n.t('knowledge_upload_text_custom_doc_name_tips')}
/>
</Form.Slot>
<Form.Slot
className={styles['form-segment-content']}
label={{ text: I18n.t('knowledge_upload_text_custom_doc_content') }}
>
<DocumentEditor
editor={editor}
placeholder={I18n.t(
'knowledge_upload_text_custom_doc_content_tips',
)}
editorContextMenuItemsRegistry={editorContextActionRegistry}
editorBottomSlot={
<EditorToolbar
editor={editor}
actionRegistry={editorToolbarActionRegistry}
/>
}
/>
{pageList?.[0]?.url ? (
<div className={styles['browse-source-url']}>
{I18n.t('knowledge_insert_img_003', {
url: pageList[0]?.url,
})}
</div>
) : null}
</Form.Slot>
</Form>
</Modal>
);
};
export const useBrowseUrlModal = (props: BrowseUrlModalProps) => {
const [visible, setVisible] = useState(false);
return {
open: () => setVisible(true),
close: () => setVisible(false),
node: visible ? (
<BrowseUrlModal {...props} onCancel={() => setVisible(false)} />
) : null,
};
};

View File

@@ -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 { UploadImageMenu } from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions/upload-image';
import {
createEditorActionFeatureRegistry,
type EditorActionRegistry,
} from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions';
export const editorContextActionRegistry: EditorActionRegistry = (() => {
const editorContextActionFeatureRegistry = createEditorActionFeatureRegistry(
'editor-context-actions',
);
editorContextActionFeatureRegistry.registerSome([
{
type: 'upload-image',
module: {
Component: UploadImageMenu,
},
},
]);
return editorContextActionFeatureRegistry;
})();

View File

@@ -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 { UploadImageButton } from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions/upload-image';
import {
createEditorActionFeatureRegistry,
type EditorActionRegistry,
} from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions';
export const editorToolbarActionRegistry: EditorActionRegistry = (() => {
const editorToolbarActionFeatureRegistry = createEditorActionFeatureRegistry(
'editor-toolbar-actions',
);
editorToolbarActionFeatureRegistry.registerSome([
{
type: 'upload-image',
module: {
Component: UploadImageButton,
},
},
]);
return editorToolbarActionFeatureRegistry;
})();

View File

@@ -0,0 +1,75 @@
/*
* 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 { useRequest } from 'ahooks';
import { KnowledgeApi } from '@coze-arch/bot-api';
import { type ViewOnlinePageDetailProps } from '@/types';
/**
* 将API返回的网页信息转换为视图数据
*/
const transformWebInfoToViewData = (webInfo: {
id?: string;
url?: string;
title?: string;
content?: string;
}): ViewOnlinePageDetailProps => ({
id: webInfo?.id,
url: webInfo?.url,
title: webInfo?.title,
content: webInfo?.content,
});
export const useGetWebInfo = (): {
data: ViewOnlinePageDetailProps[];
loading: boolean;
runAsync: (webID: string) => Promise<ViewOnlinePageDetailProps[]>;
mutate: (data: ViewOnlinePageDetailProps[]) => void;
} => {
const { data, mutate, loading, runAsync } = useRequest(
async (webID: string) => {
const { data: responseData } = await KnowledgeApi.GetWebInfo({
web_ids: [webID],
include_content: true,
});
// 如果没有数据,返回空数组
if (!responseData?.[webID]?.web_info) {
return [] as ViewOnlinePageDetailProps[];
}
const webInfo = responseData[webID].web_info;
const mainPageData = transformWebInfoToViewData(webInfo);
const result = [mainPageData];
// 处理子页面数据
if (webInfo?.subpages?.length) {
const subpagesData = webInfo.subpages.map(transformWebInfoToViewData);
result.push(...subpagesData);
}
return result;
},
);
return {
data: data || [],
loading,
runAsync,
mutate,
};
};

View File

@@ -0,0 +1,106 @@
/* stylelint-disable declaration-no-important */
.browse-detail-modal {
padding-bottom: 40px;
&-main {
display: flex;
align-items: center;
margin-bottom: 16px;
font-size: 14px;
font-weight: 600;
line-height: 20px;
color: rgb(28 31 35 / 100%);
&-image {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
margin-right: 7px;
background-color: rgb(152 205 253 / 100%);
border-radius: 2px;
}
&-title {
max-width: 40%;
}
&-url {
max-width: 40%;
margin-left: 7px;
font-size: 12px;
font-weight: 400;
line-height: 16px;
color: rgb(28 31 35 / 35%);
}
}
&-collapse {
overflow-y: auto;
max-height: 400px;
background-color: #f5f5f5;
border-radius: 4px;
:global {
.semi-collapse-item {
border-color: #efefef !important;
}
.semi-input-textarea-wrapper {
background-color: rgb(0 0 0 / 0%) !important;
border: none !important;
}
}
}
}
.browse-modal-header {
display: flex;
flex-wrap: nowrap;
align-items: center;
padding: 24px 0;
&-left {
display: flex;
align-items: center;
&-icon {
margin-right: 8px;
}
}
}
.modal.upgrade-level {
:global {
.semi-modal-body {
height: 0;
padding-bottom: 0;
}
.semi-modal-content {
background-color: var(--semi-color-tertiary-light-default);
}
}
}
.browse-detail-tooltip {
width: 200px;
word-wrap: break-word;
}
.browse-source-url {
margin-top: 12px;
font-size: 12px;
font-weight: 400;
font-style: normal;
line-height: 16px;
color: rgb(29 28 36 / 35%)
}

View File

@@ -0,0 +1,17 @@
/*
* 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 { useBrowseUrlModal } from './browser-url-modal';