feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
31
frontend/packages/agent-ide/layout/.storybook/main.js
Normal file
31
frontend/packages/agent-ide/layout/.storybook/main.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { mergeConfig } from 'vite';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
|
||||
/** @type { import('@storybook/react-vite').StorybookConfig } */
|
||||
const config = {
|
||||
stories: ['../stories/**/*.mdx', '../stories/**/*.stories.tsx'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-onboarding',
|
||||
'@storybook/addon-interactions',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
viteFinal: config =>
|
||||
mergeConfig(config, {
|
||||
plugins: [
|
||||
svgr({
|
||||
svgrOptions: {
|
||||
native: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
};
|
||||
export default config;
|
||||
14
frontend/packages/agent-ide/layout/.storybook/preview.js
Normal file
14
frontend/packages/agent-ide/layout/.storybook/preview.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/** @type { import('@storybook/react').Preview } */
|
||||
const preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
5
frontend/packages/agent-ide/layout/.stylelintrc.js
Normal file
5
frontend/packages/agent-ide/layout/.stylelintrc.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { defineConfig } = require('@coze-arch/stylelint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
extends: [],
|
||||
});
|
||||
16
frontend/packages/agent-ide/layout/README.md
Normal file
16
frontend/packages/agent-ide/layout/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# @coze-agent-ide/layout
|
||||
|
||||
> Project template for react component with storybook.
|
||||
|
||||
## Features
|
||||
|
||||
- [x] eslint & ts
|
||||
- [x] esm bundle
|
||||
- [x] umd bundle
|
||||
- [x] storybook
|
||||
|
||||
## Commands
|
||||
|
||||
- init: `rush update`
|
||||
- dev: `npm run dev`
|
||||
- build: `npm run build`
|
||||
12
frontend/packages/agent-ide/layout/config/rush-project.json
Normal file
12
frontend/packages/agent-ide/layout/config/rush-project.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
7
frontend/packages/agent-ide/layout/eslint.config.js
Normal file
7
frontend/packages/agent-ide/layout/eslint.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {},
|
||||
});
|
||||
93
frontend/packages/agent-ide/layout/package.json
Normal file
93
frontend/packages/agent-ide/layout/package.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"name": "@coze-agent-ide/layout",
|
||||
"version": "0.0.1",
|
||||
"description": "bot 编辑页 layout",
|
||||
"license": "Apache-2.0",
|
||||
"author": "meixuliang.3@bytedance.com",
|
||||
"maintainers": [],
|
||||
"main": "src/index.tsx",
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "vitest --run --passWithNoTests",
|
||||
"test:cov": "npm run test -- --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coze-agent-ide/agent-ide-commons": "workspace:*",
|
||||
"@coze-agent-ide/bot-creator-context": "workspace:*",
|
||||
"@coze-agent-ide/bot-input-length-limit": "workspace:*",
|
||||
"@coze-agent-ide/bot-plugin": "workspace:*",
|
||||
"@coze-agent-ide/chat-area-provider-adapter": "workspace:*",
|
||||
"@coze-agent-ide/chat-debug-area": "workspace:*",
|
||||
"@coze-agent-ide/onboarding": "workspace:*",
|
||||
"@coze-agent-ide/space-bot": "workspace:*",
|
||||
"@coze-arch/bot-api": "workspace:*",
|
||||
"@coze-arch/bot-error": "workspace:*",
|
||||
"@coze-arch/bot-flags": "workspace:*",
|
||||
"@coze-arch/bot-hooks": "workspace:*",
|
||||
"@coze-arch/bot-icons": "workspace:*",
|
||||
"@coze-arch/bot-semi": "workspace:*",
|
||||
"@coze-arch/bot-space-api": "workspace:*",
|
||||
"@coze-arch/bot-studio-store": "workspace:*",
|
||||
"@coze-arch/bot-tea": "workspace:*",
|
||||
"@coze-arch/bot-utils": "workspace:*",
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-arch/logger": "workspace:*",
|
||||
"@coze-arch/report-events": "workspace:*",
|
||||
"@coze-common/assets": "workspace:*",
|
||||
"@coze-common/chat-area": "workspace:*",
|
||||
"@coze-common/chat-core": "workspace:*",
|
||||
"@coze-common/md-editor-adapter": "workspace:*",
|
||||
"@coze-foundation/global-store": "workspace:*",
|
||||
"@coze-foundation/layout": "workspace:*",
|
||||
"@coze-foundation/space-store": "workspace:*",
|
||||
"@coze-studio/bot-detail-store": "workspace:*",
|
||||
"@coze-studio/components": "workspace:*",
|
||||
"@coze-studio/entity-adapter": "workspace:*",
|
||||
"@coze-studio/publish-manage-hooks": "workspace:*",
|
||||
"@coze-studio/user-store": "workspace:*",
|
||||
"@douyinfe/semi-icons": "^2.36.0",
|
||||
"ahooks": "^3.7.8",
|
||||
"classnames": "^2.3.2",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"immer": "^10.0.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
"react-helmet": "^6.1.0",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/stylelint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@rsbuild/core": "1.1.13",
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/lodash-es": "^4.17.10",
|
||||
"@types/node": "18.18.9",
|
||||
"@types/react": "18.2.37",
|
||||
"@types/react-dom": "18.2.15",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"react-is": ">= 16.8.0",
|
||||
"react-router-dom": "^6.22.0",
|
||||
"styled-components": ">= 2",
|
||||
"stylelint": "^15.11.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vite": "^4.3.9",
|
||||
"vite-plugin-svgr": "~3.3.0",
|
||||
"vitest": "~3.0.5",
|
||||
"webpack": "~5.91.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.2.0",
|
||||
"react-dom": ">=18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozPeopleFill,
|
||||
IconCozTeamFill,
|
||||
IconCozCheckMarkCircleFillPalette,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
Avatar,
|
||||
Typography,
|
||||
Tag,
|
||||
Popover,
|
||||
IconButton,
|
||||
} from '@coze-arch/coze-design';
|
||||
import { formatDate } from '@coze-arch/bot-utils';
|
||||
import { useSpaceStore } from '@coze-arch/bot-studio-store';
|
||||
import { IconEditNew } from '@coze-arch/bot-icons';
|
||||
import { SpaceType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import { BotPublishStatus } from '../bot-publish-status';
|
||||
|
||||
const BotInfoCardContent = ({ deployButton }: { deployButton: ReactNode }) => {
|
||||
const { botInfo } = useBotInfoStore(
|
||||
useShallow(state => ({
|
||||
botInfo: state,
|
||||
})),
|
||||
);
|
||||
const {
|
||||
space: { name: spaceName, space_type: spaceType },
|
||||
} = useSpaceStore();
|
||||
const isPersonal = spaceType === SpaceType.Personal;
|
||||
return (
|
||||
<div className="w-[260px] p-4 coz-bg-max">
|
||||
<div className="flex items-center justify-center mb-7">
|
||||
<Avatar size="medium" src={botInfo.icon_url} />
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2 flex-col">
|
||||
<Typography.Text strong className="!text-xxl !font-medium">
|
||||
{botInfo.name}
|
||||
</Typography.Text>
|
||||
|
||||
<div className="flex items-cente">
|
||||
<Tag
|
||||
color="primary"
|
||||
className="max-w-[160px] !bg-transparent !coz-fg-secondary !p-0"
|
||||
prefixIcon={
|
||||
isPersonal ? <IconCozPeopleFill /> : <IconCozTeamFill />
|
||||
}
|
||||
>
|
||||
{spaceName}
|
||||
</Tag>
|
||||
<BotPublishStatus deployButton={deployButton} />
|
||||
</div>
|
||||
|
||||
{botInfo.description ? (
|
||||
<Typography.Paragraph
|
||||
className="text-sm coz-fg-primary"
|
||||
ellipsis={{ rows: 2 }}
|
||||
>
|
||||
{botInfo.description}
|
||||
</Typography.Paragraph>
|
||||
) : null}
|
||||
<div className="text-xs coz-fg-secondary">
|
||||
{I18n.t('Create_time')}:{' '}
|
||||
{botInfo.create_time ? formatDate(Number(botInfo.create_time)) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface BotInfoCardProps {
|
||||
isReadonly: boolean;
|
||||
editBotInfoFn: () => void;
|
||||
deployButton: ReactNode;
|
||||
}
|
||||
|
||||
export const BotInfoCard = ({
|
||||
isReadonly,
|
||||
editBotInfoFn,
|
||||
deployButton,
|
||||
}: BotInfoCardProps) => {
|
||||
const { botInfo, noPublish } = useBotInfoStore(
|
||||
useShallow(state => ({
|
||||
botInfo: state,
|
||||
noPublish: !state.has_publish,
|
||||
})),
|
||||
);
|
||||
|
||||
const triggerContent = (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative">
|
||||
<Avatar
|
||||
size="small"
|
||||
shape="square"
|
||||
src={botInfo?.icon_url}
|
||||
className="rounded"
|
||||
></Avatar>
|
||||
{!noPublish ? (
|
||||
<div className="absolute flex justify-center items-center -right-[1px] -bottom-[1px] w-3 h-3 text-[12px] coz-bg-max box-content border-[1.5px] border-solid rounded-small border-[#fff]">
|
||||
<IconCozCheckMarkCircleFillPalette className="relative coz-fg-hglt-green" />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Typography.Title className="!text-[16px] !coz-fg-plus !font-medium !leading-[22px]">
|
||||
{botInfo?.name}
|
||||
</Typography.Title>
|
||||
{!isReadonly && (
|
||||
<IconButton
|
||||
className="edit-btn"
|
||||
color="secondary"
|
||||
icon={<IconEditNew />}
|
||||
theme="borderless"
|
||||
onClick={() => {
|
||||
editBotInfoFn();
|
||||
}}
|
||||
data-testid="bot.ide.bot_creator.bot-info-edit-create-edit-info-button"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={<BotInfoCardContent deployButton={deployButton} />}
|
||||
trigger="hover"
|
||||
position="bottomLeft"
|
||||
>
|
||||
{triggerContent}
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -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, type ReactNode } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozCheckMarkCircleFill,
|
||||
IconCozInfoCircleFill,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import { Tag, Popover, Divider } from '@coze-arch/coze-design';
|
||||
import { ConnectorDynamicStatus } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import s from '../bot-status/style.module.less';
|
||||
import { renderWarningContent } from '../bot-status/origin-status';
|
||||
|
||||
export const BotPublishStatus = ({
|
||||
deployButton,
|
||||
}: {
|
||||
deployButton: ReactNode;
|
||||
}) => {
|
||||
const { connectors, noPublish } = useBotInfoStore(
|
||||
useShallow(store => ({
|
||||
noPublish: !store.has_publish,
|
||||
connectors: store.connectors,
|
||||
})),
|
||||
);
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const renderPublishStatus = () => {
|
||||
const warningList = connectors?.filter(
|
||||
item => item.connector_status !== ConnectorDynamicStatus.Normal,
|
||||
);
|
||||
return warningList?.length ? (
|
||||
<Popover
|
||||
position="bottomLeft"
|
||||
visible={visible}
|
||||
content={renderWarningContent({
|
||||
warningList,
|
||||
onCancel: () => setVisible(false),
|
||||
deployButton,
|
||||
})}
|
||||
trigger="custom"
|
||||
>
|
||||
<Divider layout="vertical" className="!h-3 mx-2" />
|
||||
<Tag
|
||||
color="yellow"
|
||||
className="!p-0"
|
||||
prefixIcon={<IconCozInfoCircleFill />}
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
<div>{I18n.t('bot_status_published')}</div>
|
||||
</Tag>
|
||||
</Popover>
|
||||
) : (
|
||||
<>
|
||||
<Divider layout="vertical" className="!h-3 mx-2" />
|
||||
<Tag
|
||||
color="primary"
|
||||
className="!bg-transparent !p-0 !coz-fg-secondary"
|
||||
prefixIcon={
|
||||
<IconCozCheckMarkCircleFill className="coz-fg-hglt-green" />
|
||||
}
|
||||
>
|
||||
{I18n.t('bot_status_published')}
|
||||
</Tag>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={s['status-tag']}>
|
||||
{noPublish ? null : renderPublishStatus()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10" fill="none" >
|
||||
<circle cx="5" cy="5" r="5" fill="#C6C6CD" />
|
||||
<path d="M4.49997 2.49923L4.49996 5.39945C4.49996 5.45469 4.54474 5.49946 4.59997 5.49945L7.5 5.49902" stroke="white" stroke-linecap="round" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 298 B |
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { OriginStatus, renderWarningContent } from './origin-status';
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozLoading } from '@coze-arch/coze-design/icons';
|
||||
import { Tag } from '@coze-arch/coze-design';
|
||||
import { UIButton } from '@coze-arch/bot-semi';
|
||||
import {
|
||||
ConnectorDynamicStatus,
|
||||
type ConnectorInfo,
|
||||
} from '@coze-arch/bot-api/developer_api';
|
||||
import { IconAlertCircle } from '@douyinfe/semi-icons';
|
||||
|
||||
import s from './style.module.less';
|
||||
|
||||
export function OriginStatus() {
|
||||
const { savingInfoSaving, savingInfoTime } = usePageRuntimeStore(
|
||||
useShallow(store => ({
|
||||
savingInfoSaving: store.savingInfo.saving,
|
||||
savingInfoTime: store.savingInfo.time,
|
||||
})),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tag
|
||||
color="primary"
|
||||
className="!bg-transparent !p-0 !text-xs"
|
||||
loading={savingInfoSaving}
|
||||
prefixIcon={savingInfoSaving ? <IconCozLoading /> : null}
|
||||
>
|
||||
{savingInfoSaving ? (
|
||||
<div className={s['status-tag-spin']}>
|
||||
<span>{I18n.t('bot_autosave_saving')}</span>
|
||||
</div>
|
||||
) : (
|
||||
I18n.t('devops_publish_multibranch_auto_saved', {
|
||||
time: savingInfoTime,
|
||||
})
|
||||
)}
|
||||
</Tag>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const renderWarningContent = ({
|
||||
warningList,
|
||||
onCancel,
|
||||
readonly,
|
||||
deployButton = null,
|
||||
}: {
|
||||
warningList: ConnectorInfo[];
|
||||
onCancel: () => void;
|
||||
readonly?: boolean;
|
||||
deployButton?: ReactNode;
|
||||
}) => (
|
||||
<div className={s['warning-content']}>
|
||||
{/* TODO: 多个异常状态文案后续由接口返回, 目前只有discord存在异常状态,走不到这里 */}
|
||||
{warningList.length > 1 ? (
|
||||
<>
|
||||
<div className={s['title-box']}>
|
||||
<IconAlertCircle
|
||||
size="large"
|
||||
style={{ color: 'var(--semi-color-warning)' }}
|
||||
/>
|
||||
<span className={s.title}>
|
||||
{I18n.t('bot_pulish_offline_modal_title2', {
|
||||
platform_number: warningList?.length,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<div className={s.main}>
|
||||
{warningList?.map(item => (
|
||||
<div className={s['warning-list']}>
|
||||
<h4>{item.name}</h4>
|
||||
<span>
|
||||
{I18n.t('bot_publish_offline_notice_no_certain_time', {
|
||||
platform: item.name,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className={s['title-box']}>
|
||||
<IconAlertCircle
|
||||
size="large"
|
||||
style={{ color: 'var(--semi-color-warning)' }}
|
||||
/>
|
||||
<span className={s.title}>
|
||||
{warningList[0].connector_status === ConnectorDynamicStatus.Offline
|
||||
? I18n.t('bot_pulish_offline_modal_title1', {
|
||||
platform: warningList[0].name,
|
||||
})
|
||||
: warningList[0].name}
|
||||
</span>
|
||||
</div>
|
||||
<div className={s.main}>
|
||||
{warningList[0].connector_status === ConnectorDynamicStatus.Offline
|
||||
? I18n.t('bot_publish_offline_notice_no_certain_time', {
|
||||
platform: warningList[0].name,
|
||||
})
|
||||
: I18n.t('bot_publish_token_expired_notice', {
|
||||
platform: warningList[0].name,
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{readonly ? (
|
||||
<div className={s.footer}>
|
||||
<UIButton theme="solid" type="warning" onClick={onCancel}>
|
||||
{I18n.t('devops_publish_multibranch_i_know')}
|
||||
</UIButton>
|
||||
</div>
|
||||
) : (
|
||||
<div className={s.footer}>
|
||||
<UIButton
|
||||
theme="light"
|
||||
type="tertiary"
|
||||
className={s['cancel-btn']}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{I18n.t('Cancel')}
|
||||
</UIButton>
|
||||
{deployButton}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,153 @@
|
||||
.bot-info-background() {
|
||||
padding: 0 6px;
|
||||
background: var(--Light-color-grey---grey-1, #F0F0F5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
color: var(--light-usage-text-color-text-2, rgba(29, 28, 35, 60%));
|
||||
letter-spacing: 0.12px;
|
||||
|
||||
&-success-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&-warning-box {
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
padding: 4px;
|
||||
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(46, 47, 56, 5%);
|
||||
}
|
||||
|
||||
&-icon {
|
||||
color: var(--semi-color-warning);
|
||||
|
||||
svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.status-tag-dot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.saving-info {
|
||||
margin-left: 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
color: var(--light-usage-text-color-text-3, rgba(29, 28, 35, 35%));
|
||||
.bot-info-background();
|
||||
|
||||
.status-tag-spin {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
width: 400px;
|
||||
padding: 16px;
|
||||
|
||||
.title-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.title {
|
||||
margin-left: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
margin: 0 0 16px 28px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
|
||||
.warning-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
|
||||
span {
|
||||
color: rgba(29, 28, 35, 60%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.cancel-btn {
|
||||
height: 38px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// collaboration
|
||||
.collaboration-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
color: var(--Light-usage-text---color-text-2, rgba(29, 28, 35, 60%));
|
||||
.bot-info-background();
|
||||
|
||||
:global(.semi-icon) {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.icon-submitted {
|
||||
color: var(--semi-color-primary)
|
||||
}
|
||||
|
||||
.icon-published {
|
||||
color: var(--semi-color-success)
|
||||
}
|
||||
|
||||
.icon-warning {
|
||||
color: var(--semi-color-warning);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
|
||||
import { getBotDetailIsReadonly } from '@coze-studio/bot-detail-store';
|
||||
import { EVENT_NAMES, sendTeaEvent } from '@coze-arch/bot-tea';
|
||||
import { type Type } from '@coze-arch/bot-semi/Button';
|
||||
|
||||
export interface DeployButtonProps {
|
||||
btnType?: Type;
|
||||
btnText?: string;
|
||||
customStyle?: Record<string, string>;
|
||||
readonly?: boolean;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
export const useDeployService = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { botId, botInfo, spaceId } = useBotInfoStore(
|
||||
useShallow(s => ({
|
||||
description: s.description,
|
||||
botId: s.botId,
|
||||
botInfo: s,
|
||||
spaceId: s.space_id,
|
||||
})),
|
||||
);
|
||||
|
||||
const handleDeploy = () => {
|
||||
if (!botId || getBotDetailIsReadonly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigate(`/space/${spaceId}/bot/${botId}/publish`);
|
||||
};
|
||||
|
||||
const handlePublish = () => {
|
||||
sendTeaEvent(EVENT_NAMES.bot_publish_button_click, {
|
||||
bot_id: botId || '',
|
||||
bot_name: botInfo?.name || '',
|
||||
});
|
||||
|
||||
handleDeploy();
|
||||
};
|
||||
|
||||
return {
|
||||
handlePublish,
|
||||
} as const;
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 { Fragment } from 'react';
|
||||
|
||||
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozCheckMarkCircleFillPalette } from '@coze-arch/coze-design/icons';
|
||||
import { Tooltip } from '@coze-arch/coze-design';
|
||||
import { type Type } from '@coze-arch/bot-semi/Button';
|
||||
import { BotDebugButton } from '@coze-agent-ide/space-bot/component';
|
||||
|
||||
import { useDeployService } from './hooks/service';
|
||||
|
||||
export interface DeployButtonUIProps {
|
||||
btnType?: Type;
|
||||
btnText?: string;
|
||||
customStyle?: Record<string, string>;
|
||||
readonly?: boolean;
|
||||
tooltip?: string;
|
||||
onClick?: () => void;
|
||||
showChangeTip?: boolean;
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export type DeployButtonProps = Omit<
|
||||
DeployButtonUIProps,
|
||||
'showChangeTip' | 'onClick' | 'disabled' | 'loading'
|
||||
>;
|
||||
|
||||
export { useDeployService };
|
||||
|
||||
export const DeployButton: React.FC<DeployButtonProps> = props => {
|
||||
const { handlePublish } = useDeployService();
|
||||
|
||||
const hasUnpublishChange = usePageRuntimeStore(s => s.hasUnpublishChange);
|
||||
|
||||
const showChangeTip = hasUnpublishChange;
|
||||
return (
|
||||
<DeployButtonUI
|
||||
onClick={handlePublish}
|
||||
showChangeTip={showChangeTip}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const DeployButtonUI = ({
|
||||
btnType = 'primary',
|
||||
btnText = I18n.t('bot_publish_button'),
|
||||
customStyle,
|
||||
readonly = false,
|
||||
tooltip,
|
||||
showChangeTip,
|
||||
onClick,
|
||||
disabled,
|
||||
loading,
|
||||
}: DeployButtonUIProps) => {
|
||||
const showTip = showChangeTip || !!tooltip;
|
||||
const ToolTipCom = showTip ? Tooltip : Fragment;
|
||||
|
||||
const btn = (
|
||||
<ToolTipCom
|
||||
content={tooltip || I18n.t('bot_has_changes_tip')}
|
||||
visible={showChangeTip}
|
||||
>
|
||||
<BotDebugButton
|
||||
data-testid="agent-ide.goto.publish-button"
|
||||
theme="solid"
|
||||
type={btnType}
|
||||
iconPosition="right"
|
||||
icon={
|
||||
showChangeTip ? (
|
||||
<IconCozCheckMarkCircleFillPalette className="w-[5px] h-[5px]" />
|
||||
) : undefined
|
||||
}
|
||||
style={customStyle}
|
||||
disabled={disabled || readonly}
|
||||
onClick={onClick}
|
||||
loading={loading}
|
||||
>
|
||||
{btnText}
|
||||
</BotDebugButton>
|
||||
</ToolTipCom>
|
||||
);
|
||||
|
||||
return disabled ? (
|
||||
<Tooltip
|
||||
content={I18n.t('devops_publish_multibranch_publish_disabled_tooltip')}
|
||||
>
|
||||
{btn}
|
||||
</Tooltip>
|
||||
) : (
|
||||
btn
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,274 @@
|
||||
/* stylelint-disable no-descending-specificity */
|
||||
/* stylelint-disable max-nesting-depth */
|
||||
@import '@coze-common/assets/style/mixins.less';
|
||||
|
||||
@import '@coze-common/assets/style/common.less';
|
||||
|
||||
.header {
|
||||
z-index: 999;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
height: 56px;
|
||||
padding: 8px;
|
||||
padding-right: 12px;
|
||||
|
||||
background: var(--light-color-white);
|
||||
border-bottom: 1px solid theme('colors.stroke.5');
|
||||
|
||||
.bot-avatar-ctn {
|
||||
position: relative;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0 12px;
|
||||
|
||||
.bot-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.bot-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.bot-info-title {
|
||||
overflow: hidden;
|
||||
// 最大宽度 = 视图宽度 - 一半视图宽度 - 名称到左边的距离 - 中间导航一半的距离 - 编辑icon的距离 - 编辑icon到右边导航的距离
|
||||
max-width: calc(100vw - 50vw - 104px - 85px - 24px - 24px);
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: inherit;
|
||||
color: var(--light-color-black-black, #000);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bot-info-item-gap {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.bot-info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
margin-left: 2px;
|
||||
color: #6b6d75;
|
||||
|
||||
:global {
|
||||
svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit-btn-icon {
|
||||
.common-svg-icon(16px, #6b6d75);
|
||||
}
|
||||
}
|
||||
|
||||
.team-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin-right: 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
color: var(--Light-usage-text---color-text-2, rgb(29 28 35 / 60%));
|
||||
letter-spacing: 0.12px;
|
||||
}
|
||||
|
||||
.team-avatar {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 2px;
|
||||
|
||||
:global {
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bot-exit-btn {
|
||||
:global {
|
||||
.semi-button.semi-button-with-icon-only {
|
||||
width: 32px;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
color: var(--Light-usage-text---color-text-2, rgb(29 28 35 / 60%));
|
||||
letter-spacing: 0.12px;
|
||||
|
||||
&-success-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&-warning-box {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgb(46 47 56 / 5%);
|
||||
}
|
||||
|
||||
&-icon {
|
||||
color: var(--semi-color-warning);
|
||||
|
||||
svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.bot-info-background {
|
||||
padding: 0 6px;
|
||||
background: var(--Light-color-grey---grey-1, #F0F0F5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-tag-dot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.edito-btn {
|
||||
margin-left: 8px;
|
||||
color: #6b6d75;
|
||||
}
|
||||
|
||||
.status-tag-spin {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.change-tip {
|
||||
/* 133.333% */
|
||||
margin-right: 20px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
color: var(--light-usage-text-color-text-2, rgb(29 28 35 / 60%));
|
||||
}
|
||||
|
||||
.change-tip-icon {
|
||||
.common-svg-icon(13px, rgba(255, 150, 0, 1));
|
||||
}
|
||||
|
||||
.saving-info {
|
||||
margin-left: 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
color: var(--Light-usage-text---color-text-2, rgb(29 28 35 / 60%));
|
||||
}
|
||||
|
||||
button.icon-btn {
|
||||
width: 32px;
|
||||
min-width: 32px;
|
||||
max-width: 32px;
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
max-height: 32px;
|
||||
padding: 8px;
|
||||
|
||||
color: var(--light-usage-text-color-text-2, rgba(29, 28, 35, 60%));
|
||||
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--light-usage-border-color-border, rgba(29, 28, 35, 8%));
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
width: 400px;
|
||||
padding: 16px;
|
||||
|
||||
.title-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.title {
|
||||
margin-left: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
margin: 0 0 16px 28px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
|
||||
.warning-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
|
||||
span {
|
||||
color: rgb(29 28 35 / 60%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.cancel-btn {
|
||||
height: 38px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bot-menu-nav {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.team-name {
|
||||
max-width: 320px;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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 { useNavigate } from 'react-router-dom';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { type ReactNode, useEffect, useRef } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import cx from 'classnames';
|
||||
import { useUpdateAgent } from '@coze-studio/entity-adapter';
|
||||
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
|
||||
import { useDiffTaskStore } from '@coze-studio/bot-detail-store/diff-task';
|
||||
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
|
||||
import { useBotDetailIsReadonly } from '@coze-studio/bot-detail-store';
|
||||
import { BackButton } from '@coze-foundation/layout';
|
||||
import { type SenderInfo, useBotInfo } from '@coze-common/chat-area';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { renderHtmlTitle } from '@coze-arch/bot-utils';
|
||||
import { BotPageFromEnum } from '@coze-arch/bot-typings/common';
|
||||
import { useSpaceStore } from '@coze-arch/bot-studio-store';
|
||||
import { type DraftBot } from '@coze-arch/bot-api/developer_api';
|
||||
import {
|
||||
ModeSelect,
|
||||
type ModeSelectProps,
|
||||
} from '@coze-agent-ide/space-bot/component';
|
||||
|
||||
import { BotInfoCard } from './bot-info-card';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
export interface BotHeaderProps {
|
||||
pageName?: string;
|
||||
isEditLocked?: boolean;
|
||||
addonAfter?: ReactNode;
|
||||
modeOptionList: ModeSelectProps['optionList'];
|
||||
deployButton: ReactNode;
|
||||
}
|
||||
|
||||
export const BotHeader: React.FC<BotHeaderProps> = props => {
|
||||
const navigate = useNavigate();
|
||||
const spaceID = useSpaceStore(state => state.space.id);
|
||||
const isReadonly = useBotDetailIsReadonly();
|
||||
const { pageFrom } = usePageRuntimeStore(
|
||||
useShallow(state => ({
|
||||
pageFrom: state.pageFrom,
|
||||
})),
|
||||
);
|
||||
|
||||
const botInfo = useBotInfoStore();
|
||||
|
||||
const { updateBotInfo } = useBotInfo();
|
||||
|
||||
const botInfoRef = useRef<DraftBot>();
|
||||
|
||||
useEffect(() => {
|
||||
botInfoRef.current = botInfo as DraftBot;
|
||||
}, [botInfo]);
|
||||
|
||||
const { modal: updateBotModal, startEdit: editBotInfoFn } = useUpdateAgent({
|
||||
botInfoRef,
|
||||
onSuccess: (
|
||||
botID?: string,
|
||||
spaceId?: string,
|
||||
extra?: {
|
||||
botName?: string;
|
||||
botAvatar?: string;
|
||||
},
|
||||
) => {
|
||||
updateBotInfo(oldBotInfo => {
|
||||
const botInfoMap = cloneDeep(oldBotInfo);
|
||||
|
||||
if (!botID) {
|
||||
return botInfoMap;
|
||||
}
|
||||
botInfoMap[botID] = {
|
||||
url: extra?.botAvatar ?? '',
|
||||
nickname: extra?.botName ?? '',
|
||||
id: botID,
|
||||
allowMention: false,
|
||||
} satisfies SenderInfo;
|
||||
|
||||
return botInfoMap;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const diffTask = useDiffTaskStore(state => state.diffTask);
|
||||
|
||||
const goBackToBotList = () => {
|
||||
navigate(`/space/${spaceID}/develop`);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cx(s.header, 'coz-bg-primary')}>
|
||||
{/* page title */}
|
||||
<Helmet>
|
||||
<title>
|
||||
{renderHtmlTitle(
|
||||
pageFrom === BotPageFromEnum.Bot
|
||||
? I18n.t('tab_bot_detail', {
|
||||
bot_name: botInfo?.name ?? '',
|
||||
})
|
||||
: I18n.t('tab_explore_bot_detail', {
|
||||
bot_name: botInfo?.name ?? '',
|
||||
}),
|
||||
)}
|
||||
</title>
|
||||
</Helmet>
|
||||
{/** 1. 左侧bot信息区 */}
|
||||
<div className="flex items-center">
|
||||
<BackButton onClickBack={goBackToBotList} />
|
||||
<BotInfoCard
|
||||
isReadonly={isReadonly}
|
||||
editBotInfoFn={editBotInfoFn}
|
||||
deployButton={props.deployButton}
|
||||
/>
|
||||
{/** 模式选择器 */}
|
||||
{diffTask || IS_OPEN_SOURCE ? null : (
|
||||
<ModeSelect optionList={props.modeOptionList} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 2. 中间bot菜单区 - 已下线 */}
|
||||
|
||||
{/* 3. 右侧bot状态区 */}
|
||||
{props.addonAfter}
|
||||
{updateBotModal}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
div.link-img {
|
||||
display: flex;
|
||||
border-radius: unset;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid var(--light-usage-fill-color-fill-1, rgb(46 46 56 / 8%));
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.open-in-tips {
|
||||
padding: 0 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
color: var(--light-usage-text-color-text-2, rgba(29, 28, 35, 60%));
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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 { useNavigate } from 'react-router-dom';
|
||||
import React, { type FC } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useIsPublishRecordReady } from '@coze-studio/publish-manage-hooks';
|
||||
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
|
||||
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
|
||||
import { useBotDetailIsReadonly } from '@coze-studio/bot-detail-store';
|
||||
import MORE_PLATFORM_ICON from '@coze-common/assets/image/more-platform-icon.jpg';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Divider } from '@coze-arch/bot-semi';
|
||||
import { IconMenuLogo } from '@coze-arch/bot-icons';
|
||||
import { useFlags } from '@coze-arch/bot-flags';
|
||||
import { IntelligenceType } from '@coze-arch/bot-api/intelligence_api';
|
||||
import {
|
||||
BotMarketStatus,
|
||||
ConnectorDynamicStatus,
|
||||
type ConnectorInfo,
|
||||
} from '@coze-arch/bot-api/developer_api';
|
||||
import { IconCozArrowRightFill, IconCozMore } from '@coze-arch/coze-design/icons';
|
||||
import { Dropdown, IconButton, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import { LinkDropItem } from './link-drop-item';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
export type ExtendedConnectorInfo = Omit<ConnectorInfo, 'icon'> & {
|
||||
icon?: string | React.ReactNode;
|
||||
};
|
||||
|
||||
const LinkMenu = (props: {
|
||||
connectors: ExtendedConnectorInfo[];
|
||||
renderMorePlatform: boolean;
|
||||
isReadOnly: boolean;
|
||||
}) => {
|
||||
const { connectors = [], renderMorePlatform, isReadOnly } = props;
|
||||
return (
|
||||
<Dropdown.Menu mode="menu">
|
||||
{!isReadOnly && (
|
||||
<>
|
||||
<div className={s['open-in-tips']}>
|
||||
{I18n.t('bot_list_open_button', {
|
||||
platform: '',
|
||||
})}
|
||||
</div>
|
||||
<Divider margin={2} layout="horizontal" />
|
||||
</>
|
||||
)}
|
||||
{connectors.map(item => (
|
||||
<LinkDropItem
|
||||
linkInfo={item}
|
||||
key={item.name}
|
||||
hasMorePlatform={false}
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
))}
|
||||
{renderMorePlatform ? (
|
||||
<LinkDropItem
|
||||
linkInfo={{
|
||||
name: I18n.t('bot_share_more_platforms'),
|
||||
icon: MORE_PLATFORM_ICON,
|
||||
}}
|
||||
hasMorePlatform
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
) : null}
|
||||
</Dropdown.Menu>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line complexity, @coze-arch/max-line-per-function
|
||||
export const MoreMenuButton: FC = () => {
|
||||
const { hasPublish, connectors, version, botId, spaceId, botMarketStatus } =
|
||||
useBotInfoStore(
|
||||
useShallow(state => ({
|
||||
botId: state.botId,
|
||||
spaceId: state.space_id,
|
||||
hasPublish: state.has_publish,
|
||||
connectors: state.connectors,
|
||||
version: state.version,
|
||||
botMarketStatus: state.botMarketStatus,
|
||||
})),
|
||||
);
|
||||
const { historyVisible } = usePageRuntimeStore(
|
||||
useShallow(state => ({
|
||||
historyVisible: state.historyVisible,
|
||||
})),
|
||||
);
|
||||
|
||||
const isReadOnly = useBotDetailIsReadonly();
|
||||
|
||||
const StoreConnector: ExtendedConnectorInfo = {
|
||||
id: 'store',
|
||||
name: I18n.t('bot_edit_store'),
|
||||
icon: (
|
||||
<div className="w-4 h-4 rounded-mini [&_.semi-icon-default]:w-full [&_.semi-icon-default]:h-full [&_svg]:w-full [&_svg]:h-full">
|
||||
<IconMenuLogo />
|
||||
</div>
|
||||
),
|
||||
share_link: `${window.location.origin}/store/agent/${botId}?bot_id=true`,
|
||||
connector_status:
|
||||
botMarketStatus === BotMarketStatus.Online
|
||||
? ConnectorDynamicStatus.Normal
|
||||
: ConnectorDynamicStatus.Offline,
|
||||
};
|
||||
const extendedConnectors = [
|
||||
...(connectors as ExtendedConnectorInfo[]),
|
||||
].concat(botMarketStatus === BotMarketStatus.Online ? [StoreConnector] : []);
|
||||
|
||||
const hasMorePlatform = extendedConnectors?.some(item => !item.share_link);
|
||||
|
||||
// 不展示open条件 1.来自 explore的bot(已经没有 explore 了) 2. 未发布过平台的bot 3. 历史版本的bot(考虑revert后展示) 4.发布过的所有平台都没有分享链接
|
||||
const hideOpenIn =
|
||||
!extendedConnectors?.length ||
|
||||
(version && historyVisible) ||
|
||||
extendedConnectors?.every(item => !item.share_link);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [FLAGS] = useFlags();
|
||||
|
||||
//有编辑权限 && 有发布的业务线
|
||||
const showPublishManageMenu = !isReadOnly && hasPublish;
|
||||
|
||||
const { ready, inited } = useIsPublishRecordReady({
|
||||
type: IntelligenceType.Bot,
|
||||
intelligenceId: botId,
|
||||
spaceId,
|
||||
enable:
|
||||
showPublishManageMenu &&
|
||||
// 社区版暂不支持该功能
|
||||
FLAGS['bot.studio.publish_management'] &&
|
||||
!IS_OPEN_SOURCE,
|
||||
});
|
||||
|
||||
if (!showPublishManageMenu && hideOpenIn) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const publishMenuItems = [
|
||||
{
|
||||
label: I18n.t('analytics_page_title'),
|
||||
to: `/space/${spaceId}/publish/agent/${botId}?tab=analysis`,
|
||||
},
|
||||
{
|
||||
label: I18n.t('release_management_trace'),
|
||||
to: `/space/${spaceId}/publish/agent/${botId}?tab=logs`,
|
||||
},
|
||||
{
|
||||
label: I18n.t('release_management_trigger'),
|
||||
to: `/space/${spaceId}/publish/agent/${botId}?tab=triggers`,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
render={
|
||||
<Dropdown.Menu mode="menu">
|
||||
{/* 社区版暂不支持该功能 */}
|
||||
{showPublishManageMenu &&
|
||||
FLAGS['bot.studio.publish_management'] &&
|
||||
!IS_OPEN_SOURCE
|
||||
? publishMenuItems.map(item => {
|
||||
const menuItem = (
|
||||
<Dropdown.Item
|
||||
disabled={!ready}
|
||||
onClick={() => navigate(item.to)}
|
||||
>
|
||||
{item.label}
|
||||
</Dropdown.Item>
|
||||
);
|
||||
return ready || !inited ? (
|
||||
menuItem
|
||||
) : (
|
||||
<Tooltip content={I18n.t('release_management_generating')}>
|
||||
<div>{menuItem}</div>
|
||||
</Tooltip>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
{hideOpenIn ? null : (
|
||||
<>
|
||||
{/* 社区版暂不支持该功能 */}
|
||||
{showPublishManageMenu &&
|
||||
FLAGS['bot.studio.publish_management'] &&
|
||||
!IS_OPEN_SOURCE ? (
|
||||
<Dropdown.Divider />
|
||||
) : null}
|
||||
<Dropdown
|
||||
render={
|
||||
<LinkMenu
|
||||
connectors={extendedConnectors?.filter(
|
||||
item =>
|
||||
item.share_link &&
|
||||
// 只读态仅显示状态正常的发布渠道
|
||||
(!isReadOnly ||
|
||||
item.connector_status ===
|
||||
ConnectorDynamicStatus.Normal),
|
||||
)}
|
||||
renderMorePlatform={!isReadOnly && !!hasMorePlatform}
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Dropdown.Item>
|
||||
<div className="w-full flex items-center">
|
||||
<div className="flex-1">
|
||||
{I18n.t('release_management_openin')}
|
||||
</div>
|
||||
<IconCozArrowRightFill />
|
||||
</div>
|
||||
</Dropdown.Item>
|
||||
</Dropdown>
|
||||
</>
|
||||
)}
|
||||
</Dropdown.Menu>
|
||||
}
|
||||
>
|
||||
<IconButton icon={<IconCozMore />} />
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { useBoolean, useRequest } from 'ahooks';
|
||||
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
|
||||
import {
|
||||
getBotDetailDtoInfo,
|
||||
updateHeaderStatus,
|
||||
updateBotRequest,
|
||||
} from '@coze-studio/bot-detail-store';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
Item,
|
||||
UIIconButton,
|
||||
Toast,
|
||||
Tooltip,
|
||||
Space,
|
||||
Image,
|
||||
} from '@coze-arch/bot-semi';
|
||||
import {
|
||||
ConnectorDynamicStatus,
|
||||
type ConnectorInfo,
|
||||
} from '@coze-arch/bot-api/developer_api';
|
||||
import { IconLink } from '@douyinfe/semi-icons';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
export type ExtendedConnectorInfo = Omit<ConnectorInfo, 'icon'> & {
|
||||
icon?: string | ReactNode;
|
||||
};
|
||||
|
||||
export const LinkDropItem = (props: {
|
||||
linkInfo: ExtendedConnectorInfo;
|
||||
hasMorePlatform: boolean;
|
||||
isReadOnly: boolean;
|
||||
}) => {
|
||||
const { linkInfo, hasMorePlatform, isReadOnly } = props;
|
||||
const [mouseIn, { setTrue, setFalse }] = useBoolean(false);
|
||||
const { botId, mode } = useBotInfoStore(
|
||||
useShallow(state => ({
|
||||
botId: state.botId,
|
||||
mode: state.mode,
|
||||
})),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const onLinkCopy = (e: any) => {
|
||||
if (!linkInfo.share_link) {
|
||||
return;
|
||||
}
|
||||
e.stopPropagation();
|
||||
const res = copy(linkInfo.share_link);
|
||||
if (res) {
|
||||
Toast.success({
|
||||
showClose: false,
|
||||
content: I18n.t('copy_success'),
|
||||
});
|
||||
}
|
||||
};
|
||||
const { run: updateBot } = useRequest(
|
||||
async () => {
|
||||
if (!botId || isReadOnly) {
|
||||
return;
|
||||
}
|
||||
const { botSkillInfo } = getBotDetailDtoInfo();
|
||||
const { data } = await updateBotRequest({
|
||||
...botSkillInfo,
|
||||
bot_mode: mode,
|
||||
});
|
||||
|
||||
updateHeaderStatus(data);
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
},
|
||||
);
|
||||
|
||||
const onConnectorClick = () => {
|
||||
updateBot();
|
||||
window.open(linkInfo.share_link);
|
||||
};
|
||||
|
||||
const isDisableDropItem =
|
||||
linkInfo.connector_status !== ConnectorDynamicStatus.Normal ||
|
||||
hasMorePlatform;
|
||||
|
||||
const content = (function () {
|
||||
return (
|
||||
<div className={s['link-item']}>
|
||||
<Space>
|
||||
{linkInfo.icon ? (
|
||||
typeof linkInfo.icon === 'string' ? (
|
||||
<Image
|
||||
src={linkInfo.icon}
|
||||
width={16}
|
||||
height={16}
|
||||
preview={false}
|
||||
className={s['link-img']}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-4 h-4 rounded-mini [&_.semi-icon-default]:w-full [&_.semi-icon-default]:h-full [&_svg]:w-full [&_svg]:h-full">
|
||||
{linkInfo.icon}
|
||||
</div>
|
||||
)
|
||||
) : null}
|
||||
<div> {linkInfo.name} </div>
|
||||
</Space>
|
||||
|
||||
<Tooltip content={I18n.t('Copy_link')} position="right">
|
||||
{/* flow 目前不支持粘贴分享链接,coming soon.. */}
|
||||
{mouseIn &&
|
||||
linkInfo.share_link &&
|
||||
linkInfo.id !== FLOW_PUBLISH_ID &&
|
||||
!isReadOnly ? (
|
||||
<UIIconButton
|
||||
icon={<IconLink />}
|
||||
onClick={onLinkCopy}
|
||||
type="tertiary"
|
||||
className={s['copy-btn']}
|
||||
iconSize="small"
|
||||
/>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
})();
|
||||
|
||||
return (
|
||||
<Item
|
||||
onMouseEnter={setTrue}
|
||||
onMouseLeave={setFalse}
|
||||
disabled={isDisableDropItem}
|
||||
onClick={onConnectorClick}
|
||||
>
|
||||
{isDisableDropItem ? (
|
||||
<Tooltip
|
||||
content={
|
||||
hasMorePlatform
|
||||
? I18n.t('bot_share_not_supported_opening')
|
||||
: I18n.t('bot_publish_token_expired_notice', {
|
||||
platform: linkInfo.name,
|
||||
})
|
||||
}
|
||||
position="leftTop"
|
||||
>
|
||||
{content}
|
||||
</Tooltip>
|
||||
) : (
|
||||
content
|
||||
)}
|
||||
</Item>
|
||||
);
|
||||
};
|
||||
263
frontend/packages/agent-ide/layout/src/index.module.less
Normal file
263
frontend/packages/agent-ide/layout/src/index.module.less
Normal file
@@ -0,0 +1,263 @@
|
||||
/* stylelint-disable no-descending-specificity */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
/* stylelint-disable declaration-no-important */
|
||||
@import '@coze-common/assets/style/common.less';
|
||||
|
||||
.wrapper {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
@apply bg-background-1;
|
||||
}
|
||||
|
||||
.layout-hotzone {
|
||||
z-index: calc(var(--chat-z-index-header, 50) + 1);
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
|
||||
width: 100%;
|
||||
// 动态撑满父容器且可以滚动
|
||||
// @reference: https://stackoverflow.com/questions/14962468/how-can-i-combine-flexbox-and-vertical-scroll-in-a-full-height-app
|
||||
min-height: 0;
|
||||
|
||||
&.store {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.spin {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: rgb(255 255 255 / 50%);
|
||||
}
|
||||
|
||||
.playground-neat {
|
||||
.message-area {
|
||||
min-width: 258px;
|
||||
}
|
||||
}
|
||||
|
||||
.develop-area {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@apply coz-bg-plus;
|
||||
}
|
||||
|
||||
.develop-area-scroll {
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.setting-area {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex: 1 1;
|
||||
flex-direction: column;
|
||||
|
||||
border-left: 1px solid rgb(28 29 35 / 12%);
|
||||
|
||||
.setting-title-block {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-collapse-item {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.semi-collapse-header {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.semi-collapse-content {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.semi-select {
|
||||
width: 100%;
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-collapsible-wrapper {
|
||||
padding-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-area {
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
width: 100%;
|
||||
min-width: 404px;
|
||||
height: 100%;
|
||||
|
||||
transition: min-width 0.2s ease;
|
||||
|
||||
@apply bg-background-3;
|
||||
}
|
||||
|
||||
.playground-neat {
|
||||
.message-area {
|
||||
min-width: 258px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 8px 0 0 4px !important;
|
||||
}
|
||||
|
||||
.sheet-title-node-cover {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bj-cover {
|
||||
@apply bg-background-3;
|
||||
|
||||
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 4%),
|
||||
0 0 1px 0 rgb(0 0 0 / 8%);
|
||||
|
||||
}
|
||||
|
||||
.bj-img-cover {
|
||||
z-index: 20;
|
||||
text-shadow: 0 0.5px 1px rgba(0, 0, 0, 25%);
|
||||
// 有背景图时需要覆盖组件样式
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.bj-single-cover {
|
||||
flex: 0 0 auto;
|
||||
height: 64px !important;
|
||||
}
|
||||
|
||||
.border-cover {
|
||||
@apply bg-background-3;
|
||||
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.spin-wrapper.top-level {
|
||||
width: 100%;
|
||||
height: 100% !important;
|
||||
|
||||
:global {
|
||||
.semi-spin-children {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sheet-view-new-header {
|
||||
height: 64px !important;
|
||||
}
|
||||
|
||||
.sheet-view-left-header {
|
||||
padding: 16px 16px 16px 28px !important;
|
||||
}
|
||||
|
||||
.icon-button-16 {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-button {
|
||||
&.semi-button-size-small {
|
||||
height: 16px;
|
||||
padding: 1px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 能力模块默认说明文案样式
|
||||
.tip-text {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 22px;
|
||||
color: var(--light-usage-text-color-text-2, rgb(28 29 35 / 60%));
|
||||
}
|
||||
|
||||
.sheet-view-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.left-sheet-config {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wrapper-single-with-tool-area-hidden {
|
||||
grid-template-columns: 26fr 14fr !important;
|
||||
}
|
||||
|
||||
.tool-card {
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-columns: 13fr 13fr;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tool-card-with-tool-area-hidden {
|
||||
grid-template-columns: 26fr !important;
|
||||
}
|
||||
|
||||
.tool-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.config-left {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.display-none {
|
||||
display: none !important;
|
||||
}
|
||||
46
frontend/packages/agent-ide/layout/src/index.tsx
Normal file
46
frontend/packages/agent-ide/layout/src/index.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 {
|
||||
BotEditorInitLayout,
|
||||
type BotEditorLayoutSlot,
|
||||
type BotEditorLayoutProps,
|
||||
type CustomProviderProps,
|
||||
} from './layout';
|
||||
|
||||
export { BotHeader, type BotHeaderProps } from './components/header';
|
||||
|
||||
export { MoreMenuButton } from './components/header/more-menu-button';
|
||||
|
||||
export {
|
||||
DeployButtonUI,
|
||||
DeployButton,
|
||||
type DeployButtonProps,
|
||||
type DeployButtonUIProps,
|
||||
} from './components/header/deploy-button';
|
||||
|
||||
export {
|
||||
renderWarningContent,
|
||||
OriginStatus,
|
||||
} from './components/header/bot-status';
|
||||
|
||||
export {
|
||||
type BotEditorLayoutSlot,
|
||||
type BotEditorLayoutProps,
|
||||
type CustomProviderProps,
|
||||
};
|
||||
|
||||
export default BotEditorInitLayout;
|
||||
123
frontend/packages/agent-ide/layout/src/layout.tsx
Normal file
123
frontend/packages/agent-ide/layout/src/layout.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 { useParams } from 'react-router-dom';
|
||||
import React, {
|
||||
useState,
|
||||
type FC,
|
||||
type PropsWithChildren,
|
||||
type ReactNode,
|
||||
type ComponentType,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useUpdateEffect } from 'ahooks';
|
||||
import { userStoreService } from '@coze-studio/user-store';
|
||||
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
|
||||
import { type DynamicParams } from '@coze-arch/bot-typings/teamspace';
|
||||
import { Spin } from '@coze-arch/bot-semi';
|
||||
import { CustomError } from '@coze-arch/bot-error';
|
||||
import { useBotPageStore } from '@coze-agent-ide/space-bot/store';
|
||||
import { BotDebugChatAreaProviderAdapter } from '@coze-agent-ide/chat-area-provider-adapter';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
export interface CustomProviderProps {
|
||||
botId: string;
|
||||
}
|
||||
export interface BotEditorLayoutProps {
|
||||
hasHeader?: boolean;
|
||||
}
|
||||
|
||||
export interface BotEditorLayoutSlot {
|
||||
header?: ReactNode;
|
||||
headerBottom?: ReactNode;
|
||||
headerTop?: ReactNode;
|
||||
customProvider?: ComponentType<PropsWithChildren<CustomProviderProps>>;
|
||||
}
|
||||
|
||||
const DefaultFragment: React.FC<PropsWithChildren<CustomProviderProps>> = ({
|
||||
children,
|
||||
}) => <React.Fragment>{children}</React.Fragment>;
|
||||
|
||||
const BotEditorInitLayoutImpl: FC<
|
||||
PropsWithChildren<
|
||||
Omit<BotEditorLayoutProps, 'loading'> &
|
||||
BotEditorLayoutSlot &
|
||||
CustomProviderProps
|
||||
>
|
||||
> = ({
|
||||
children,
|
||||
botId,
|
||||
hasHeader = true,
|
||||
headerBottom,
|
||||
headerTop,
|
||||
header,
|
||||
customProvider,
|
||||
}) => {
|
||||
// 初次加载
|
||||
const [isFirstLoad, setIsFirstLoad] = useState(true);
|
||||
const init = usePageRuntimeStore(state => state.init);
|
||||
const userInfo = userStoreService.useUserInfo();
|
||||
|
||||
const modeSwitching = useBotPageStore(state => state.bot.modeSwitching);
|
||||
const CustomProvider = customProvider || DefaultFragment;
|
||||
// 因为clearStore会保留init值,在切换bot时,init是true,不会是初始值false
|
||||
useUpdateEffect(() => {
|
||||
// init每次initStore都会被更新状态,但这里只需要记录初次的loading,所以需要对isFirstLoad判断
|
||||
if (isFirstLoad && init) {
|
||||
// 如果init完成并且是首次load,表示初次请求完成,将isFirstLoad置为false
|
||||
setIsFirstLoad(false);
|
||||
}
|
||||
}, [init]);
|
||||
|
||||
return (
|
||||
<div className={s.wrapper}>
|
||||
{isFirstLoad && !init ? (
|
||||
<Spin spinning wrapperClassName="h-full w-full" />
|
||||
) : (
|
||||
<CustomProvider botId={botId}>
|
||||
<BotDebugChatAreaProviderAdapter
|
||||
botId={botId}
|
||||
userId={userInfo?.user_id_str}
|
||||
>
|
||||
<Spin
|
||||
spinning={modeSwitching}
|
||||
wrapperClassName={classNames(s['spin-wrapper'], s['top-level'])}
|
||||
>
|
||||
{headerTop}
|
||||
{hasHeader ? header : null}
|
||||
{headerBottom}
|
||||
{children}
|
||||
</Spin>
|
||||
</BotDebugChatAreaProviderAdapter>
|
||||
</CustomProvider>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const BotEditorInitLayout: React.FC<
|
||||
PropsWithChildren<BotEditorLayoutProps & BotEditorLayoutSlot>
|
||||
> = props => {
|
||||
const { bot_id } = useParams<DynamicParams>();
|
||||
if (!bot_id) {
|
||||
throw new CustomError('normal_error', 'failed to get bot_id');
|
||||
}
|
||||
|
||||
return <BotEditorInitLayoutImpl {...props} botId={bot_id} />;
|
||||
};
|
||||
export default BotEditorInitLayout;
|
||||
17
frontend/packages/agent-ide/layout/src/typings.d.ts
vendored
Normal file
17
frontend/packages/agent-ide/layout/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' />
|
||||
129
frontend/packages/agent-ide/layout/tsconfig.build.json
Normal file
129
frontend/packages/agent-ide/layout/tsconfig.build.json
Normal file
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"compilerOptions": {
|
||||
"types": [],
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": true,
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.build.tsbuildinfo"
|
||||
},
|
||||
"include": ["src"],
|
||||
"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-hooks/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-space-api/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-store/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-tea/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-typings/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/bot-utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/i18n/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/logger/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../arch/report-events/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../bot-input-length-limit/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../bot-plugin/entry/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../chat-area-provider-adapter/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../chat-debug-area/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../common/assets/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../common/chat-area/chat-area/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../common/chat-area/chat-core/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../common/md-editor-adapter/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../commons/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../components/bot-icons/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../components/bot-semi/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": "../../../config/vitest-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../context/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../foundation/global-store/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../foundation/layout/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../foundation/space-store/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../onboarding/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../space-bot/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../studio/components/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../studio/entity-adapter/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../studio/publish-manage-hooks/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../studio/stores/bot-detail/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../studio/user-store/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
frontend/packages/agent-ide/layout/tsconfig.json
Normal file
15
frontend/packages/agent-ide/layout/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.misc.json"
|
||||
}
|
||||
],
|
||||
"exclude": ["**/*"]
|
||||
}
|
||||
18
frontend/packages/agent-ide/layout/tsconfig.misc.json
Normal file
18
frontend/packages/agent-ide/layout/tsconfig.misc.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"include": ["__tests__", "stories", "vitest.config.ts", "tailwind.config.ts"],
|
||||
"exclude": ["./dist"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./",
|
||||
"outDir": "./dist",
|
||||
"types": ["vitest/globals"],
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": true
|
||||
}
|
||||
}
|
||||
22
frontend/packages/agent-ide/layout/vitest.config.ts
Normal file
22
frontend/packages/agent-ide/layout/vitest.config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineConfig } from '@coze-arch/vitest-config';
|
||||
|
||||
export default defineConfig({
|
||||
dirname: __dirname,
|
||||
preset: 'web',
|
||||
});
|
||||
Reference in New Issue
Block a user