feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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;
|
||||
})();
|
||||
@@ -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;
|
||||
})();
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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%)
|
||||
}
|
||||
@@ -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';
|
||||
Reference in New Issue
Block a user