feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
const { defineConfig } = require('@coze-arch/stylelint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
extends: [],
|
||||
});
|
||||
63
frontend/packages/project-ide/biz-workflow/README.md
Normal file
63
frontend/packages/project-ide/biz-workflow/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# @coze-project-ide/biz-workflow
|
||||
|
||||
A workflow package for the Coze Studio monorepo
|
||||
|
||||
## Overview
|
||||
|
||||
This package is part of the Coze Studio monorepo and provides workflow functionality. It serves as a core component in the Coze ecosystem.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Add this package to your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@coze-project-ide/biz-workflow": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
rush update
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { /* exported functions/components */ } from '@coze-project-ide/biz-workflow';
|
||||
|
||||
// Example usage
|
||||
// TODO: Add specific usage examples
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Core functionality for Coze Studio
|
||||
- TypeScript support
|
||||
- Modern ES modules
|
||||
|
||||
## API Reference
|
||||
|
||||
Please refer to the TypeScript definitions for detailed API documentation.
|
||||
|
||||
## Development
|
||||
|
||||
This package is built with:
|
||||
|
||||
- TypeScript
|
||||
- Modern JavaScript
|
||||
|
||||
- ESLint for code quality
|
||||
|
||||
## Contributing
|
||||
|
||||
This package is part of the Coze Studio monorepo. Please follow the monorepo contribution guidelines.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json",
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./lib-ts"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dupCheck": {
|
||||
"ignoreGlobPatterns": ["src/**/*"]
|
||||
}
|
||||
}
|
||||
14
frontend/packages/project-ide/biz-workflow/eslint.config.js
Normal file
14
frontend/packages/project-ide/biz-workflow/eslint.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {
|
||||
'no-restricted-syntax': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'@typescript-eslint/no-magic-numbers': 'off',
|
||||
'@coze-arch/no-batch-import-or-export': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
},
|
||||
});
|
||||
51
frontend/packages/project-ide/biz-workflow/package.json
Normal file
51
frontend/packages/project-ide/biz-workflow/package.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "@coze-project-ide/biz-workflow",
|
||||
"version": "0.0.1",
|
||||
"author": "jiangxujin@bytedance.com",
|
||||
"exports": {
|
||||
".": "./src/index.tsx"
|
||||
},
|
||||
"main": "./src/index.tsx",
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache --quiet"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coze-arch/bot-api": "workspace:*",
|
||||
"@coze-arch/bot-error": "workspace:*",
|
||||
"@coze-arch/bot-flags": "workspace:*",
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-arch/report-events": "workspace:*",
|
||||
"@coze-common/auth": "workspace:*",
|
||||
"@coze-project-ide/biz-components": "workspace:*",
|
||||
"@coze-project-ide/framework": "workspace:*",
|
||||
"@coze-studio/open-chat": "workspace:*",
|
||||
"@coze-studio/user-store": "workspace:*",
|
||||
"@coze-workflow/base": "workspace:*",
|
||||
"@coze-workflow/components": "workspace:*",
|
||||
"@coze-workflow/playground": "workspace:*",
|
||||
"ahooks": "^3.7.8",
|
||||
"classnames": "^2.3.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react-scroll": "^1.9.3",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/stylelint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@rsbuild/core": "1.1.13",
|
||||
"@types/node": "18.18.9",
|
||||
"@types/react": "18.2.37",
|
||||
"@types/react-dom": "18.2.15",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"react-router-dom": "^6.22.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"typescript": "~5.8.2",
|
||||
"webpack": "~5.91.0"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 { WorkflowTooltip } from './workflow-tooltip';
|
||||
export { WorkflowWidgetIcon } from './workflow-widget-icon';
|
||||
@@ -0,0 +1,4 @@
|
||||
.image {
|
||||
width: 160px;
|
||||
border-radius: var(--coze-8);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 React, { type FC, useMemo } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
|
||||
import { Image, useTheme } from '@coze-arch/coze-design';
|
||||
|
||||
import workflowLightImg from './assets/workflow-light.jpg';
|
||||
import workflowDarkImg from './assets/workflow-dark.jpg';
|
||||
import chatflowLightImg from './assets/chatflow-light.jpg';
|
||||
import chatflowDarkImg from './assets/chatflow-dark.jpg';
|
||||
|
||||
export interface WorkflowTooltipProps {
|
||||
flowMode: WorkflowMode;
|
||||
}
|
||||
|
||||
const ILLUSTRATION_IMG_URL = {
|
||||
workflow: {
|
||||
dark: workflowDarkImg,
|
||||
light: workflowLightImg,
|
||||
},
|
||||
chatflow: {
|
||||
dark: chatflowDarkImg,
|
||||
light: chatflowLightImg,
|
||||
},
|
||||
};
|
||||
|
||||
export const WorkflowTooltip: FC<WorkflowTooltipProps> = ({ flowMode }) => {
|
||||
const { theme } = useTheme();
|
||||
const imgUrl = useMemo(() => {
|
||||
switch (flowMode) {
|
||||
case WorkflowMode.ChatFlow:
|
||||
return (
|
||||
ILLUSTRATION_IMG_URL.chatflow[theme] ||
|
||||
ILLUSTRATION_IMG_URL.chatflow.light
|
||||
);
|
||||
default:
|
||||
return (
|
||||
ILLUSTRATION_IMG_URL.workflow[theme] ||
|
||||
ILLUSTRATION_IMG_URL.workflow.light
|
||||
);
|
||||
}
|
||||
}, [theme, flowMode]);
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Image
|
||||
src={imgUrl}
|
||||
crossOrigin="anonymous"
|
||||
imgStyle={{
|
||||
width: 200,
|
||||
minHeight: 120,
|
||||
borderRadius: '7.5px',
|
||||
border: '1px solid var(--coz-stroke-primary)',
|
||||
}}
|
||||
preview={false}
|
||||
/>
|
||||
<div className="px-2 pt-1 pb-2">
|
||||
<p className="text-14 font-medium coz-fg-primary leading-5">
|
||||
{flowMode === WorkflowMode.Workflow ? 'Workflow' : 'Chatflow'}
|
||||
</p>
|
||||
<span className="text-[12px] coz-fg-primary leading-4">
|
||||
{flowMode === WorkflowMode.Workflow
|
||||
? I18n.t('wf_chatflow_02')
|
||||
: I18n.t('wf_chatflow_01')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 FC, useEffect, useState, useMemo } from 'react';
|
||||
|
||||
import { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
|
||||
import { type WidgetContext } from '@coze-project-ide/framework';
|
||||
|
||||
import { WORKFLOW_SUB_TYPE_ICON_MAP } from '../constants';
|
||||
|
||||
interface WorkflowWidgetIconProps {
|
||||
context: WidgetContext;
|
||||
}
|
||||
export const WorkflowWidgetIcon: FC<WorkflowWidgetIconProps> = ({
|
||||
context,
|
||||
}) => {
|
||||
const { widget } = context;
|
||||
const [iconType, setIconType] = useState<string>(
|
||||
widget.getIconType() || String(WorkflowMode.Workflow),
|
||||
);
|
||||
const icon = useMemo(() => WORKFLOW_SUB_TYPE_ICON_MAP[iconType], [iconType]);
|
||||
useEffect(() => {
|
||||
const disposable = widget.onIconTypeChanged(_iconType =>
|
||||
setIconType(_iconType),
|
||||
);
|
||||
return () => disposable?.dispose?.();
|
||||
}, []);
|
||||
return icon;
|
||||
};
|
||||
24
frontend/packages/project-ide/biz-workflow/src/constants.tsx
Normal file
24
frontend/packages/project-ide/biz-workflow/src/constants.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import { IconCozChat, IconCozWorkflow } from '@coze-arch/coze-design/icons';
|
||||
import { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
|
||||
export const WORKFLOW_SUB_TYPE_ICON_MAP = {
|
||||
[WorkflowMode.Workflow]: <IconCozWorkflow />,
|
||||
[WorkflowMode.ChatFlow]: <IconCozChat />,
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
.skeleton-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 16px;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 28px 97px;
|
||||
|
||||
.skeleton-item {
|
||||
display: flex;
|
||||
column-gap: 12px;
|
||||
width: 100%;
|
||||
|
||||
.skeleton-column {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.skeleton-avatar {
|
||||
flex-shrink: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.skeleton-name {
|
||||
width: 64px;
|
||||
height: 13px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.skeleton-content {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
height: 102px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.skeleton-content-mini {
|
||||
flex-shrink: 0;
|
||||
width: 50%;
|
||||
height: 60px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 React, { Suspense, lazy, useMemo } from 'react';
|
||||
|
||||
import { userStoreService } from '@coze-studio/user-store';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozIllusAdd } from '@coze-arch/coze-design/illustrations';
|
||||
import { EmptyState } from '@coze-arch/coze-design';
|
||||
import { CreateEnv } from '@coze-arch/bot-api/workflow_api';
|
||||
import type { IProject } from '@coze-studio/open-chat';
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
|
||||
import { DISABLED_CONVERSATION } from '../constants';
|
||||
import { useSkeleton } from './use-skeleton';
|
||||
|
||||
const LazyBuilderChat = lazy(async () => {
|
||||
const { BuilderChat } = await import('@coze-studio/open-chat');
|
||||
return { default: BuilderChat };
|
||||
});
|
||||
|
||||
export interface ChatHistoryProps {
|
||||
/**
|
||||
* 会话 id
|
||||
*/
|
||||
conversationId?: string;
|
||||
/**
|
||||
* 会话名称
|
||||
*/
|
||||
conversationName: string;
|
||||
/**
|
||||
* 渠道 id
|
||||
*/
|
||||
connectorId: string;
|
||||
/**
|
||||
* 创建会话的环境
|
||||
*/
|
||||
createEnv: CreateEnv;
|
||||
}
|
||||
|
||||
export const ChatHistory: React.FC<ChatHistoryProps> = ({
|
||||
conversationId,
|
||||
conversationName,
|
||||
connectorId,
|
||||
createEnv,
|
||||
}) => {
|
||||
const userInfo = userStoreService.getUserInfo();
|
||||
const renderLoading = useSkeleton();
|
||||
|
||||
const projectInfo = useIDEGlobalStore(
|
||||
store => store.projectInfo?.projectInfo,
|
||||
);
|
||||
|
||||
const innerProjectInfo = useMemo<IProject>(
|
||||
() => ({
|
||||
id: projectInfo?.id || '',
|
||||
conversationId,
|
||||
connectorId,
|
||||
conversationName,
|
||||
name: conversationName || projectInfo?.name,
|
||||
iconUrl: projectInfo?.icon_url,
|
||||
type: 'app',
|
||||
mode: createEnv === CreateEnv.Draft ? 'draft' : 'release',
|
||||
caller: createEnv === CreateEnv.Draft ? 'CANVAS' : undefined,
|
||||
}),
|
||||
[projectInfo, conversationId, connectorId, conversationName, createEnv],
|
||||
);
|
||||
|
||||
const chatUserInfo = {
|
||||
id: userInfo?.user_id_str || '',
|
||||
name: userInfo?.name || '',
|
||||
avatar: userInfo?.avatar_url || '',
|
||||
};
|
||||
|
||||
if (
|
||||
!innerProjectInfo.id ||
|
||||
!conversationName ||
|
||||
(conversationId === DISABLED_CONVERSATION && createEnv !== CreateEnv.Draft)
|
||||
) {
|
||||
return (
|
||||
<EmptyState
|
||||
size="full_screen"
|
||||
icon={<IconCozIllusAdd />}
|
||||
title={I18n.t('wf_chatflow_61')}
|
||||
description={I18n.t('wf_chatflow_62')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<LazyBuilderChat
|
||||
workflow={{}}
|
||||
project={innerProjectInfo}
|
||||
areaUi={{
|
||||
// 只看会话记录,不可操作
|
||||
isDisabled: true,
|
||||
isNeedClearContext: false,
|
||||
input: {
|
||||
isShow: false,
|
||||
},
|
||||
renderLoading,
|
||||
uiTheme: 'chatFlow',
|
||||
}}
|
||||
userInfo={chatUserInfo}
|
||||
auth={{
|
||||
type: 'internal',
|
||||
}}
|
||||
></LazyBuilderChat>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
@@ -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 React, { useCallback } from 'react';
|
||||
|
||||
import { Skeleton } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const useSkeleton = () => {
|
||||
const renderLoading = useCallback(
|
||||
() => (
|
||||
<Skeleton
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
placeholder={
|
||||
<div className={styles['skeleton-container']}>
|
||||
<div className={styles['skeleton-item']}>
|
||||
<Skeleton.Avatar className={styles['skeleton-avatar']} />
|
||||
<div className={styles['skeleton-column']}>
|
||||
<Skeleton.Title className={styles['skeleton-name']} />
|
||||
<Skeleton.Image className={styles['skeleton-content']} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['skeleton-item']}>
|
||||
<Skeleton.Avatar className={styles['skeleton-avatar']} />
|
||||
<Skeleton.Image className={styles['skeleton-content-mini']} />
|
||||
</div>
|
||||
<div className={styles['skeleton-item']}>
|
||||
<Skeleton.Avatar className={styles['skeleton-avatar']} />
|
||||
<div className={styles['skeleton-column']}>
|
||||
<Skeleton.Title className={styles['skeleton-name']} />
|
||||
<Skeleton.Image className={styles['skeleton-content']} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
active
|
||||
loading={true}
|
||||
/>
|
||||
),
|
||||
[],
|
||||
);
|
||||
return renderLoading;
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 { I18n } from '@coze-arch/i18n';
|
||||
|
||||
// 默认会话 unique_id
|
||||
export const DEFAULT_UNIQUE_ID = '0';
|
||||
|
||||
export const DEFAULT_CONVERSATION_NAME = 'Default';
|
||||
|
||||
export const MAX_LIMIT = 1000;
|
||||
|
||||
export enum ErrorCode {
|
||||
DUPLICATE = 'duplicate',
|
||||
EXCEED_MAX_LENGTH = 'exceed-max-length',
|
||||
}
|
||||
|
||||
export const MAX_INPUT_LEN = 200;
|
||||
|
||||
/**
|
||||
* 调试的渠道 id
|
||||
*/
|
||||
export const DEBUG_CONNECTOR_ID = '_10000010';
|
||||
|
||||
export const DEFAULT_CONNECTOR = {
|
||||
connectorId: DEBUG_CONNECTOR_ID,
|
||||
connectorName: I18n.t('workflow_saved_database'),
|
||||
};
|
||||
|
||||
export const COZE_CONNECTOR_ID = '10000010';
|
||||
export const API_CONNECTOR_ID = '1024';
|
||||
export const CHAT_SDK_CONNECTOR_ID = '999';
|
||||
export const COZE_CONNECTOR_IDS = [COZE_CONNECTOR_ID, '10000122', '10000129'];
|
||||
/**
|
||||
* 不存在的会话
|
||||
*/
|
||||
export const DISABLED_CONVERSATION = '0';
|
||||
|
||||
/**
|
||||
* 只展示这些线上渠道,其他的后端不支持 @qiangshunliang
|
||||
*/
|
||||
export const ALLOW_CONNECTORS = [
|
||||
COZE_CONNECTOR_ID,
|
||||
API_CONNECTOR_ID,
|
||||
CHAT_SDK_CONNECTOR_ID,
|
||||
];
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 React, { useState, useMemo } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozWarningCircleFill } from '@coze-arch/coze-design/icons';
|
||||
import { Input, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import { ErrorCode } from '../constants';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
export const EditInput = ({
|
||||
ref,
|
||||
defaultValue,
|
||||
loading,
|
||||
onBlur,
|
||||
onValidate,
|
||||
}: {
|
||||
ref?: React.Ref<HTMLInputElement>;
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
defaultValue?: string;
|
||||
/**
|
||||
* loading
|
||||
*/
|
||||
loading: boolean;
|
||||
/**
|
||||
* 失焦 / 回车后执行的行为
|
||||
*/
|
||||
onBlur?: (input?: string, error?: ErrorCode) => void;
|
||||
/**
|
||||
* 校验函数,返回 true 标识校验通过
|
||||
*/
|
||||
onValidate?: (input: string) => ErrorCode | undefined;
|
||||
}) => {
|
||||
const [input, setInput] = useState(defaultValue);
|
||||
const [error, setError] = useState<ErrorCode | undefined>(undefined);
|
||||
|
||||
const handleCreateSession = () => {
|
||||
onBlur?.(input, error);
|
||||
setInput('');
|
||||
};
|
||||
|
||||
const handleValidateName = (_input: string) => {
|
||||
setInput(_input);
|
||||
const validateRes = onValidate?.(_input);
|
||||
if (validateRes) {
|
||||
setError(validateRes);
|
||||
} else {
|
||||
setError(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const renderError = useMemo(() => {
|
||||
if (error === ErrorCode.DUPLICATE) {
|
||||
return I18n.t('wf_chatflow_109');
|
||||
} else if (error === ErrorCode.EXCEED_MAX_LENGTH) {
|
||||
return I18n.t('wf_chatflow_116');
|
||||
}
|
||||
}, [error]);
|
||||
return (
|
||||
<Input
|
||||
ref={ref}
|
||||
className={s.input}
|
||||
size="small"
|
||||
loading={loading}
|
||||
autoFocus
|
||||
onChange={handleValidateName}
|
||||
placeholder={'Please enter'}
|
||||
defaultValue={defaultValue}
|
||||
error={Boolean(error)}
|
||||
suffix={
|
||||
error ? (
|
||||
<Tooltip content={renderError} position="right">
|
||||
<IconCozWarningCircleFill className="coz-fg-hglt-red absolute right-1 text-[13px]" />
|
||||
</Tooltip>
|
||||
) : null
|
||||
}
|
||||
onBlur={handleCreateSession}
|
||||
onEnterPress={handleCreateSession}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
.page-container {
|
||||
display: flex;
|
||||
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
|
||||
background: var(--coz-bg-plus);
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
.chat-list-container {
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
|
||||
width: 260px;
|
||||
height: 100%;
|
||||
|
||||
border-right: 1px solid var(--coz-stroke-primary);
|
||||
|
||||
.test-run-title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
margin-bottom: 12px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 4px;
|
||||
padding: 12px 12px 0;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-plus);
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 12px;
|
||||
padding: 0 12px;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.new-chat-button {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.chat-item {
|
||||
cursor: pointer;
|
||||
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
column-gap: 4px;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
margin-bottom: 2px;
|
||||
padding: 4px 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-secondary);
|
||||
|
||||
border-radius: 6px;
|
||||
|
||||
.icons {
|
||||
display: none;
|
||||
flex-grow: 1;
|
||||
column-gap: 4px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
width: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--coz-mg-primary);
|
||||
|
||||
.icons {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-item-activate {
|
||||
background-color: var(--coz-mg-primary);
|
||||
}
|
||||
|
||||
.chat-item-editing {
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-area {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ui 特殊兼容样式,白底 @maijinning.888
|
||||
// 设计稿地址:https://www.figma.com/design/e4X3MThLYyo1Fhjcg8uhpr/Workflow-%26-Imageflow?node-id=7133-217329&node-type=instance&m=dev
|
||||
.input {
|
||||
/* stylelint-disable-next-line declaration-no-important */
|
||||
background-color: #FFF !important;
|
||||
|
||||
:global {
|
||||
.semi-input {
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.new-list {
|
||||
overflow-y: auto;
|
||||
flex-grow: 1;
|
||||
height: 0;
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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 { Element } from 'react-scroll';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import React, {
|
||||
useMemo,
|
||||
useCallback,
|
||||
useState,
|
||||
useRef,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
type ProjectConversation,
|
||||
CreateEnv,
|
||||
} from '@coze-arch/bot-api/workflow_api';
|
||||
import {
|
||||
CONVERSATION_URI,
|
||||
getURIPathByPathname,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
import { StaticChatList } from '../static-chat-list';
|
||||
import {
|
||||
useCreateChat,
|
||||
useUpdateChat,
|
||||
useDeleteChat,
|
||||
useBatchDelete,
|
||||
useConversationListWithConnector,
|
||||
} from '../hooks';
|
||||
import { DynamicChatList } from '../dynamic-chat-list';
|
||||
import { ErrorCode, MAX_INPUT_LEN } from '../constants';
|
||||
import { ChatHistory } from '../chat-history';
|
||||
import { EditInput } from './edit-input';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface ConversationContentProps {
|
||||
connectorId: string;
|
||||
createEnv: CreateEnv;
|
||||
canEdit?: boolean;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @coze-arch/max-line-per-function
|
||||
export const ConversationContent: React.FC<ConversationContentProps> = ({
|
||||
connectorId,
|
||||
createEnv,
|
||||
canEdit,
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
// 顶部的创建弹窗
|
||||
const [inputVisible, setInputVisible] = useState(false);
|
||||
const [activateChat, setActivateChat] = useState<
|
||||
ProjectConversation | undefined
|
||||
>();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const { staticList, dynamicList, fetch } = useConversationListWithConnector({
|
||||
connector_id: connectorId,
|
||||
create_env: createEnv,
|
||||
});
|
||||
|
||||
const { loading: createLoading, handleCreateChat } = useCreateChat({
|
||||
manualRefresh: () => fetch(),
|
||||
});
|
||||
const { handleUpdateChat, loading: updateLoading } = useUpdateChat({
|
||||
manualRefresh: () => fetch(),
|
||||
});
|
||||
const { handleDelete, modalDom } = useDeleteChat({
|
||||
staticList,
|
||||
manualRefresh: () => fetch(),
|
||||
setActivateChat,
|
||||
});
|
||||
|
||||
const handleSelectChat = useCallback((chatItem?: ProjectConversation) => {
|
||||
setActivateChat(chatItem);
|
||||
}, []);
|
||||
const { batchDelete } = useBatchDelete({
|
||||
connectorId,
|
||||
createEnv,
|
||||
manualRefresh: () => fetch(),
|
||||
setActivateChat,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// 初始化选中接口返回数据。conversationId 可能为 0 要展示空
|
||||
if (!activateChat && staticList?.length) {
|
||||
handleSelectChat(staticList[0]);
|
||||
}
|
||||
}, [staticList]);
|
||||
|
||||
const handleCreateInput = useCallback(() => {
|
||||
setInputVisible(true);
|
||||
}, []);
|
||||
|
||||
const handleValidateName = (_input: string) => {
|
||||
if (_input?.length > MAX_INPUT_LEN) {
|
||||
return ErrorCode.EXCEED_MAX_LENGTH;
|
||||
}
|
||||
if (staticList.some(item => item.conversation_name === _input)) {
|
||||
return ErrorCode.DUPLICATE;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const handleCreateSession = async (input?: string, error?: ErrorCode) => {
|
||||
if (!input) {
|
||||
setInputVisible(false);
|
||||
return;
|
||||
}
|
||||
if (!error) {
|
||||
await handleCreateChat(input);
|
||||
}
|
||||
setInputVisible(false);
|
||||
};
|
||||
|
||||
const conversationName = useMemo(() => {
|
||||
if (
|
||||
createEnv !== CreateEnv.Draft &&
|
||||
activateChat?.release_conversation_name
|
||||
) {
|
||||
return activateChat?.release_conversation_name;
|
||||
}
|
||||
return activateChat?.conversation_name || '';
|
||||
}, [createEnv, activateChat]);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputVisible) {
|
||||
inputRef.current?.scrollIntoView();
|
||||
}
|
||||
}, [inputVisible]);
|
||||
|
||||
const renderCreateInput = () =>
|
||||
inputVisible ? (
|
||||
<EditInput
|
||||
ref={inputRef}
|
||||
loading={createLoading}
|
||||
onBlur={handleCreateSession}
|
||||
onValidate={handleValidateName}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
useEffect(() => {
|
||||
// 判断会话页是否显示,实现切换tab时刷新列表效果
|
||||
const value = getURIPathByPathname(pathname);
|
||||
if (value && CONVERSATION_URI.displayName === value) {
|
||||
fetch();
|
||||
}
|
||||
}, [pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
setActivateChat(undefined);
|
||||
}, [connectorId, createEnv]);
|
||||
|
||||
return (
|
||||
<div className={styles['page-container']}>
|
||||
<div className={styles['chat-list-container']}>
|
||||
<div className={styles.title}>{I18n.t('wf_chatflow_101')}</div>
|
||||
<div className={styles.description}>
|
||||
{createEnv === CreateEnv.Release
|
||||
? I18n.t('wf_chatflow_102')
|
||||
: I18n.t('workflow_chatflow_testrun_conversation_des')}
|
||||
</div>
|
||||
<div className={styles['new-list']} id="conversation-list">
|
||||
<Element name="static" />
|
||||
<StaticChatList
|
||||
canEdit={canEdit}
|
||||
list={staticList}
|
||||
activateChat={activateChat}
|
||||
updateLoading={updateLoading}
|
||||
onUpdate={handleUpdateChat}
|
||||
onDelete={handleDelete}
|
||||
onValidate={handleValidateName}
|
||||
onSelectChat={handleSelectChat}
|
||||
renderCreateInput={renderCreateInput}
|
||||
handleCreateInput={handleCreateInput}
|
||||
/>
|
||||
<Element name="dynamic" />
|
||||
<DynamicChatList
|
||||
list={dynamicList}
|
||||
canEdit={canEdit}
|
||||
activateChat={activateChat}
|
||||
onDelete={handleDelete}
|
||||
onBatchDelete={batchDelete}
|
||||
onSelectChat={handleSelectChat}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['chat-area']}>
|
||||
<ChatHistory
|
||||
createEnv={createEnv}
|
||||
connectorId={connectorId}
|
||||
conversationId={activateChat?.conversation_id}
|
||||
conversationName={conversationName}
|
||||
/>
|
||||
</div>
|
||||
{modalDom}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/* stylelint-disable declaration-no-important */
|
||||
.list-container {
|
||||
padding: 0 12px;
|
||||
|
||||
.empty-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
&.in-batch {
|
||||
padding-bottom: 56px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
top: 28px;
|
||||
bottom: 0;
|
||||
|
||||
&.is-bottom {
|
||||
border-top: 1px solid var(--coz-stroke-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.batch-wrap {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: 16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
align-items: center;
|
||||
|
||||
padding: 5px;
|
||||
|
||||
background-color: var(--coz-bg-max);
|
||||
border: 1px solid var(--coz-stroke-primary);
|
||||
border-radius: 10px;
|
||||
box-shadow: var(--coz-shadow-default);
|
||||
}
|
||||
|
||||
.is-batch-selected {
|
||||
background-color: var(--coz-mg-hglt-secondary)!important;
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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 @coze-arch/max-line-per-function */
|
||||
import { scroller } from 'react-scroll';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useInViewport } from 'ahooks';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozEmpty,
|
||||
IconCozTrashCan,
|
||||
IconCozListDisorder,
|
||||
IconCozCross,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
EmptyState,
|
||||
IconButton,
|
||||
Button,
|
||||
Typography,
|
||||
Tooltip,
|
||||
Checkbox,
|
||||
Popconfirm,
|
||||
} from '@coze-arch/coze-design';
|
||||
import { useFlags } from '@coze-arch/bot-flags';
|
||||
import { type ProjectConversation } from '@coze-arch/bot-api/workflow_api';
|
||||
|
||||
import { TitleWithTooltip } from '../title-with-tooltip';
|
||||
import commonStyle from '../conversation-content/index.module.less';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const ChatItem: React.FC<{
|
||||
chat: ProjectConversation;
|
||||
canEdit?: boolean;
|
||||
isActivate: boolean;
|
||||
isInBatch: boolean;
|
||||
isInBatchSelected: boolean;
|
||||
onBatchSelectChange: (data: ProjectConversation) => void;
|
||||
onActivate: (data: ProjectConversation) => void;
|
||||
onDelete: (data: ProjectConversation) => void;
|
||||
}> = ({
|
||||
chat,
|
||||
isActivate,
|
||||
isInBatch,
|
||||
isInBatchSelected,
|
||||
canEdit,
|
||||
onBatchSelectChange,
|
||||
onActivate,
|
||||
onDelete,
|
||||
}) => {
|
||||
const showActivate = isActivate && !isInBatch;
|
||||
const canDeleteOperate = canEdit && !isInBatch;
|
||||
|
||||
const handleClick = () => {
|
||||
if (isInBatch) {
|
||||
onBatchSelectChange(chat);
|
||||
} else {
|
||||
onActivate(chat);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
commonStyle['chat-item'],
|
||||
showActivate && commonStyle['chat-item-activate'],
|
||||
isInBatchSelected && styles['is-batch-selected'],
|
||||
)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{isInBatch ? <Checkbox checked={isInBatchSelected} /> : null}
|
||||
<Text ellipsis={{ showTooltip: true }}>{chat.conversation_name}</Text>
|
||||
{canDeleteOperate ? (
|
||||
<div className={commonStyle.icons}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
icon={<IconCozTrashCan />}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onDelete(chat);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DynamicChatList = ({
|
||||
canEdit,
|
||||
list,
|
||||
activateChat,
|
||||
onDelete,
|
||||
onBatchDelete,
|
||||
onSelectChat,
|
||||
}: {
|
||||
canEdit?: boolean;
|
||||
list: ProjectConversation[];
|
||||
activateChat?: ProjectConversation;
|
||||
onDelete: (chatItem: ProjectConversation) => Promise<void>;
|
||||
onBatchDelete: (ids: string[]) => Promise<void>;
|
||||
onSelectChat: (chatItem: ProjectConversation) => void;
|
||||
}) => {
|
||||
const [FLAGS] = useFlags();
|
||||
const [inBatch, setInBatch] = useState(false);
|
||||
const [batchSelected, setBatchSelected] = useState<
|
||||
Record<string, ProjectConversation | undefined>
|
||||
>({});
|
||||
const dynamicTopRef = useRef(null);
|
||||
const [inDynamicTopViewport] = useInViewport(dynamicTopRef);
|
||||
const batchSelectedList = useMemo(
|
||||
() =>
|
||||
Object.values(batchSelected).filter((i): i is ProjectConversation => !!i),
|
||||
[batchSelected],
|
||||
);
|
||||
const canBatchOperate =
|
||||
!!canEdit &&
|
||||
!!list?.length &&
|
||||
!inBatch &&
|
||||
// 社区版暂不支持该功能
|
||||
FLAGS['bot.automation.conversation_batch_delete'];
|
||||
const exitBatch = () => {
|
||||
setInBatch(false);
|
||||
setBatchSelected({});
|
||||
};
|
||||
const handleBatchSelectChange = (item: ProjectConversation) => {
|
||||
const key = item.unique_id || '';
|
||||
const next = batchSelected[key] ? undefined : item;
|
||||
setBatchSelected({
|
||||
...batchSelected,
|
||||
[item.unique_id || '']: next,
|
||||
});
|
||||
};
|
||||
const handleBatchDelete = async (items: ProjectConversation[]) => {
|
||||
const ids = items.map(i => i.unique_id).filter((i): i is string => !!i);
|
||||
await onBatchDelete(ids);
|
||||
// 删除成功后退出批量操作模式
|
||||
exitBatch();
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<TitleWithTooltip
|
||||
className={classNames(
|
||||
styles.title,
|
||||
!inDynamicTopViewport && styles['is-bottom'],
|
||||
)}
|
||||
title={I18n.t('project_conversation_list_dynamic_title')}
|
||||
tooltip={I18n.t('wf_chatflow_44')}
|
||||
extra={
|
||||
canBatchOperate && (
|
||||
<Tooltip
|
||||
content={I18n.t(
|
||||
'project_conversation_list_operate_batch_tooltip',
|
||||
)}
|
||||
>
|
||||
<IconButton
|
||||
icon={<IconCozListDisorder />}
|
||||
size="small"
|
||||
color="secondary"
|
||||
onClick={() => setInBatch(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
onClick={() =>
|
||||
scroller.scrollTo('dynamic', {
|
||||
duration: 200,
|
||||
smooth: true,
|
||||
containerId: 'conversation-list',
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div ref={dynamicTopRef} />
|
||||
<div
|
||||
className={classNames(
|
||||
styles['list-container'],
|
||||
inBatch && styles['in-batch'],
|
||||
)}
|
||||
>
|
||||
{list?.length ? (
|
||||
list.map(data => (
|
||||
<ChatItem
|
||||
chat={data}
|
||||
canEdit={canEdit}
|
||||
isActivate={data.unique_id === activateChat?.unique_id}
|
||||
isInBatch={inBatch}
|
||||
isInBatchSelected={!!batchSelected[data.unique_id || '']}
|
||||
onBatchSelectChange={handleBatchSelectChange}
|
||||
onActivate={onSelectChat}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className={styles['empty-container']}>
|
||||
<EmptyState
|
||||
size="default"
|
||||
icon={<IconCozEmpty />}
|
||||
title={I18n.t('wf_chatflow_41')}
|
||||
description={I18n.t('wf_chatflow_42')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{inBatch ? (
|
||||
<div className={styles['batch-wrap']}>
|
||||
<Popconfirm
|
||||
title={I18n.t('project_conversation_list_batch_delete_tooltip')}
|
||||
okText={I18n.t('delete_title')}
|
||||
cancelText={I18n.t('cancel')}
|
||||
content={I18n.t(
|
||||
'project_conversation_list_batch_delete_tooltip_context',
|
||||
{
|
||||
len: batchSelectedList.length,
|
||||
},
|
||||
)}
|
||||
onConfirm={() => {
|
||||
handleBatchDelete(batchSelectedList);
|
||||
}}
|
||||
>
|
||||
<Button size="small" disabled={!batchSelectedList.length}>
|
||||
{I18n.t('project_conversation_list_batch_delete_btn', {
|
||||
len: batchSelectedList.length,
|
||||
})}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<Popconfirm
|
||||
title={I18n.t('filebox_0040')}
|
||||
okText={I18n.t('filebox_0040')}
|
||||
cancelText={I18n.t('cancel')}
|
||||
content={I18n.t(
|
||||
'project_conversation_list_delete_all_tooltip_context',
|
||||
)}
|
||||
okButtonColor="red"
|
||||
onConfirm={() => {
|
||||
handleBatchDelete(list);
|
||||
}}
|
||||
>
|
||||
<Button size="small" color="redhglt">
|
||||
{I18n.t('url_add_008')}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
|
||||
<IconButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
icon={<IconCozCross />}
|
||||
onClick={exitBatch}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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 { useDeleteChat } from './use-delete-chat';
|
||||
export { useUpdateChat } from './use-update-chat';
|
||||
export { useCreateChat } from './use-create-chat';
|
||||
export { useConversationListWithConnector } from './use-conversation-list';
|
||||
export { useConnectorList } from './use-connector-list';
|
||||
export { useBatchDelete } from './use-batch-delete';
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 { I18n } from '@coze-arch/i18n';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
import {
|
||||
CreateEnv,
|
||||
type ProjectConversation,
|
||||
} from '@coze-arch/bot-api/workflow_api';
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
|
||||
interface UseBatchDeleteOptions {
|
||||
connectorId: string;
|
||||
createEnv: CreateEnv;
|
||||
manualRefresh: () => void;
|
||||
setActivateChat: (_chat: ProjectConversation | undefined) => void;
|
||||
}
|
||||
|
||||
export const useBatchDelete = (options: UseBatchDeleteOptions) => {
|
||||
const { spaceId, projectId } = useIDEGlobalStore(store => ({
|
||||
spaceId: store.spaceId,
|
||||
projectId: store.projectId,
|
||||
}));
|
||||
|
||||
const batchDelete = async (ids: string[]) => {
|
||||
const isDraft = options.createEnv === CreateEnv.Draft;
|
||||
const res = await workflowApi.BatchDeleteProjectConversation({
|
||||
space_id: spaceId,
|
||||
project_id: projectId,
|
||||
unique_id_list: ids,
|
||||
draft_mode: isDraft,
|
||||
connector_id: isDraft ? '' : options.connectorId,
|
||||
});
|
||||
if (res.Success) {
|
||||
Toast.success(I18n.t('wf_chatflow_112'));
|
||||
options.manualRefresh();
|
||||
options.setActivateChat(undefined);
|
||||
} else {
|
||||
Toast.error(I18n.t('wf_chatflow_151'));
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
batchDelete,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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, useMemo } from 'react';
|
||||
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { CreateEnv } from '@coze-arch/bot-api/workflow_api';
|
||||
import { intelligenceApi } from '@coze-arch/bot-api';
|
||||
import {
|
||||
useProjectId,
|
||||
useListenMessageEvent,
|
||||
CONVERSATION_URI,
|
||||
type MessageEvent,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
import {
|
||||
DEFAULT_CONNECTOR,
|
||||
DEBUG_CONNECTOR_ID,
|
||||
COZE_CONNECTOR_ID,
|
||||
COZE_CONNECTOR_IDS,
|
||||
ALLOW_CONNECTORS,
|
||||
} from '../constants';
|
||||
|
||||
interface Connector {
|
||||
connectorId: string;
|
||||
connectorName?: string;
|
||||
}
|
||||
|
||||
export const useConnectorList = () => {
|
||||
const projectId = useProjectId();
|
||||
|
||||
const [connectorList, setConnectorList] = useState<Connector[]>([
|
||||
DEFAULT_CONNECTOR,
|
||||
]);
|
||||
const [activeKey, setActiveKey] = useState(DEBUG_CONNECTOR_ID);
|
||||
|
||||
const createEnv = useMemo(() => {
|
||||
if (activeKey === DEBUG_CONNECTOR_ID) {
|
||||
return CreateEnv.Draft;
|
||||
}
|
||||
return CreateEnv.Release;
|
||||
}, [activeKey]);
|
||||
|
||||
const fetch = async () => {
|
||||
const res = await intelligenceApi.GetProjectPublishedConnector({
|
||||
project_id: projectId,
|
||||
});
|
||||
const data = res.data || [];
|
||||
let noCoze = true;
|
||||
const next = data
|
||||
.reduce((prev, current) => {
|
||||
if (!current.id) {
|
||||
return prev;
|
||||
}
|
||||
if (COZE_CONNECTOR_IDS.includes(current.id)) {
|
||||
if (noCoze) {
|
||||
prev.push({
|
||||
connectorId: COZE_CONNECTOR_ID,
|
||||
connectorName: I18n.t('platform_name'),
|
||||
});
|
||||
noCoze = false;
|
||||
}
|
||||
} else {
|
||||
prev.push({
|
||||
connectorId: current.id,
|
||||
connectorName: current.name,
|
||||
});
|
||||
}
|
||||
return prev;
|
||||
}, [] as Connector[])
|
||||
.filter(i => ALLOW_CONNECTORS.includes(i.connectorId));
|
||||
setConnectorList([DEFAULT_CONNECTOR, ...next]);
|
||||
};
|
||||
|
||||
const handleTabChange = (v: string) => {
|
||||
setActiveKey(v);
|
||||
};
|
||||
|
||||
const listener = useMemoizedFn((e: MessageEvent) => {
|
||||
if (e.name === 'tab' && e.data?.value === 'testrun') {
|
||||
setActiveKey(DEBUG_CONNECTOR_ID);
|
||||
}
|
||||
});
|
||||
|
||||
useListenMessageEvent(CONVERSATION_URI, listener);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
connectorList,
|
||||
activeKey,
|
||||
createEnv,
|
||||
onTabChange: handleTabChange,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 { useState } from 'react';
|
||||
|
||||
import {
|
||||
type ProjectConversation,
|
||||
type ListProjectConversationRequest,
|
||||
CreateMethod,
|
||||
CreateEnv,
|
||||
} from '@coze-arch/bot-api/workflow_api';
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
|
||||
export const MAX_LIMIT = 1000;
|
||||
|
||||
type ListProjectConversationDefParams = Pick<
|
||||
ListProjectConversationRequest,
|
||||
'create_env' | 'create_method'
|
||||
> & {
|
||||
connector_id: string;
|
||||
};
|
||||
|
||||
type ConversationListWithConnectorParams = Pick<
|
||||
ListProjectConversationDefParams,
|
||||
'connector_id' | 'create_env'
|
||||
>;
|
||||
|
||||
const useConversationList = (params: ListProjectConversationDefParams) => {
|
||||
const { spaceId, projectId, version } = useIDEGlobalStore(store => ({
|
||||
spaceId: store.spaceId,
|
||||
projectId: store.projectId,
|
||||
version: store.version,
|
||||
}));
|
||||
|
||||
const [list, setList] = useState<ProjectConversation[]>([]);
|
||||
|
||||
const fetch = async () => {
|
||||
const staticList = await workflowApi.ListProjectConversationDef({
|
||||
space_id: spaceId,
|
||||
project_id: projectId,
|
||||
project_version: version,
|
||||
create_method: CreateMethod.ManualCreate,
|
||||
create_env: CreateEnv.Release,
|
||||
limit: MAX_LIMIT,
|
||||
...params,
|
||||
});
|
||||
setList(staticList.data || []);
|
||||
};
|
||||
|
||||
return {
|
||||
list,
|
||||
fetch,
|
||||
};
|
||||
};
|
||||
|
||||
export const useConversationListWithConnector = (
|
||||
params: ConversationListWithConnectorParams,
|
||||
) => {
|
||||
// 静态
|
||||
const { list: staticList, fetch: fetchStatic } = useConversationList({
|
||||
create_method: CreateMethod.ManualCreate,
|
||||
...params,
|
||||
});
|
||||
// 动态
|
||||
const { list: dynamicList, fetch: fetchDynamic } = useConversationList({
|
||||
create_method: CreateMethod.NodeCreate,
|
||||
...params,
|
||||
});
|
||||
|
||||
const fetch = () => {
|
||||
fetchStatic();
|
||||
fetchDynamic();
|
||||
};
|
||||
|
||||
return {
|
||||
staticList,
|
||||
dynamicList,
|
||||
fetch,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 { useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
|
||||
export const useCreateChat = ({
|
||||
manualRefresh,
|
||||
}: {
|
||||
manualRefresh: () => void;
|
||||
}) => {
|
||||
const { spaceId, projectId } = useIDEGlobalStore(store => ({
|
||||
spaceId: store.spaceId,
|
||||
projectId: store.projectId,
|
||||
}));
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handleCreateChat = async (input: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const res = await workflowApi.CreateProjectConversationDef({
|
||||
space_id: spaceId,
|
||||
project_id: projectId,
|
||||
conversation_name: input,
|
||||
});
|
||||
if (res?.code === 0) {
|
||||
Toast.success(I18n.t('wf_chatflow_111'));
|
||||
manualRefresh();
|
||||
} else {
|
||||
Toast.error(I18n.t('wf_chatflow_112'));
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
return { loading, handleCreateChat };
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
.modal {
|
||||
:global {
|
||||
.semi-modal {
|
||||
.semi-modal-footer {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.semi-modal-content {
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.content-text {
|
||||
margin-top: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.rebind-chat {
|
||||
margin-top: 24px;
|
||||
|
||||
.rebind-title {
|
||||
margin-bottom: 4px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-primary);
|
||||
}
|
||||
|
||||
.rebind-icon {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.rebind-text {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.rebind-desc {
|
||||
margin-bottom: 18px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.rebind-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rebind-select {
|
||||
.option-text-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global {
|
||||
.option-text-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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 React, { useMemo, useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozChat } from '@coze-arch/coze-design/icons';
|
||||
import { Modal, Select, Typography, Toast } from '@coze-arch/coze-design';
|
||||
import {
|
||||
type Workflow,
|
||||
type ProjectConversation,
|
||||
} from '@coze-arch/bot-api/workflow_api';
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
|
||||
import { DEFAULT_UNIQUE_ID, DEFAULT_CONVERSATION_NAME } from '../../constants';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
// eslint-disable-next-line @coze-arch/max-line-per-function
|
||||
export const useDeleteChat = ({
|
||||
staticList,
|
||||
manualRefresh,
|
||||
setActivateChat,
|
||||
}: {
|
||||
staticList: ProjectConversation[];
|
||||
manualRefresh: () => void;
|
||||
setActivateChat: (_chat: ProjectConversation | undefined) => void;
|
||||
}) => {
|
||||
const { spaceId, projectId } = useIDEGlobalStore(store => ({
|
||||
spaceId: store.spaceId,
|
||||
projectId: store.projectId,
|
||||
}));
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||
const [replace, setReplace] = useState<Workflow[]>([]);
|
||||
const [chat, setChat] = useState<ProjectConversation | undefined>(undefined);
|
||||
// key:workflowId,value:conversationId
|
||||
const [rebindReplace, setRebindReplace] = useState<Record<string, string>>(
|
||||
{},
|
||||
);
|
||||
|
||||
const optionList = staticList
|
||||
.filter(item => item.unique_id !== chat?.unique_id)
|
||||
.map(item => ({
|
||||
label: (
|
||||
<Text style={{ width: '100%' }} ellipsis={{ showTooltip: true }}>
|
||||
{item.conversation_name}
|
||||
</Text>
|
||||
),
|
||||
value: item.unique_id,
|
||||
conversationId: item.conversation_id,
|
||||
}));
|
||||
|
||||
/**
|
||||
* 给外部的 check,用作 replace 请求
|
||||
*/
|
||||
const handleDelete = async (_chat?: ProjectConversation) => {
|
||||
setChat(_chat);
|
||||
const res = await workflowApi.DeleteProjectConversationDef({
|
||||
space_id: spaceId,
|
||||
project_id: projectId,
|
||||
check_only: true,
|
||||
unique_id: _chat?.unique_id || '',
|
||||
});
|
||||
|
||||
if (res.need_replace) {
|
||||
setReplace(res.need_replace);
|
||||
const rebindInit = {};
|
||||
res.need_replace.forEach(_replace => {
|
||||
if (_replace.workflow_id) {
|
||||
rebindInit[_replace.workflow_id] = DEFAULT_CONVERSATION_NAME;
|
||||
}
|
||||
});
|
||||
setRebindReplace(rebindInit);
|
||||
} else {
|
||||
setReplace([]);
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const handleModalOk = async () => {
|
||||
setDeleteLoading(true);
|
||||
try {
|
||||
const res = await workflowApi.DeleteProjectConversationDef({
|
||||
space_id: spaceId,
|
||||
project_id: projectId,
|
||||
unique_id: chat?.unique_id || '',
|
||||
replace: rebindReplace,
|
||||
});
|
||||
|
||||
if (res.success) {
|
||||
setReplace([]);
|
||||
setVisible(false);
|
||||
Toast.success(I18n.t('wf_chatflow_112'));
|
||||
// 删除成功后刷新列表
|
||||
manualRefresh();
|
||||
setActivateChat(undefined);
|
||||
} else {
|
||||
Toast.error(I18n.t('wf_chatflow_151'));
|
||||
}
|
||||
} finally {
|
||||
setDeleteLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectChange = (
|
||||
workflowId?: string,
|
||||
conversationName?: string,
|
||||
) => {
|
||||
if (workflowId && conversationName) {
|
||||
const newBind = {
|
||||
...rebindReplace,
|
||||
[workflowId]: conversationName,
|
||||
};
|
||||
setRebindReplace(newBind);
|
||||
}
|
||||
};
|
||||
|
||||
const modalDom = useMemo(() => {
|
||||
const dom = (
|
||||
<div className={s['rebind-chat']}>
|
||||
<div className={s['rebind-title']}>{I18n.t('wf_chatflow_53')}</div>
|
||||
<div className={s['rebind-desc']}>{I18n.t('wf_chatflow_54')}</div>
|
||||
{replace.map(item => {
|
||||
const { name } = item;
|
||||
return (
|
||||
<div className={s['rebind-item']}>
|
||||
<IconCozChat className={s['rebind-icon']} />
|
||||
<Text
|
||||
ellipsis={{ showTooltip: true }}
|
||||
className={s['rebind-text']}
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
<Select
|
||||
dropdownClassName={s['rebind-select']}
|
||||
style={{ width: '50%' }}
|
||||
dropdownStyle={{ width: 220 }}
|
||||
size="small"
|
||||
defaultValue={DEFAULT_UNIQUE_ID}
|
||||
optionList={optionList}
|
||||
onChange={value => {
|
||||
const selectItem = staticList.find(
|
||||
option => option.unique_id === value,
|
||||
);
|
||||
handleSelectChange(
|
||||
item.workflow_id,
|
||||
selectItem?.conversation_name,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
width={480}
|
||||
type="dialog"
|
||||
title={I18n.t('wf_chatflow_51')}
|
||||
className={s.modal}
|
||||
okText={I18n.t('wf_chatflow_55')}
|
||||
cancelText={I18n.t('wf_chatflow_56')}
|
||||
onCancel={() => setVisible(false)}
|
||||
okButtonColor="red"
|
||||
okButtonProps={{
|
||||
loading: deleteLoading,
|
||||
}}
|
||||
onOk={handleModalOk}
|
||||
>
|
||||
<div className={s['content-container']}>
|
||||
<div className={s['content-text']}>{I18n.t('wf_chatflow_52')}</div>
|
||||
{replace?.length ? dom : null}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}, [chat, replace, visible, optionList]);
|
||||
|
||||
return {
|
||||
handleDelete,
|
||||
modalDom,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { useState } from 'react';
|
||||
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
|
||||
export const useUpdateChat = ({
|
||||
manualRefresh,
|
||||
}: {
|
||||
manualRefresh: () => void;
|
||||
}) => {
|
||||
const { spaceId, projectId } = useIDEGlobalStore(store => ({
|
||||
spaceId: store.spaceId,
|
||||
projectId: store.projectId,
|
||||
}));
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handleUpdateChat = async (
|
||||
uniqueId: string,
|
||||
conversationName: string,
|
||||
) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
await workflowApi.UpdateProjectConversationDef({
|
||||
space_id: spaceId,
|
||||
project_id: projectId,
|
||||
unique_id: uniqueId,
|
||||
conversation_name: conversationName,
|
||||
});
|
||||
manualRefresh();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { loading, handleUpdateChat };
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
.connector-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 54px;
|
||||
padding: 0 12px;
|
||||
|
||||
background: var(--coz-bg-plus);
|
||||
border-bottom: 1px solid var(--coz-stroke-primary);
|
||||
}
|
||||
@@ -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 React, { useEffect } from 'react';
|
||||
|
||||
import { TabBar } from '@coze-arch/coze-design';
|
||||
import { useProjectRole } from '@coze-common/auth';
|
||||
import {
|
||||
useProjectId,
|
||||
useCommitVersion,
|
||||
useCurrentWidgetContext,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
import { useConnectorList } from './hooks';
|
||||
import { ConversationContent } from './conversation-content';
|
||||
import { DEBUG_CONNECTOR_ID, COZE_CONNECTOR_ID } from './constants';
|
||||
|
||||
import css from './main.module.less';
|
||||
|
||||
const Conversation = () => {
|
||||
const projectId = useProjectId();
|
||||
const { version: commitVersion } = useCommitVersion();
|
||||
const { widget: uiWidget } = useCurrentWidgetContext();
|
||||
|
||||
const { connectorList, activeKey, createEnv, onTabChange } =
|
||||
useConnectorList();
|
||||
|
||||
const projectRoles = useProjectRole(projectId);
|
||||
const readonly = !projectRoles?.length || !!commitVersion;
|
||||
|
||||
useEffect(() => {
|
||||
uiWidget.setUIState('normal');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabBar
|
||||
type="text"
|
||||
mode="select"
|
||||
className={css['connector-tab']}
|
||||
activeKey={activeKey}
|
||||
onTabClick={onTabChange}
|
||||
>
|
||||
{connectorList.map(connector => (
|
||||
<TabBar.TabPanel
|
||||
tab={connector.connectorName}
|
||||
itemKey={connector.connectorId}
|
||||
/>
|
||||
))}
|
||||
</TabBar>
|
||||
<ConversationContent
|
||||
canEdit={!readonly}
|
||||
connectorId={
|
||||
activeKey === DEBUG_CONNECTOR_ID ? COZE_CONNECTOR_ID : activeKey
|
||||
}
|
||||
createEnv={createEnv}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Conversation;
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozChatSetting } from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
LayoutPanelType,
|
||||
withLazyLoad,
|
||||
type WidgetRegistry,
|
||||
type WidgetContext,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
export const ConversationRegistry: WidgetRegistry = {
|
||||
match: /(\/session.*|\/conversation.*)/,
|
||||
area: LayoutPanelType.MAIN_PANEL,
|
||||
load: (ctx: WidgetContext) =>
|
||||
Promise.resolve().then(() => {
|
||||
ctx.widget.setTitle(I18n.t('wf_chatflow_101'));
|
||||
ctx.widget.setUIState('normal');
|
||||
}),
|
||||
renderContent() {
|
||||
const Component = withLazyLoad(() => import('./main'));
|
||||
return <Component />;
|
||||
},
|
||||
renderIcon() {
|
||||
return <IconCozChatSetting />;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
.list-container {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
top: 0;
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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 { scroller } from 'react-scroll';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozEdit,
|
||||
IconCozPlus,
|
||||
IconCozTrashCan,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import { IconButton, Typography } from '@coze-arch/coze-design';
|
||||
import { type ProjectConversation } from '@coze-arch/bot-api/workflow_api';
|
||||
|
||||
import { TitleWithTooltip } from '../title-with-tooltip';
|
||||
import commonStyles from '../conversation-content/index.module.less';
|
||||
import { EditInput } from '../conversation-content/edit-input';
|
||||
import { DEFAULT_UNIQUE_ID, type ErrorCode } from '../constants';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export const StaticChatList = ({
|
||||
canEdit,
|
||||
list,
|
||||
activateChat,
|
||||
updateLoading,
|
||||
onUpdate,
|
||||
onDelete,
|
||||
onValidate,
|
||||
onSelectChat,
|
||||
renderCreateInput,
|
||||
handleCreateInput,
|
||||
}: {
|
||||
canEdit?: boolean;
|
||||
list: ProjectConversation[];
|
||||
activateChat?: ProjectConversation;
|
||||
updateLoading: boolean;
|
||||
onUpdate: (uniqueId: string, conversationName: string) => void;
|
||||
onDelete: (chatItem: ProjectConversation) => Promise<void>;
|
||||
onValidate: (_input: string) => ErrorCode | undefined;
|
||||
onSelectChat: (chatItem: ProjectConversation) => void;
|
||||
renderCreateInput: () => React.ReactNode;
|
||||
handleCreateInput?: () => void;
|
||||
}) => {
|
||||
// 存储 session_id
|
||||
const [editingUniqueId, setEditingUniqueId] = useState('');
|
||||
|
||||
const handleEditSession = (inputStr?: string, error?: ErrorCode) => {
|
||||
if (!error) {
|
||||
onUpdate(editingUniqueId, inputStr || '');
|
||||
}
|
||||
setEditingUniqueId('');
|
||||
};
|
||||
|
||||
const handleSessionVisible = (_uniqueId?: string) => {
|
||||
setEditingUniqueId(_uniqueId || '');
|
||||
};
|
||||
|
||||
/**
|
||||
* ux @wangwenbo.me 设计,default 放在首位,
|
||||
* 剩余的接口返回按照创建先后顺序倒序排序(后创建的放前边)
|
||||
*/
|
||||
return (
|
||||
<>
|
||||
<TitleWithTooltip
|
||||
className={s.title}
|
||||
title={I18n.t('project_conversation_list_static_title')}
|
||||
tooltip={I18n.t('wf_chatflow_104')}
|
||||
extra={
|
||||
canEdit && (
|
||||
<IconButton
|
||||
icon={<IconCozPlus />}
|
||||
color="highlight"
|
||||
size="small"
|
||||
onClick={handleCreateInput}
|
||||
/>
|
||||
)
|
||||
}
|
||||
onClick={() =>
|
||||
scroller.scrollTo('static', {
|
||||
duration: 200,
|
||||
smooth: true,
|
||||
containerId: 'conversation-list',
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div className={s['list-container']}>
|
||||
<div
|
||||
className={classNames(
|
||||
commonStyles['chat-item'],
|
||||
activateChat?.unique_id === list[0]?.unique_id &&
|
||||
commonStyles['chat-item-activate'],
|
||||
)}
|
||||
key={list[0]?.unique_id}
|
||||
onClick={() => onSelectChat(list[0])}
|
||||
>
|
||||
<Text ellipsis={{ showTooltip: true }}>
|
||||
{list[0]?.conversation_name}
|
||||
</Text>
|
||||
</div>
|
||||
{renderCreateInput()}
|
||||
{list.slice(1).map(item => (
|
||||
<div
|
||||
className={classNames(
|
||||
commonStyles['chat-item'],
|
||||
activateChat?.unique_id === item.unique_id &&
|
||||
commonStyles['chat-item-activate'],
|
||||
editingUniqueId === item.unique_id &&
|
||||
commonStyles['chat-item-editing'],
|
||||
)}
|
||||
key={item.unique_id}
|
||||
onClick={() => onSelectChat(item)}
|
||||
>
|
||||
{editingUniqueId === item.unique_id ? (
|
||||
<EditInput
|
||||
loading={updateLoading}
|
||||
defaultValue={item.conversation_name}
|
||||
onBlur={handleEditSession}
|
||||
onValidate={onValidate}
|
||||
/>
|
||||
) : (
|
||||
<Text ellipsis={{ showTooltip: true }}>
|
||||
{item.conversation_name}
|
||||
</Text>
|
||||
)}
|
||||
{editingUniqueId === item.unique_id ||
|
||||
item.unique_id === DEFAULT_UNIQUE_ID ||
|
||||
!canEdit ? null : (
|
||||
<div className={commonStyles.icons}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
icon={<IconCozEdit />}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
handleSessionVisible(item.unique_id);
|
||||
}}
|
||||
/>
|
||||
{/* 默认会话不可删除 */}
|
||||
<IconButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
icon={<IconCozTrashCan />}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onDelete(item);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
.title-container {
|
||||
cursor: pointer;
|
||||
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
height: 28px;
|
||||
margin-bottom: 2px;
|
||||
padding: 0 8px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-secondary);
|
||||
|
||||
background-color: var(--coz-bg-plus);
|
||||
|
||||
.title-with-tip {
|
||||
display: flex;
|
||||
column-gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.extra {
|
||||
display: flex;
|
||||
column-gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { IconCozInfoCircle } from '@coze-arch/coze-design/icons';
|
||||
import { Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
interface TitleWithTooltipProps {
|
||||
title: React.ReactNode;
|
||||
tooltip?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export const TitleWithTooltip: React.FC<TitleWithTooltipProps> = ({
|
||||
title,
|
||||
tooltip,
|
||||
extra,
|
||||
className,
|
||||
onClick,
|
||||
}) => (
|
||||
<div className={cls(s['title-container'], className)} onClick={onClick}>
|
||||
<div className={s['title-with-tip']}>
|
||||
{title}
|
||||
<Tooltip content={tooltip}>
|
||||
<IconCozInfoCircle />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className={s.extra} onClick={e => e.stopPropagation()}>
|
||||
{extra}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { ProjectResourceActionKey } from '@coze-arch/bot-api/plugin_develop';
|
||||
|
||||
export const workflowActions = [
|
||||
{
|
||||
key: ProjectResourceActionKey.Rename,
|
||||
enable: true,
|
||||
},
|
||||
{
|
||||
key: ProjectResourceActionKey.Copy,
|
||||
enable: true,
|
||||
},
|
||||
{
|
||||
key: ProjectResourceActionKey.MoveToLibrary,
|
||||
enable: false,
|
||||
hint: '不能移动到资源库',
|
||||
},
|
||||
{
|
||||
key: ProjectResourceActionKey.CopyToLibrary,
|
||||
enable: true,
|
||||
hint: '复制到资源库',
|
||||
},
|
||||
{
|
||||
// 切换为 chatflow
|
||||
key: ProjectResourceActionKey.SwitchToChatflow,
|
||||
enable: true,
|
||||
},
|
||||
{
|
||||
key: ProjectResourceActionKey.Delete,
|
||||
enable: true,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 { useProjectApi } from './use-project-api';
|
||||
export { useListenWFMessageEvent } from './use-listen-message-event';
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 {
|
||||
useIDENavigate,
|
||||
getURIByResource,
|
||||
useProjectIDEServices,
|
||||
} from '@coze-project-ide/framework';
|
||||
import { usePrimarySidebarStore } from '@coze-project-ide/biz-components';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
export const useChangeFlowMode = () => {
|
||||
const refetch = usePrimarySidebarStore(state => state.refetch);
|
||||
const navigate = useIDENavigate();
|
||||
const { view } = useProjectIDEServices();
|
||||
|
||||
return async (
|
||||
flowMode: WorkflowMode,
|
||||
workflowId: string,
|
||||
spaceId: string,
|
||||
) => {
|
||||
await workflowApi.UpdateWorkflowMeta({
|
||||
workflow_id: workflowId,
|
||||
space_id: spaceId,
|
||||
flow_mode: flowMode,
|
||||
});
|
||||
Toast.success(
|
||||
I18n.t('wf_chatflow_123', {
|
||||
Chatflow: I18n.t(
|
||||
flowMode === WorkflowMode.ChatFlow ? 'wf_chatflow_76' : 'Workflow',
|
||||
),
|
||||
}),
|
||||
);
|
||||
await refetch();
|
||||
const uri = getURIByResource('workflow', workflowId);
|
||||
const widgetContext = view.getWidgetContextFromURI(uri);
|
||||
const widgetOpened = Boolean(widgetContext?.widget);
|
||||
// 已经打开的 widget,加 refresh 参数刷新,未打开的直接打开会刷新
|
||||
navigate(`/workflow/${workflowId}${widgetOpened ? '?refresh=true' : ''}`);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 {
|
||||
useWorkflowModal,
|
||||
WorkflowModalFrom,
|
||||
type WorkFlowModalModeProps,
|
||||
WorkflowCategory,
|
||||
} from '@coze-workflow/components';
|
||||
import {
|
||||
BizResourceTypeEnum,
|
||||
useOpenResource,
|
||||
usePrimarySidebarStore,
|
||||
useResourceCopyDispatch,
|
||||
} from '@coze-project-ide/biz-components';
|
||||
import { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
|
||||
import { resource_resource_common } from '@coze-arch/bot-api/plugin_develop';
|
||||
|
||||
import { useNameValidators } from './use-name-validators';
|
||||
|
||||
export const useImportLibraryWorkflow = ({
|
||||
projectId,
|
||||
}: {
|
||||
projectId: string;
|
||||
}): {
|
||||
modal: ReactNode;
|
||||
importLibrary: () => void;
|
||||
} => {
|
||||
const refetch = usePrimarySidebarStore(state => state.refetch);
|
||||
const openResource = useOpenResource();
|
||||
const importResource = useResourceCopyDispatch();
|
||||
const onImport: WorkFlowModalModeProps['onImport'] = async item => {
|
||||
try {
|
||||
close();
|
||||
console.log('[ResourceFolder]import library workflow>>>', item);
|
||||
await importResource({
|
||||
scene:
|
||||
resource_resource_common.ResourceCopyScene.CopyResourceFromLibrary,
|
||||
res_id: item.workflow_id,
|
||||
res_type: resource_resource_common.ResType.Workflow,
|
||||
project_id: projectId,
|
||||
res_name: item.name,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('[ResourceFolder]import library workflow error>>>', e);
|
||||
}
|
||||
};
|
||||
const nameValidators = useNameValidators();
|
||||
const { node, open, close } = useWorkflowModal({
|
||||
from: WorkflowModalFrom.ProjectImportLibrary,
|
||||
flowMode: WorkflowMode.Workflow,
|
||||
hiddenExplore: true,
|
||||
hiddenCreate: true,
|
||||
hiddenWorkflowCategories: [
|
||||
WorkflowCategory.Example,
|
||||
WorkflowCategory.Project,
|
||||
],
|
||||
projectId,
|
||||
onImport,
|
||||
nameValidators,
|
||||
onCreateSuccess: async ({ workflowId }) => {
|
||||
close();
|
||||
await refetch();
|
||||
openResource({
|
||||
resourceType: BizResourceTypeEnum.Workflow,
|
||||
resourceId: workflowId,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return { modal: node, importLibrary: open };
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 RefObject } from 'react';
|
||||
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { type WorkflowPlaygroundRef } from '@coze-workflow/playground';
|
||||
import {
|
||||
useListenMessageEvent,
|
||||
type URI,
|
||||
type MessageEvent,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
export const useListenWFMessageEvent = (
|
||||
uri: URI,
|
||||
ref: RefObject<WorkflowPlaygroundRef>,
|
||||
) => {
|
||||
const listener = useMemoizedFn((e: MessageEvent) => {
|
||||
if (e.name === 'process' && e.data?.executeId && ref.current) {
|
||||
ref.current.getProcess({ executeId: e.data.executeId });
|
||||
} else if (e.name === 'debug' && ref.current) {
|
||||
const { nodeId, executeId, subExecuteId } = e?.data || {};
|
||||
if (nodeId) {
|
||||
setTimeout(() => {
|
||||
ref.current?.scrollToNode(nodeId);
|
||||
}, 1000);
|
||||
}
|
||||
if (executeId) {
|
||||
ref.current.showTestRunResult(executeId, subExecuteId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useListenMessageEvent(uri, listener);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 RefObject } from 'react';
|
||||
|
||||
import {
|
||||
useResourceList,
|
||||
type BizResourceType,
|
||||
} from '@coze-project-ide/biz-components';
|
||||
import { REPORT_EVENTS } from '@coze-arch/report-events';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { CustomError } from '@coze-arch/bot-error';
|
||||
export const useNameValidators = ({
|
||||
currentResourceRef,
|
||||
}: {
|
||||
currentResourceRef?: RefObject<BizResourceType | undefined>;
|
||||
} = {}): Array<{
|
||||
validator: (rules: unknown[], value: string) => boolean | Error;
|
||||
}> => {
|
||||
const { workflowResource } = useResourceList();
|
||||
return [
|
||||
{
|
||||
validator(_, value) {
|
||||
// 过滤掉当前资源
|
||||
const otherResource = currentResourceRef?.current
|
||||
? workflowResource.filter(
|
||||
r => r.res_id !== currentResourceRef?.current?.res_id,
|
||||
)
|
||||
: workflowResource;
|
||||
if (otherResource.map(item => item.name).includes(value)) {
|
||||
return new CustomError(
|
||||
REPORT_EVENTS.formValidation,
|
||||
I18n.t('project_resource_sidebar_warning_label_exists', {
|
||||
label: value,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { useMemoizedFn } from 'ahooks';
|
||||
import { type ProjectApi } from '@coze-workflow/playground';
|
||||
import {
|
||||
useSendMessageEvent,
|
||||
useIDENavigate,
|
||||
useCurrentWidgetContext,
|
||||
useIDEGlobalContext,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
/**
|
||||
* 注入到 workflow 内 project api 的能力。
|
||||
* 注:非响应式
|
||||
*/
|
||||
export const useProjectApi = () => {
|
||||
const { sendOpen } = useSendMessageEvent();
|
||||
const { widget: uiWidget } = useCurrentWidgetContext();
|
||||
const navigate = useIDENavigate();
|
||||
const ideGlobalContext = useIDEGlobalContext();
|
||||
|
||||
const getProjectAPI = useMemoizedFn(() => {
|
||||
const api: ProjectApi = {
|
||||
navigate,
|
||||
ideGlobalStore: ideGlobalContext,
|
||||
setWidgetUIState: (status: string) => uiWidget.setUIState(status as any),
|
||||
sendMsgOpenWidget: sendOpen,
|
||||
};
|
||||
return api;
|
||||
});
|
||||
|
||||
return getProjectAPI;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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, type RefObject } from 'react';
|
||||
|
||||
import { type WorkflowPlaygroundRef } from '@coze-workflow/playground';
|
||||
import {
|
||||
useIDEParams,
|
||||
useIDENavigate,
|
||||
useCurrentWidget,
|
||||
getURLByURI,
|
||||
type ProjectIDEWidget,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
export const useRefresh = (ref: RefObject<WorkflowPlaygroundRef>) => {
|
||||
const widget = useCurrentWidget<ProjectIDEWidget>();
|
||||
const params = useIDEParams();
|
||||
const navigate = useIDENavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (params.refresh) {
|
||||
ref.current?.reload();
|
||||
navigate(getURLByURI(widget.uri!.removeQueryObject('refresh')), {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
}, [params.refresh, ref, widget, navigate]);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 BizResourceType,
|
||||
useResourceCopyDispatch,
|
||||
} from '@coze-project-ide/biz-components';
|
||||
import { resource_resource_common } from '@coze-arch/bot-api/plugin_develop';
|
||||
|
||||
export interface ResourceOperationProps {
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
export const useResourceOperation = ({ projectId }: ResourceOperationProps) => {
|
||||
const copyDispatch = useResourceCopyDispatch();
|
||||
return async ({
|
||||
scene,
|
||||
resource,
|
||||
}: {
|
||||
scene: resource_resource_common.ResourceCopyScene;
|
||||
resource?: BizResourceType;
|
||||
}) => {
|
||||
try {
|
||||
console.log(
|
||||
`[ResourceFolder]workflow resource copy dispatch, scene ${scene}>>>`,
|
||||
resource,
|
||||
);
|
||||
await copyDispatch({
|
||||
scene,
|
||||
res_id: resource?.id,
|
||||
res_type: resource_resource_common.ResType.Workflow,
|
||||
project_id: projectId,
|
||||
res_name: resource?.name || '',
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`[ResourceFolder]workflow resource copy dispatch, scene ${scene} error>>>`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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 React, {
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
type RefObject,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
useCreateWorkflowModal,
|
||||
WorkflowModalFrom,
|
||||
} from '@coze-workflow/components';
|
||||
import {
|
||||
type ResourceFolderProps,
|
||||
type ResourceType,
|
||||
useProjectId,
|
||||
useSpaceId,
|
||||
} from '@coze-project-ide/framework';
|
||||
import {
|
||||
BizResourceContextMenuBtnType,
|
||||
type BizResourceType,
|
||||
BizResourceTypeEnum,
|
||||
type ResourceFolderCozeProps,
|
||||
useOpenResource,
|
||||
usePrimarySidebarStore,
|
||||
} from '@coze-project-ide/biz-components';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
|
||||
import { ResourceCopyScene } from '@coze-arch/bot-api/plugin_develop';
|
||||
import { workflowApi } from '@coze-arch/bot-api';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
|
||||
import { WORKFLOW_SUB_TYPE_ICON_MAP } from '@/constants';
|
||||
import { WorkflowTooltip } from '@/components';
|
||||
|
||||
import { useResourceOperation } from './use-resource-operation';
|
||||
import { useNameValidators } from './use-name-validators';
|
||||
import { useImportLibraryWorkflow } from './use-import-library-workflow';
|
||||
import { useChangeFlowMode } from './use-change-flow-mode';
|
||||
type UseWorkflowResourceReturn = Pick<
|
||||
ResourceFolderCozeProps,
|
||||
| 'onCustomCreate'
|
||||
| 'onDelete'
|
||||
| 'onChangeName'
|
||||
| 'onAction'
|
||||
| 'createResourceConfig'
|
||||
| 'iconRender'
|
||||
> & { modals: ReactNode };
|
||||
|
||||
// eslint-disable-next-line @coze-arch/max-line-per-function
|
||||
export const useWorkflowResource = (): UseWorkflowResourceReturn => {
|
||||
const refetch = usePrimarySidebarStore(state => state.refetch);
|
||||
const spaceId = useSpaceId();
|
||||
const projectId = useProjectId();
|
||||
const openResource = useOpenResource();
|
||||
const currentResourceRef = useRef<BizResourceType>();
|
||||
const nameValidators = useNameValidators({
|
||||
currentResourceRef: currentResourceRef as RefObject<
|
||||
BizResourceType | undefined
|
||||
>,
|
||||
});
|
||||
const {
|
||||
createWorkflowModal,
|
||||
workflowModal: templateWorkflowModal,
|
||||
openCreateModal,
|
||||
handleEditWorkflow,
|
||||
} = useCreateWorkflowModal({
|
||||
from: WorkflowModalFrom.ProjectAddWorkflowResource,
|
||||
spaceId,
|
||||
projectId,
|
||||
hiddenTemplateEntry: true,
|
||||
nameValidators,
|
||||
refreshPage: () => {
|
||||
currentResourceRef.current = undefined;
|
||||
refetch?.();
|
||||
},
|
||||
onCreateSuccess: async ({ workflowId }) => {
|
||||
await refetch?.();
|
||||
openResource({
|
||||
resourceType: BizResourceTypeEnum.Workflow,
|
||||
resourceId: workflowId,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const onCustomCreate: ResourceFolderCozeProps['onCustomCreate'] = (
|
||||
resourceType,
|
||||
subType,
|
||||
) => {
|
||||
console.log('[ResourceFolder]on custom create>>>', resourceType, subType);
|
||||
openCreateModal(subType as WorkflowMode);
|
||||
};
|
||||
|
||||
const onChangeName: ResourceFolderProps['onChangeName'] = useCallback(
|
||||
async changeNameEvent => {
|
||||
try {
|
||||
console.log('[ResourceFolder]on change name>>>', changeNameEvent);
|
||||
const resp = await workflowApi.UpdateWorkflowMeta({
|
||||
space_id: spaceId,
|
||||
workflow_id: changeNameEvent.id,
|
||||
name: changeNameEvent.name,
|
||||
});
|
||||
console.log('[ResourceFolder]rename workflow response>>>', resp);
|
||||
} catch (e) {
|
||||
console.log('[ResourceFolder]rename workflow error>>>', e);
|
||||
} finally {
|
||||
refetch();
|
||||
}
|
||||
},
|
||||
[refetch, spaceId],
|
||||
);
|
||||
|
||||
const updateDesc = useCallback(
|
||||
async (resource?: BizResourceType) => {
|
||||
if (!resource?.res_id) {
|
||||
return;
|
||||
}
|
||||
currentResourceRef.current = resource;
|
||||
const resp = await workflowApi.GetWorkflowDetail({
|
||||
space_id: spaceId,
|
||||
workflow_ids: [resource?.res_id],
|
||||
});
|
||||
const workflowInfo = resp?.data?.[0];
|
||||
if (!workflowInfo) {
|
||||
return;
|
||||
}
|
||||
handleEditWorkflow({
|
||||
space_id: workflowInfo.space_id,
|
||||
workflow_id: workflowInfo.workflow_id,
|
||||
url: workflowInfo.icon,
|
||||
icon_uri: workflowInfo.icon_uri,
|
||||
name: workflowInfo.name,
|
||||
desc: workflowInfo.desc,
|
||||
});
|
||||
},
|
||||
[spaceId, handleEditWorkflow],
|
||||
);
|
||||
|
||||
const onDelete = useCallback(
|
||||
async (resources: ResourceType[]) => {
|
||||
try {
|
||||
console.log('[ResourceFolder]on delete>>>', resources);
|
||||
console.log('delete start>>>', Date.now());
|
||||
const resp = await workflowApi.BatchDeleteWorkflow({
|
||||
space_id: spaceId,
|
||||
workflow_id_list: resources
|
||||
.filter(r => r.type === BizResourceTypeEnum.Workflow)
|
||||
.map(r => r.id),
|
||||
});
|
||||
console.log('delete end>>>', Date.now());
|
||||
Toast.success(I18n.t('Delete_success'));
|
||||
refetch().then(() => console.log('refetch end>>>', Date.now()));
|
||||
console.log('[ResourceFolder]delete workflow response>>>', resp);
|
||||
} catch (e) {
|
||||
console.log('[ResourceFolder]delete workflow error>>>', e);
|
||||
Toast.error(I18n.t('Delete_failed'));
|
||||
}
|
||||
},
|
||||
[refetch, spaceId],
|
||||
);
|
||||
|
||||
const { modal: workflowModal, importLibrary } = useImportLibraryWorkflow({
|
||||
projectId,
|
||||
});
|
||||
const changeFlowMode = useChangeFlowMode();
|
||||
const resourceOperation = useResourceOperation({ projectId });
|
||||
const onAction = (
|
||||
action: BizResourceContextMenuBtnType,
|
||||
resource?: BizResourceType,
|
||||
) => {
|
||||
switch (action) {
|
||||
case BizResourceContextMenuBtnType.ImportLibraryResource:
|
||||
return importLibrary();
|
||||
case BizResourceContextMenuBtnType.DuplicateResource:
|
||||
return resourceOperation({
|
||||
scene: ResourceCopyScene.CopyProjectResource,
|
||||
resource,
|
||||
});
|
||||
case BizResourceContextMenuBtnType.MoveToLibrary:
|
||||
return resourceOperation({
|
||||
scene: ResourceCopyScene.MoveResourceToLibrary,
|
||||
resource,
|
||||
});
|
||||
case BizResourceContextMenuBtnType.CopyToLibrary:
|
||||
return resourceOperation({
|
||||
scene: ResourceCopyScene.CopyResourceToLibrary,
|
||||
resource,
|
||||
});
|
||||
case BizResourceContextMenuBtnType.UpdateDesc:
|
||||
return updateDesc(resource);
|
||||
case BizResourceContextMenuBtnType.SwitchToChatflow:
|
||||
return changeFlowMode(
|
||||
WorkflowMode.ChatFlow,
|
||||
resource?.res_id ?? '',
|
||||
spaceId,
|
||||
);
|
||||
case BizResourceContextMenuBtnType.SwitchToWorkflow:
|
||||
return changeFlowMode(
|
||||
WorkflowMode.Workflow,
|
||||
resource?.res_id ?? '',
|
||||
spaceId,
|
||||
);
|
||||
default:
|
||||
console.warn('[WorkflowResource]unsupported action>>>', action);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const createResourceConfig = useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
icon: WORKFLOW_SUB_TYPE_ICON_MAP[WorkflowMode.Workflow],
|
||||
label: I18n.t('project_resource_sidebar_create_new_resource', {
|
||||
resource: I18n.t('library_resource_type_workflow'),
|
||||
}),
|
||||
subType: WorkflowMode.Workflow,
|
||||
tooltip: <WorkflowTooltip flowMode={WorkflowMode.Workflow} />,
|
||||
},
|
||||
// 社区版本暂不支持对话流
|
||||
IS_OPEN_SOURCE
|
||||
? null
|
||||
: {
|
||||
icon: WORKFLOW_SUB_TYPE_ICON_MAP[WorkflowMode.ChatFlow],
|
||||
label: I18n.t('project_resource_sidebar_create_new_resource', {
|
||||
resource: I18n.t('wf_chatflow_76'),
|
||||
}),
|
||||
subType: WorkflowMode.ChatFlow,
|
||||
tooltip: <WorkflowTooltip flowMode={WorkflowMode.ChatFlow} />,
|
||||
},
|
||||
].filter(Boolean) as ResourceFolderCozeProps['createResourceConfig'],
|
||||
[],
|
||||
);
|
||||
|
||||
const iconRender: ResourceFolderCozeProps['iconRender'] = useMemo(
|
||||
() =>
|
||||
({ resource }) => (
|
||||
<>
|
||||
{
|
||||
WORKFLOW_SUB_TYPE_ICON_MAP[
|
||||
resource.res_sub_type || WorkflowMode.Workflow
|
||||
]
|
||||
}
|
||||
</>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
return {
|
||||
onChangeName,
|
||||
onAction,
|
||||
onDelete,
|
||||
onCustomCreate,
|
||||
createResourceConfig,
|
||||
iconRender,
|
||||
modals: [workflowModal, createWorkflowModal, templateWorkflowModal],
|
||||
};
|
||||
};
|
||||
31
frontend/packages/project-ide/biz-workflow/src/index.tsx
Normal file
31
frontend/packages/project-ide/biz-workflow/src/index.tsx
Normal 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 {
|
||||
ResourceRefTooltip,
|
||||
usePluginDetail,
|
||||
LinkNode,
|
||||
navigateResource,
|
||||
} from '@coze-workflow/playground';
|
||||
|
||||
export { WorkflowWidgetRegistry } from './registry';
|
||||
/**
|
||||
* 会话管理 registry
|
||||
*/
|
||||
export { ConversationRegistry } from './conversation/registry';
|
||||
export { useWorkflowResource } from './hooks/use-workflow-resource';
|
||||
export { WorkflowTooltip, WorkflowWidgetIcon } from './components';
|
||||
export { WORKFLOW_SUB_TYPE_ICON_MAP } from './constants';
|
||||
124
frontend/packages/project-ide/biz-workflow/src/main.tsx
Normal file
124
frontend/packages/project-ide/biz-workflow/src/main.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
import {
|
||||
WorkflowPlayground,
|
||||
type WorkflowGlobalStateEntity,
|
||||
type WorkflowPlaygroundRef,
|
||||
} from '@coze-workflow/playground';
|
||||
import type { WsMessageProps } from '@coze-project-ide/framework/src/types';
|
||||
import {
|
||||
useSpaceId,
|
||||
useProjectId,
|
||||
useCommitVersion,
|
||||
useCurrentWidgetContext,
|
||||
useCurrentWidget,
|
||||
type ProjectIDEWidget,
|
||||
useWsListener,
|
||||
} from '@coze-project-ide/framework';
|
||||
import {
|
||||
CustomResourceFolderShortcutService,
|
||||
usePrimarySidebarStore,
|
||||
} from '@coze-project-ide/biz-components';
|
||||
import { useFlags } from '@coze-arch/bot-flags';
|
||||
|
||||
import { useRefresh } from './hooks/use-refresh';
|
||||
import { useProjectApi, useListenWFMessageEvent } from './hooks';
|
||||
|
||||
const Main = () => {
|
||||
const workflowRef = useRef<WorkflowPlaygroundRef>(null);
|
||||
const spaceId = useSpaceId();
|
||||
const projectId = useProjectId();
|
||||
const { version: commitVersion } = useCommitVersion();
|
||||
const [FLAGS] = useFlags();
|
||||
|
||||
const refetchProjectResourceList = usePrimarySidebarStore(
|
||||
state => state.refetch,
|
||||
);
|
||||
const { uri, widget: uiWidget } = useCurrentWidgetContext();
|
||||
const widget = useCurrentWidget<ProjectIDEWidget>();
|
||||
|
||||
const workflowId = useMemo(() => uri?.displayName, [uri]);
|
||||
|
||||
const getProjectApi = useProjectApi();
|
||||
|
||||
const handleInit = useCallback(
|
||||
(workflowState: WorkflowGlobalStateEntity) => {
|
||||
const name = workflowState.info?.name;
|
||||
if (name) {
|
||||
uiWidget.setTitle(name);
|
||||
uiWidget.setUIState('normal');
|
||||
}
|
||||
uiWidget.setIconType(String(workflowState.flowMode));
|
||||
},
|
||||
[uiWidget],
|
||||
);
|
||||
|
||||
const handleReload = () => {
|
||||
widget.refresh();
|
||||
widget.context.widget.setUIState('loading');
|
||||
};
|
||||
|
||||
useWsListener((props: WsMessageProps) => {
|
||||
if (!FLAGS['bot.automation.project_multi_tab']) {
|
||||
return;
|
||||
}
|
||||
workflowRef.current?.onResourceChange(props, handleReload);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const disposable = uiWidget.onFocus(() => {
|
||||
workflowRef.current?.triggerFitView();
|
||||
workflowRef.current?.loadGlobalVariables();
|
||||
});
|
||||
return () => {
|
||||
disposable?.dispose?.();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useRefresh(workflowRef);
|
||||
useListenWFMessageEvent(uri!, workflowRef);
|
||||
|
||||
if (!spaceId || !workflowId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<WorkflowPlayground
|
||||
ref={workflowRef}
|
||||
spaceId={spaceId}
|
||||
workflowId={workflowId}
|
||||
projectCommitVersion={commitVersion}
|
||||
renderHeader={() => null}
|
||||
onInit={handleInit}
|
||||
projectId={projectId}
|
||||
getProjectApi={getProjectApi}
|
||||
// parentContainer={widget.container}
|
||||
className="project-ide-workflow-playground"
|
||||
refetchProjectResourceList={refetchProjectResourceList}
|
||||
renameProjectResource={(resourceId: string) => {
|
||||
const shortcutService = widget.container.get(
|
||||
CustomResourceFolderShortcutService,
|
||||
);
|
||||
shortcutService.renameResource(resourceId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Main;
|
||||
40
frontend/packages/project-ide/biz-workflow/src/registry.tsx
Normal file
40
frontend/packages/project-ide/biz-workflow/src/registry.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import {
|
||||
LayoutPanelType,
|
||||
withLazyLoad,
|
||||
type WidgetRegistry,
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
import { WorkflowWidgetIcon } from './components';
|
||||
|
||||
export const WorkflowWidgetRegistry: WidgetRegistry = {
|
||||
match: /\/workflow\/.*/,
|
||||
area: LayoutPanelType.MAIN_PANEL,
|
||||
renderContent() {
|
||||
const Component = withLazyLoad(() => import('./main'));
|
||||
return <Component />;
|
||||
},
|
||||
renderIcon(ctx) {
|
||||
return <WorkflowWidgetIcon context={ctx} />;
|
||||
},
|
||||
onFocus(ctx) {
|
||||
ctx.widget.onFocusEmitter.fire();
|
||||
},
|
||||
};
|
||||
17
frontend/packages/project-ide/biz-workflow/src/typings.d.ts
vendored
Normal file
17
frontend/packages/project-ide/biz-workflow/src/typings.d.ts
vendored
Normal 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.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"types": ["react", "react-dom"],
|
||||
"jsx": "react",
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib-ts",
|
||||
"tsBuildInfoFile": "./lib-ts/tsconfig.build.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src", "./src/**/*.json"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../arch/bot-api/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-error/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-flags/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-typings/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/i18n/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/report-events/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../biz-components/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../common/auth/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../config/eslint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../config/stylelint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../config/ts-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../framework/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../studio/open-platform/open-chat/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../studio/user-store/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../workflow/base/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../workflow/components/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../workflow/playground/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
frontend/packages/project-ide/biz-workflow/tsconfig.json
Normal file
15
frontend/packages/project-ide/biz-workflow/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"exclude": ["**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.misc.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"include": ["tailwind.config.ts"],
|
||||
"exclude": ["**/node_modules", "./dist"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"types": ["react", "react-dom"],
|
||||
"jsx": "react",
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"rootDir": "./",
|
||||
"outDir": "./dist"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user