Files
coze-studio/frontend/packages/agent-ide/entry/src/modes/workflow-mode/index.tsx
fanlv 890153324f feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
2025-07-20 17:36:12 +08:00

250 lines
7.8 KiB
TypeScript

/*
* 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 { useShallow } from 'zustand/react/shallow';
import classNames from 'classnames';
import { ResizableLayout } from '@coze-studio/components';
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
import { useBotSkillStore } from '@coze-studio/bot-detail-store/bot-skill';
import { useBotDetailIsReadonly } from '@coze-studio/bot-detail-store';
import { I18n } from '@coze-arch/i18n';
import { BotPageFromEnum } from '@coze-arch/bot-typings/common';
import {
IconBotMultiLeftBtnIcon,
IconBotMultiRightBtnIcon,
} from '@coze-arch/bot-icons';
import { LayoutContext, PlacementEnum } from '@coze-arch/bot-hooks';
import { BotMode, TabStatus } from '@coze-arch/bot-api/developer_api';
import { WorkflowConfigArea } from '@coze-agent-ide/workflow';
import { ToolGroupKey } from '@coze-agent-ide/tool-config';
import {
GroupingContainer,
AbilityAreaContainer,
ToolKey,
ToolView,
} from '@coze-agent-ide/tool';
import { useBotPageStore } from '@coze-agent-ide/space-bot/store';
import {
DataMemory,
settingAreaScrollId,
ChatBackground,
SheetView,
ContentView,
BotDebugPanel,
} from '@coze-agent-ide/space-bot/component';
import { OnboardingMessage } from '@coze-agent-ide/onboarding-message-adapter';
import { BotDebugChatArea } from '@coze-agent-ide/chat-debug-area';
import { BotConfigArea } from '@coze-agent-ide/bot-config-area-adapter';
import s from '../../index.module.less';
export interface WorkflowModeProps {
rightSheetSlot?: ReactNode;
memoryToolSlot?: ReactNode;
dialogToolSlot?: ReactNode;
renderChatTitleNode?: (params: {
pageFrom: BotPageFromEnum | undefined;
showBackground: boolean;
}) => ReactNode;
chatSlot?: ReactNode;
chatHeaderClassName?: string;
chatAreaReadOnly?: boolean;
}
// eslint-disable-next-line @coze-arch/max-line-per-function
export const WorkflowMode: React.FC<WorkflowModeProps> = ({
rightSheetSlot,
memoryToolSlot,
dialogToolSlot,
renderChatTitleNode,
chatSlot,
chatHeaderClassName,
chatAreaReadOnly,
}) => {
const { isInit, editable, historyVisible, pageFrom, defaultAllHidden } =
usePageRuntimeStore(
useShallow(state => ({
editable: state.editable,
isInit: state.init,
historyVisible: state.historyVisible,
pageFrom: state.pageFrom,
defaultAllHidden: !Object.values(
state.botSkillBlockCollapsibleState,
).some(val => val !== TabStatus.Hide),
})),
);
const { showBackground } = useBotSkillStore(
useShallow(({ backgroundImageInfoList }) => ({
showBackground: Boolean(
backgroundImageInfoList?.[0]?.mobile_background_image?.origin_image_url,
),
})),
);
const modeSwitching = useBotPageStore(state => state.bot.modeSwitching);
const isReadonly = useBotDetailIsReadonly();
const toolArea = (
<div className="overflow-hidden h-full flex">
<LayoutContext value={{ placement: PlacementEnum.CENTER }}>
<div
className={classNames(s['setting-area'], 'coz-bg-plus', {
[s['tool-hidden']]: defaultAllHidden,
})}
>
<div
className="px-[28px] py-[16px] overflow-auto flex-1"
id={settingAreaScrollId}
>
<ToolView>
<WorkflowConfigArea />
<div className="mx-[-28px] my-[16px] border-0 border-b border-solid coz-stroke-primary" />
<GroupingContainer
toolGroupKey={ToolGroupKey.MEMORY}
title={I18n.t('bot_edit_type_memory')}
>
{/* 变量存储 */}
<DataMemory
toolKey={ToolKey.VARIABLE}
title={I18n.t('user_profile')}
/>
{memoryToolSlot}
</GroupingContainer>
<GroupingContainer
toolGroupKey={ToolGroupKey.DIALOG}
title={I18n.t('bot_edit_type_dialog')}
>
{/* 开场白 */}
<OnboardingMessage
toolKey={ToolKey.ONBOARDING}
title={I18n.t('bot_preview_opening_remarks')}
/>
{/* 聊天背景图片 */}
<ChatBackground
toolKey={ToolKey.BACKGROUND}
title={I18n.t('bgi_title')}
/>
{dialogToolSlot}
</GroupingContainer>
</ToolView>
</div>
</div>
</LayoutContext>
</div>
);
const leftSheet = (
<SheetView
headerClassName={classNames([
'coz-bg-plus',
'coz-fg-secondary',
s['sheet-view-left-header'],
s['sheet-view-new-header'],
])}
mode={BotMode.WorkflowMode}
title={I18n.t('bot_build_title')}
titleNode={
<div className={s['sheet-title-node-cover']}>
<BotConfigArea pageFrom={pageFrom} editable={editable} />
</div>
}
slideProps={{
placement: 'left',
closeBtnTooltip: I18n.t('chatflow_develop_tooltip_hide'),
openBtnTooltip: I18n.t('chatflow_develop_tooltip_show'),
width: 400,
visible: true,
btnNode: <IconBotMultiLeftBtnIcon />,
}}
>
{toolArea}
</SheetView>
);
const rightSheet = (
<SheetView
mode={BotMode.WorkflowMode}
title={I18n.t('bot_preview_debug_title')}
titleClassName={showBackground ? '!coz-fg-images-white' : ''}
containerClassName={classNames(
s['bj-cover'],
showBackground && `${s['bj-img-cover']} `,
s['bj-single-cover'],
)}
headerClassName={classNames(
s['debug-chat-header-padding'],
`${s['border-cover']}`,
{
'!bg-transparent': showBackground,
},
chatHeaderClassName,
)}
slideProps={{
placement: 'right',
closeBtnTooltip: I18n.t('chatflow_preview_tooltip_hide'),
openBtnTooltip: I18n.t('chatflow_preview_tooltip_show'),
width: 400,
visible: true,
btnNode: <IconBotMultiRightBtnIcon />,
}}
titleNode={renderChatTitleNode?.({ pageFrom, showBackground })}
renderContent={headerNode => (
<div className={classNames(s['message-area'])}>
<BotDebugChatArea
headerNode={headerNode}
readOnly={chatAreaReadOnly}
/>
{chatSlot}
</div>
)}
/>
);
return (
<div
className={classNames(
s.container,
historyVisible && s['playground-neat'],
pageFrom === BotPageFromEnum.Store && s.store,
)}
>
<AbilityAreaContainer
enableToolHiddenMode
isReadonly={isReadonly}
mode={BotMode.WorkflowMode}
modeSwitching={modeSwitching}
isInit={isInit}
>
<ContentView mode={BotMode.WorkflowMode}>
<ResizableLayout
className="h-full"
hotZoneClassName={s['layout-hotzone']}
>
<div className="w-1/2 min-w-[610px]">{leftSheet}</div>
<div className="w-1/2 min-w-[480px]">{rightSheet}</div>
</ResizableLayout>
</ContentView>
{/* Debug调试台 */}
<BotDebugPanel />
{rightSheetSlot}
</AbilityAreaContainer>
</div>
);
};