feat: Support for Chat Flow & Agent Support for binding a single chat flow (#765)
Co-authored-by: Yu Yang <72337138+tomasyu985@users.noreply.github.com> Co-authored-by: zengxiaohui <csu.zengxiaohui@gmail.com> Co-authored-by: lijunwen.gigoo <lijunwen.gigoo@bytedance.com> Co-authored-by: lvxinyu.1117 <lvxinyu.1117@bytedance.com> Co-authored-by: liuyunchao.0510 <liuyunchao.0510@bytedance.com> Co-authored-by: haozhenfei <37089575+haozhenfei@users.noreply.github.com> Co-authored-by: July <jiangxujin@bytedance.com> Co-authored-by: tecvan-fe <fanwenjie.fe@bytedance.com>
This commit is contained in:
@@ -33,6 +33,12 @@ const mergedConfig = defineConfig({
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
},
|
||||
{
|
||||
context: ['/v1'],
|
||||
target: API_PROXY_TARGET,
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
html: {
|
||||
|
||||
@@ -21,9 +21,9 @@ import { pluginSass } from '@rsbuild/plugin-sass';
|
||||
import { pluginReact } from '@rsbuild/plugin-react';
|
||||
import { pluginLess } from '@rsbuild/plugin-less';
|
||||
import { type RsbuildConfig, mergeRsbuildConfig } from '@rsbuild/core';
|
||||
import { SemiRspackPlugin } from '@douyinfe/semi-rspack-plugin';
|
||||
import { PkgRootWebpackPlugin } from '@coze-arch/pkg-root-webpack-plugin';
|
||||
import { GLOBAL_ENVS } from '@coze-arch/bot-env';
|
||||
import { SemiRspackPlugin } from '@douyinfe/semi-rspack-plugin';
|
||||
|
||||
const getDefine = () => {
|
||||
const define = {};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"exports": {
|
||||
".": "./src/index.js",
|
||||
"./coze": "./src/coze.js",
|
||||
"./util": "./src/util.js",
|
||||
"./design-token": "./src/design-token.ts"
|
||||
},
|
||||
"main": "src/index.js",
|
||||
|
||||
282
frontend/config/tailwind-config/src/util.js
Normal file
282
frontend/config/tailwind-config/src/util.js
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const plugin = require('tailwindcss/plugin');
|
||||
|
||||
const lightModeVariables = require('./light');
|
||||
const darkModeVariables = require('./dark');
|
||||
|
||||
// 用于生成 CSS 变量的帮助函数
|
||||
function generateCssVariables(variables, theme) {
|
||||
return Object.entries(variables).reduce((acc, [key, value]) => {
|
||||
acc[`--${key}`] = theme ? theme(value) : value;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// 样式语义化
|
||||
function generateSemanticVariables(semantics, theme, property) {
|
||||
return Object.entries(semantics).map(([key, value]) => ({
|
||||
[`.${key}`]: {
|
||||
[property]: theme(value),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
const semanticForeground = {
|
||||
/* Theme */
|
||||
'coz-fg-hglt-plus': 'colors.foreground.5',
|
||||
'coz-fg-hglt-plus-dim': 'colors.foreground.5',
|
||||
'coz-fg-hglt': 'colors.brand.5',
|
||||
'coz-fg-hglt-dim': 'colors.brand.3',
|
||||
'coz-fg-plus': 'colors.foreground.4',
|
||||
'coz-fg': 'colors.foreground.3',
|
||||
'coz-fg-primary': 'colors.foreground.3',
|
||||
'coz-fg-secondary': 'colors.foreground.2',
|
||||
'coz-fg-dim': 'colors.foreground.1',
|
||||
'coz-fg-white': 'colors.foreground.7',
|
||||
'coz-fg-white-dim': 'colors.foreground.white',
|
||||
'coz-fg-hglt-ai': 'colors.purple.5',
|
||||
'coz-fg-hglt-ai-dim': 'colors.purple.3',
|
||||
/* Functional Color */
|
||||
'coz-fg-hglt-red': 'colors.red.5',
|
||||
'coz-fg-hglt-red-dim': 'colors.red.3',
|
||||
'coz-fg-hglt-yellow': 'colors.yellow.5',
|
||||
'coz-fg-hglt-yellow-dim': 'colors.yellow.3',
|
||||
'coz-fg-hglt-green': 'colors.green.5',
|
||||
'coz-fg-hglt-green-dim': 'colors.green.3',
|
||||
/* Chart, Tag Only */
|
||||
'coz-fg-color-orange': 'colors.yellow.5',
|
||||
'coz-fg-color-orange-dim': 'colors.yellow.3',
|
||||
'coz-fg-color-emerald': 'colors.green.5',
|
||||
'coz-fg-color-emerald-dim': 'colors.green.3',
|
||||
'coz-fg-color-cyan': 'colors.cyan.50',
|
||||
'coz-fg-color-cyan-dim': 'colors.cyan.30',
|
||||
'coz-fg-color-blue': 'colors.blue.50',
|
||||
'coz-fg-color-blue-dim': 'colors.blue.30',
|
||||
'coz-fg-color-purple': 'colors.purple.50',
|
||||
'coz-fg-color-purple-dim': 'colors.purple.30',
|
||||
'coz-fg-color-magenta': 'colors.magenta.50',
|
||||
'coz-fg-color-magenta-dim': 'colors.magenta.3',
|
||||
'coz-fg-color-yellow': 'colors.yellow.50',
|
||||
'coz-fg-color-yellow-dim': 'colors.yellow.30',
|
||||
/* Code Only */
|
||||
'coz-fg-hglt-orange': 'colors.orange.5',
|
||||
'coz-fg-hglt-orange-dim': 'colors.orange.3',
|
||||
'coz-fg-hglt-emerald': 'colors.emerald.5',
|
||||
'coz-fg-hglt-emerald-dim': 'colors.emerald.3',
|
||||
'coz-fg-hglt-cyan': 'colors.cyan.5',
|
||||
'coz-fg-hglt-cyan-dim': 'colors.cyan.3',
|
||||
'coz-fg-hglt-blue': 'colors.blue.5',
|
||||
'coz-fg-hglt-blue-dim': 'colors.blue.3',
|
||||
'coz-fg-hglt-purple': 'colors.purple.5',
|
||||
'coz-fg-hglt-purple-dim': 'colors.purple.3',
|
||||
'coz-fg-hglt-magenta': 'colors.magenta.5',
|
||||
'coz-fg-hglt-magenta-dim': 'colors.magenta.3',
|
||||
/* branding Only */
|
||||
'coz-fg-color-brand': 'colors.brand.50',
|
||||
'coz-fg-color-brand-dim': 'colors.brand.30',
|
||||
'coz-fg-color-alternative': 'colors.alternative.50',
|
||||
'coz-fg-color-alternative-dim': 'colors.alternative.30',
|
||||
};
|
||||
|
||||
const semanticMiddleground = {
|
||||
/* Theme */
|
||||
'coz-mg-hglt-plus-pressed': 'colors.brand.7',
|
||||
'coz-mg-hglt-plus-hovered': 'colors.brand.6',
|
||||
'coz-mg-hglt-plus': 'colors.brand.5',
|
||||
'coz-mg-hglt-plus-dim': 'colors.brand.3',
|
||||
'coz-mg-hglt-secondary-pressed': 'colors.brand.2',
|
||||
'coz-mg-hglt-secondary-hovered': 'colors.brand.1',
|
||||
'coz-mg-hglt-secondary': 'colors.brand.0',
|
||||
'coz-mg-hglt-secondary-red': 'colors.red.0',
|
||||
'coz-mg-hglt-secondary-yellow': 'colors.yellow.0',
|
||||
'coz-mg-hglt-secondary-green': 'colors.green.0',
|
||||
'coz-mg-plus-pressed': 'colors.background.8',
|
||||
'coz-mg-plus-hovered': 'colors.background.7',
|
||||
'coz-mg-plus': 'colors.background.6',
|
||||
'coz-mg-hglt-pressed': 'colors.brand.3',
|
||||
'coz-mg-hglt-hovered': 'colors.brand.2',
|
||||
'coz-mg-hglt-plus-ai-pressed': 'colors.purple.7',
|
||||
'coz-mg-hglt-plus-ai-hovered': 'colors.purple.6',
|
||||
'coz-mg-hglt-plus-ai': 'colors.purple.5',
|
||||
'coz-mg-hglt-plus-ai-dim': 'colors.purple.3',
|
||||
'coz-mg-hglt': 'colors.brand.1',
|
||||
'coz-mg-hglt-ai-pressed': 'colors.purple.3',
|
||||
'coz-mg-hglt-ai-hovered': 'colors.purple.2',
|
||||
'coz-mg-hglt-ai': 'colors.purple.1',
|
||||
/* Functional Color */
|
||||
'coz-mg-hglt-plus-red-pressed': 'colors.red.7',
|
||||
'coz-mg-hglt-plus-red-hovered': 'colors.red.6',
|
||||
'coz-mg-hglt-plus-red': 'colors.red.5',
|
||||
'coz-mg-hglt-plus-red-dim': 'colors.red.3',
|
||||
'coz-mg-hglt-plus-yellow-pressed': 'colors.yellow.7',
|
||||
'coz-mg-hglt-plus-yellow-hovered': 'colors.yellow.6',
|
||||
'coz-mg-hglt-plus-yellow': 'colors.yellow.5',
|
||||
'coz-mg-hglt-plus-yellow-dim': 'colors.yellow.3',
|
||||
'coz-mg-hglt-plus-green-pressed': 'colors.green.7',
|
||||
'coz-mg-hglt-plus-green-hovered': 'colors.green.6',
|
||||
'coz-mg-hglt-plus-green': 'colors.green.5',
|
||||
'coz-mg-hglt-plus-green-dim': 'colors.green.3',
|
||||
'coz-mg-hglt-red-pressed': 'colors.red.3',
|
||||
'coz-mg-hglt-red-hovered': 'colors.red.2',
|
||||
'coz-mg-hglt-red': 'colors.red.1',
|
||||
'coz-mg-hglt-yellow-pressed': 'colors.yellow.3',
|
||||
'coz-mg-hglt-yellow-hovered': 'colors.yellow.2',
|
||||
'coz-mg-hglt-yellow': 'colors.yellow.1',
|
||||
'coz-mg-hglt-green-pressed': 'colors.green.3',
|
||||
'coz-mg-hglt-green-hovered': 'colors.green.2',
|
||||
'coz-mg-hglt-green': 'colors.green.1',
|
||||
/* Card, Tag, Avatar Only */
|
||||
'coz-mg-color-plus-orange': 'colors.yellow.5',
|
||||
'coz-mg-color-plus-emerald': 'colors.green.5',
|
||||
'coz-mg-color-plus-cyan': 'colors.cyan.50',
|
||||
'coz-mg-color-plus-blue': 'colors.blue.50',
|
||||
'coz-mg-color-plus-purple': 'colors.purple.50',
|
||||
'coz-mg-color-plus-magenta': 'colors.magenta.50',
|
||||
'coz-mg-color-plus-yellow': 'colors.yellow.50',
|
||||
'coz-mg-color-orange-pressed': 'colors.yellow.3',
|
||||
'coz-mg-color-orange-hovered': 'colors.yellow.2',
|
||||
'coz-mg-color-orange': 'colors.yellow.1',
|
||||
'coz-mg-color-emerald-pressed': 'colors.green.3',
|
||||
'coz-mg-color-emerald-hovered': 'colors.green.2',
|
||||
'coz-mg-color-emerald': 'colors.green.1',
|
||||
'coz-mg-color-cyan-pressed': 'colors.cyan.30',
|
||||
'coz-mg-color-cyan-hovered': 'colors.cyan.20',
|
||||
'coz-mg-color-cyan': 'colors.cyan.10',
|
||||
'coz-mg-color-blue-pressed': 'colors.blue.30',
|
||||
'coz-mg-color-blue-hovered': 'colors.blue.20',
|
||||
'coz-mg-color-blue': 'colors.blue.10',
|
||||
'coz-mg-color-purple-pressed': 'colors.purple.30',
|
||||
'coz-mg-color-purple-hovered': 'colors.purple.20',
|
||||
'coz-mg-color-purple': 'colors.purple.10',
|
||||
'coz-mg-color-magenta-pressed': 'colors.magenta.30',
|
||||
'coz-mg-color-magenta-hovered': 'colors.magenta.20',
|
||||
'coz-mg-color-magenta': 'colors.magenta.10',
|
||||
'coz-mg-primary-pressed': 'colors.background.7',
|
||||
'coz-mg-primary-hovered': 'colors.background.6',
|
||||
'coz-mg-primary': 'colors.background.5',
|
||||
'coz-mg-secondary-pressed': 'colors.background.6',
|
||||
'coz-mg-secondary-hovered': 'colors.background.5',
|
||||
'coz-mg-secondary': 'colors.background.4',
|
||||
'coz-mg': 'colors.background.4',
|
||||
'coz-mg-mask': 'colors.mask.5',
|
||||
'coz-mg-table-fixed-hovered': 'colors.background.0',
|
||||
'coz-mg-card-pressed': 'colors.background.3',
|
||||
'coz-mg-card-hovered': 'colors.background.3',
|
||||
'coz-mg-card': 'colors.background.3',
|
||||
/** brand */
|
||||
'coz-mg-color-plus-brand': 'colors.brand.50',
|
||||
};
|
||||
|
||||
const semanticBackground = {
|
||||
'coz-bg-max': 'colors.background.3',
|
||||
'coz-bg-plus': 'colors.background.2',
|
||||
'coz-bg-primary': 'colors.background.1',
|
||||
'coz-bg': 'colors.background.1',
|
||||
'coz-bg-secondary': 'colors.background.0',
|
||||
};
|
||||
|
||||
const semanticShadow = {
|
||||
'coz-shadow': 'boxShadow.normal',
|
||||
'coz-shadow-large': 'boxShadow.large',
|
||||
'coz-shadow-default': 'boxShadow.normal',
|
||||
'coz-shadow-small': 'boxShadow.small',
|
||||
};
|
||||
|
||||
// Add button rounded definitions
|
||||
const buttonRounded = {
|
||||
'coz-btn-rounded-large': 'btnBorderRadius.large',
|
||||
'coz-btn-rounded-normal': 'btnBorderRadius.normal',
|
||||
'coz-btn-rounded-small': 'btnBorderRadius.small',
|
||||
'coz-btn-rounded-mini': 'btnBorderRadius.mini',
|
||||
};
|
||||
|
||||
const inputRounded = {
|
||||
'coz-input-rounded-large': 'inputBorderRadius.large',
|
||||
'coz-input-rounded-normal': 'inputBorderRadius.normal',
|
||||
'coz-input-rounded-small': 'inputBorderRadius.small',
|
||||
};
|
||||
|
||||
const inputHeight = {
|
||||
'coz-input-height-large': 'inputHeight.large',
|
||||
'coz-input-height-normal': 'inputHeight.normal',
|
||||
'coz-input-height-small': 'inputHeight.small',
|
||||
};
|
||||
|
||||
const semanticStroke = {
|
||||
'coz-stroke-hglt': 'colors.brand.5',
|
||||
'coz-stroke-plus': 'colors.stroke.6',
|
||||
'coz-stroke-primary': 'colors.stroke.5',
|
||||
'coz-stroke-hglt-red': 'colors.red.5',
|
||||
'coz-stroke-hglt-yellow': 'colors.yellow.5',
|
||||
'coz-stroke-hglt-green': 'colors.green.5',
|
||||
'coz-stroke-color-orange': 'colors.yellow.5',
|
||||
'coz-stroke-color-emerald': 'colors.green.5',
|
||||
'coz-stroke-color-cyan': 'colors.cyan.50',
|
||||
'coz-stroke-color-blue': 'colors.blue.50',
|
||||
'coz-stroke-color-purple': 'colors.purple.50',
|
||||
'coz-stroke-color-magenta': 'colors.magenta.50',
|
||||
'coz-stroke-color-yellow': 'colors.yellow.50',
|
||||
'coz-stroke-color-brand': 'colors.brand.50',
|
||||
'coz-stroke-opaque': 'colors.stroke.opaque',
|
||||
'coz-stroke-max': 'colors.stroke.max',
|
||||
};
|
||||
|
||||
function genTailwindPlugin(defaultCls, darkCls) {
|
||||
return plugin(function ({ addBase, addUtilities, theme }) {
|
||||
addBase({
|
||||
[defaultCls]: generateCssVariables(lightModeVariables),
|
||||
[darkCls]: generateCssVariables(darkModeVariables),
|
||||
});
|
||||
|
||||
addBase({
|
||||
[defaultCls]: {
|
||||
...generateCssVariables(semanticForeground, theme),
|
||||
...generateCssVariables(semanticMiddleground, theme),
|
||||
...generateCssVariables(semanticBackground, theme),
|
||||
...generateCssVariables(semanticStroke, theme),
|
||||
...generateCssVariables(semanticShadow, theme),
|
||||
...generateCssVariables(buttonRounded, theme),
|
||||
...generateCssVariables(inputRounded, theme),
|
||||
...generateCssVariables(inputHeight, theme),
|
||||
},
|
||||
});
|
||||
|
||||
addUtilities([
|
||||
...generateSemanticVariables(semanticForeground, theme, 'color'),
|
||||
...generateSemanticVariables(
|
||||
semanticMiddleground,
|
||||
theme,
|
||||
'background-color',
|
||||
),
|
||||
...generateSemanticVariables(
|
||||
semanticBackground,
|
||||
theme,
|
||||
'background-color',
|
||||
),
|
||||
...generateSemanticVariables(semanticStroke, theme, 'border-color'),
|
||||
...generateSemanticVariables(semanticShadow, theme, 'box-shadow'),
|
||||
...generateSemanticVariables(buttonRounded, theme, 'border-radius'),
|
||||
...generateSemanticVariables(inputRounded, theme, 'border-radius'),
|
||||
...generateSemanticVariables(inputHeight, theme, 'height'),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
genTailwindPlugin,
|
||||
};
|
||||
@@ -129,10 +129,8 @@ export const BotHeader: React.FC<BotHeaderProps> = props => {
|
||||
editBotInfoFn={editBotInfoFn}
|
||||
deployButton={props.deployButton}
|
||||
/>
|
||||
{/** mode selector */}
|
||||
{diffTask || IS_OPEN_SOURCE ? null : (
|
||||
<ModeSelect optionList={props.modeOptionList} />
|
||||
)}
|
||||
{/** 模式选择器 */}
|
||||
{diffTask ? null : <ModeSelect optionList={props.modeOptionList} />}
|
||||
</div>
|
||||
|
||||
{/* 2. Middle bot menu area - offline */}
|
||||
|
||||
@@ -45,4 +45,6 @@ function SwitchWithDesc({
|
||||
);
|
||||
}
|
||||
|
||||
export const FormSwitch = withField(SwitchWithDesc);
|
||||
export const FormSwitch = withField(SwitchWithDesc) as ReturnType<
|
||||
typeof withField
|
||||
>;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"maintainers": [],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./configs": "./src/configs.ts",
|
||||
"./build": "./scripts/build.ts",
|
||||
"./typings": "./src/typings.d.ts",
|
||||
"./runtime": "./src/runtime/index.ts"
|
||||
@@ -14,6 +15,9 @@
|
||||
"main": "src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"configs": [
|
||||
"./src/configs.ts"
|
||||
],
|
||||
"build": [
|
||||
"./scripts/build.ts"
|
||||
],
|
||||
|
||||
@@ -3769,5 +3769,29 @@
|
||||
"workspace_develop": "Development",
|
||||
"workspace_develop_search_project": "Search for projects",
|
||||
"workspace_library_search": "Search resources",
|
||||
"workspace_no_permission_access": "No permission to access this workspace."
|
||||
"workspace_no_permission_access": "No permission to access this workspace.",
|
||||
"web_sdk_add_new_conversation": "Create a new conversation",
|
||||
"store_start_new_chat": "Add chat",
|
||||
"sendFailed": "Send failed",
|
||||
"chat_voice_input_need_focus": "Click to activate input",
|
||||
"web_sdk_create_conversation": "A new session has been created",
|
||||
"web_sdk_conversation_history": "conversation history",
|
||||
"web_sdk_conversation_default_name": "Newly created session",
|
||||
"web_sdk_delete": "delete",
|
||||
"web_sdk_delete_conversation": "Delete session",
|
||||
"web_sdk_rename_conversation": "rename session",
|
||||
"web_sdk_confirm": "OK",
|
||||
"web_sdk_cancel": "cancel",
|
||||
"web_sdk_open_conversations": "Expand Sidebar",
|
||||
"unbind_notification": "Notification: Agent has been unbinded.",
|
||||
"profile_history_today": "Today",
|
||||
"log_pay_wall_date_filter_30_days": "Past 30 days",
|
||||
"web_sdk_past": "past",
|
||||
"404_title": "Sorry, this page doesn't exist",
|
||||
"404_content": "Please check your link or try again",
|
||||
"overview_bi_assistant_system_error": "System error. Please try again later.",
|
||||
"web_sdk_retry_notification": "Please try again later.",
|
||||
"chatInputPlaceholder": "Send message",
|
||||
"web_sdk_official_banner": "Powered by {docs_link}. AI-generated content for reference only.",
|
||||
"web_sdk_official_banner_link": "coze"
|
||||
}
|
||||
|
||||
@@ -3817,5 +3817,29 @@
|
||||
"workspace_develop": "项目开发",
|
||||
"workspace_develop_search_project": "搜索项目",
|
||||
"workspace_library_search": "搜索资源",
|
||||
"workspace_no_permission_access": "无法查看空间"
|
||||
"workspace_no_permission_access": "无法查看空间",
|
||||
"web_sdk_add_new_conversation": "创建新会话",
|
||||
"store_start_new_chat": "新增对话",
|
||||
"sendFailed": "发送失败",
|
||||
"chat_voice_input_need_focus": "点击当前对话区激活输入框",
|
||||
"web_sdk_create_conversation": "已创建新会话",
|
||||
"web_sdk_conversation_history": "会话历史",
|
||||
"web_sdk_conversation_default_name": "新创建的会话",
|
||||
"web_sdk_delete": "删除",
|
||||
"web_sdk_delete_conversation": "删除会话",
|
||||
"web_sdk_rename_conversation": "重命名会话",
|
||||
"web_sdk_confirm": "确定",
|
||||
"web_sdk_cancel": "取消",
|
||||
"web_sdk_open_conversations": "展开侧栏",
|
||||
"unbind_notification": "提示:智能体已经被解绑",
|
||||
"profile_history_today": "今天",
|
||||
"log_pay_wall_date_filter_30_days": "过去30天",
|
||||
"web_sdk_past": "过往",
|
||||
"404_title": "抱歉,该页面不存在",
|
||||
"404_content": "请检查链接或重试",
|
||||
"overview_bi_assistant_system_error": "系统错误,请稍后再试",
|
||||
"web_sdk_retry_notification": "请稍后重试。",
|
||||
"chatInputPlaceholder": "发送消息",
|
||||
"web_sdk_official_banner": "由 {docs_link} 提供支持,AI生成仅供参考",
|
||||
"web_sdk_official_banner_link": "扣子"
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type PropsWithChildren } from 'react';
|
||||
import { forwardRef, type PropsWithChildren } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
@@ -25,14 +25,16 @@ interface ActionBarHoverContainerProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export const ActionBarHoverContainer: React.FC<
|
||||
export const ActionBarHoverContainer = forwardRef<
|
||||
HTMLDivElement,
|
||||
PropsWithChildren<ActionBarHoverContainerProps>
|
||||
> = ({ children, style }) => (
|
||||
>(({ children, style }, ref) => (
|
||||
<div
|
||||
data-testid="chat-area.answer-action.hover-action-bar"
|
||||
className={classNames(s.container, ['coz-stroke-primary', 'coz-bg-max'])}
|
||||
style={style}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
));
|
||||
|
||||
@@ -34,15 +34,27 @@ import { useTooltipTrigger } from '../../hooks/use-tooltip-trigger';
|
||||
type CopyTextMessageProps = Omit<
|
||||
ComponentProps<typeof IconButton>,
|
||||
'icon' | 'iconSize' | 'onClick'
|
||||
>;
|
||||
> & {
|
||||
isMustGroupLastAnswerMessage?: boolean;
|
||||
isUseExternalContent?: boolean;
|
||||
externalContent?: string;
|
||||
};
|
||||
|
||||
export const CopyTextMessage: React.FC<
|
||||
PropsWithChildren<CopyTextMessageProps>
|
||||
> = ({ className, ...props }) => {
|
||||
> = ({
|
||||
className,
|
||||
isMustGroupLastAnswerMessage = true,
|
||||
isUseExternalContent = false,
|
||||
externalContent,
|
||||
...props
|
||||
}) => {
|
||||
const { reporter } = useChatArea();
|
||||
const { message, meta } = useMessageBoxContext();
|
||||
|
||||
const { content } = message;
|
||||
const content = isUseExternalContent
|
||||
? externalContent || ''
|
||||
: message.content;
|
||||
|
||||
const [isCopySuccessful, setIsCopySuccessful] = useState<boolean>(false);
|
||||
const trigger = useTooltipTrigger('hover');
|
||||
@@ -87,7 +99,7 @@ export const CopyTextMessage: React.FC<
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!meta.isGroupLastAnswerMessage) {
|
||||
if (!meta.isGroupLastAnswerMessage && isMustGroupLastAnswerMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./tsconfig.build.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"sourceMap": false,
|
||||
"outDir": null,
|
||||
"tsBuildInfoFile": null
|
||||
},
|
||||
"exclude": ["dist", "node_modules", "__tests__"]
|
||||
}
|
||||
@@ -9,6 +9,9 @@
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.misc.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.dev.json"
|
||||
}
|
||||
],
|
||||
"exclude": ["**/*"]
|
||||
|
||||
@@ -222,6 +222,7 @@ export const ChatArea = forwardRef<ChatAreaRef, ChatAreaProps>((props, ref) => {
|
||||
selectable,
|
||||
showClearContextDivider,
|
||||
messageWidth,
|
||||
messageMaxWidth,
|
||||
readonly,
|
||||
uiKitChatInputButtonConfig,
|
||||
uikitChatInputButtonStatus,
|
||||
@@ -237,6 +238,7 @@ export const ChatArea = forwardRef<ChatAreaRef, ChatAreaProps>((props, ref) => {
|
||||
isOnboardingCentered,
|
||||
fileLimit,
|
||||
stopRespondOverrideWaiting,
|
||||
isMiniScreen,
|
||||
} = props;
|
||||
const getScrollViewRef = useRef<() => ScrollViewController>(null);
|
||||
const {
|
||||
@@ -278,6 +280,7 @@ export const ChatArea = forwardRef<ChatAreaRef, ChatAreaProps>((props, ref) => {
|
||||
enableSelectOnboarding,
|
||||
showClearContextDivider,
|
||||
messageWidth,
|
||||
messageMaxWidth,
|
||||
readonly: readonly || isClearMessageHistoryLock,
|
||||
uiKitChatInputButtonConfig,
|
||||
theme,
|
||||
@@ -304,6 +307,7 @@ export const ChatArea = forwardRef<ChatAreaRef, ChatAreaProps>((props, ref) => {
|
||||
onboardingSuggestionsShowMode,
|
||||
showBackground,
|
||||
stopRespondOverrideWaiting,
|
||||
isMiniScreen,
|
||||
}}
|
||||
>
|
||||
<ChatAreaMain
|
||||
|
||||
@@ -28,13 +28,6 @@ import {
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
useUIKitCustomComponent,
|
||||
ChatInput as UIKitChatInput,
|
||||
type InputRefObject,
|
||||
} from '@coze-common/chat-uikit';
|
||||
import { safeAsyncThrow } from '@coze-common/chat-area-utils';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
type IChatInputProps,
|
||||
UploadType,
|
||||
@@ -42,6 +35,13 @@ import {
|
||||
MAX_FILE_MBYTE,
|
||||
Layout,
|
||||
} from '@coze-common/chat-uikit-shared';
|
||||
import {
|
||||
useUIKitCustomComponent,
|
||||
ChatInput as UIKitChatInput,
|
||||
type InputRefObject,
|
||||
} from '@coze-common/chat-uikit';
|
||||
import { safeAsyncThrow } from '@coze-common/chat-area-utils';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import { BatchUploadFileList } from '../batch-upload-file-list';
|
||||
import { getSendMultimodalMessageStrategy } from '../../utils/message';
|
||||
@@ -158,6 +158,7 @@ export const ChatInput: <T extends OverrideProps>(
|
||||
wrapperClassName,
|
||||
inputNativeCallbacks,
|
||||
safeAreaClassName,
|
||||
...restInputProps
|
||||
} = useChatInputProps();
|
||||
|
||||
const showBackground = useShowBackGround();
|
||||
@@ -395,6 +396,7 @@ export const ChatInput: <T extends OverrideProps>(
|
||||
showBackground={showBackground}
|
||||
limitFileCount={fileLimit}
|
||||
onPaste={handlePaste}
|
||||
{...restInputProps}
|
||||
{...componentProps}
|
||||
/>
|
||||
<div
|
||||
|
||||
@@ -39,6 +39,7 @@ import { usePreference } from '../../../context/preference';
|
||||
import s from './index.module.less';
|
||||
|
||||
import './index.less';
|
||||
import { usePluginCustomComponents } from '../../../plugin/hooks/use-plugin-custom-components';
|
||||
|
||||
const BuiltinMessageGroupWrapper: ComponentTypesMap['messageGroupWrapper'] = ({
|
||||
children,
|
||||
@@ -135,6 +136,18 @@ export const MessageGroupWrapper: React.FC<
|
||||
|
||||
const showContextDividerWithOnboarding =
|
||||
showClearContextDividerByPreference && showContextDivider;
|
||||
const customMessageGroupFooterPlugin =
|
||||
usePluginCustomComponents('MessageGroupFooter').at(0);
|
||||
const renderFooter = () => {
|
||||
const usedFooter = customMessageGroupFooterPlugin;
|
||||
|
||||
if (!usedFooter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { Component } = usedFooter;
|
||||
return <Component messageGroup={messageGroup} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -152,6 +165,7 @@ export const MessageGroupWrapper: React.FC<
|
||||
isSendingMessage={isSendingMessage}
|
||||
messageGroup={messageGroup}
|
||||
>
|
||||
{renderFooter?.()}
|
||||
{isLatest ? (
|
||||
<>
|
||||
{!showContextDividerWithOnboarding && <SuggestionInChat />}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
import { type MouseEvent } from 'react';
|
||||
|
||||
import {
|
||||
type IEventCallbacks,
|
||||
type IOnLinkClickParams,
|
||||
} from '@coze-common/chat-uikit-shared';
|
||||
import { type MessageBoxTheme } from '@coze-common/chat-uikit';
|
||||
import {
|
||||
type ClearMessageContextParams,
|
||||
@@ -25,7 +29,6 @@ import {
|
||||
type ChatCoreError,
|
||||
type GetHistoryMessageResponse,
|
||||
} from '@coze-common/chat-core';
|
||||
import { type IOnLinkClickParams } from '@coze-common/chat-uikit-shared';
|
||||
|
||||
import {
|
||||
type Message as BuiltInMessage,
|
||||
@@ -150,6 +153,7 @@ export interface ChatAreaMessageEventMap {
|
||||
event: MouseEvent<Element, globalThis.MouseEvent>,
|
||||
) => void;
|
||||
onBeforeStopResponding: () => void;
|
||||
onCopyUpload: IEventCallbacks['onCopyUpload'];
|
||||
}
|
||||
|
||||
export type ChatAreaEventCallback = Partial<ChatAreaLifeCycleEventMap> &
|
||||
|
||||
@@ -22,4 +22,5 @@ export const defaultConfigs: ChatAreaConfigs = {
|
||||
ignoreMessageConfigList: [],
|
||||
groupUserMessage: false,
|
||||
uploadPlugin: UploadPlugin,
|
||||
isShowFunctionCallBox: true,
|
||||
};
|
||||
|
||||
@@ -106,6 +106,7 @@ export const allIgnorableMessageTypes = [
|
||||
export interface ChatAreaConfigs {
|
||||
ignoreMessageConfigList: IgnoreMessageType[];
|
||||
showFunctionCallDetail: boolean;
|
||||
isShowFunctionCallBox: boolean;
|
||||
// Whether to group user messages (merge avatars)
|
||||
groupUserMessage: boolean;
|
||||
uploadPlugin: typeof UploadPlugin;
|
||||
|
||||
@@ -23,7 +23,8 @@ import {
|
||||
|
||||
type OnBeforeSubmit = IChatInputProps['onBeforeSubmit'];
|
||||
|
||||
export interface ChatInputProps {
|
||||
export interface ChatInputProps
|
||||
extends Pick<IChatInputProps, 'leftActions' | 'rightSlot'> {
|
||||
/**
|
||||
* {@link OnBeforeSubmit}
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,7 @@ const getDefaultCopywriting = (): CopywritingContextInterface => ({
|
||||
textareaBottomTips: '',
|
||||
clearContextDividerText: '',
|
||||
clearContextTooltipContent: '',
|
||||
audioButtonTooltipContent: '',
|
||||
});
|
||||
|
||||
export const CopywritingContext = createContext<CopywritingContextInterface>(
|
||||
|
||||
@@ -19,4 +19,5 @@ export interface CopywritingContextInterface {
|
||||
textareaBottomTips: string;
|
||||
clearContextDividerText: string;
|
||||
clearContextTooltipContent: string;
|
||||
audioButtonTooltipContent: string;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
import { createContext, type PropsWithChildren, useContext } from 'react';
|
||||
|
||||
import { isUndefined, merge, omitBy } from 'lodash-es';
|
||||
import { Layout } from '@coze-common/chat-uikit-shared';
|
||||
import { type MakeValueUndefinable } from '@coze-common/chat-area-utils';
|
||||
import { SuggestedQuestionsShowMode } from '@coze-arch/bot-api/developer_api';
|
||||
import { Layout } from '@coze-common/chat-uikit-shared';
|
||||
|
||||
import {
|
||||
type PreferenceContextInterface,
|
||||
@@ -49,6 +49,7 @@ const getDefaultPreference = (): Required<PreferenceContextInterface> => ({
|
||||
selectable: false,
|
||||
showClearContextDivider: true,
|
||||
messageWidth: '100%',
|
||||
messageMaxWidth: '',
|
||||
readonly: false,
|
||||
uiKitChatInputButtonConfig: {
|
||||
isSendButtonVisible: true,
|
||||
@@ -68,6 +69,7 @@ const getDefaultPreference = (): Required<PreferenceContextInterface> => ({
|
||||
forceShowOnboardingMessage: false,
|
||||
showStopRespond: true,
|
||||
layout: Layout.PC,
|
||||
isMiniScreen: false,
|
||||
isOnboardingCentered: false,
|
||||
stopRespondOverrideWaiting: undefined,
|
||||
});
|
||||
|
||||
@@ -96,6 +96,10 @@ export interface PreferenceContextInterface {
|
||||
* message list width
|
||||
*/
|
||||
messageWidth: string;
|
||||
/**
|
||||
* message list max width
|
||||
*/
|
||||
messageMaxWidth: string;
|
||||
/**
|
||||
* Is it read-only?
|
||||
*/
|
||||
@@ -156,6 +160,11 @@ export interface PreferenceContextInterface {
|
||||
*/
|
||||
layout: Layout;
|
||||
|
||||
/**
|
||||
* Whether to enable mini screen mode
|
||||
*/
|
||||
isMiniScreen: boolean;
|
||||
|
||||
/**
|
||||
* Whether to force the stop reply button to be displayed
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { type PropsWithChildren, useRef, useEffect } from 'react';
|
||||
|
||||
import { UploadController } from '../../service/upload-controller';
|
||||
import { useChatAreaContext } from '../../hooks/context/use-chat-area-context';
|
||||
import {
|
||||
UploadControllerContext,
|
||||
type UploadControllerContextProps,
|
||||
@@ -25,12 +26,16 @@ import {
|
||||
export const UploadControllerProvider: React.FC<PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { configs } = useChatAreaContext();
|
||||
const uploadControllerMap = useRef<
|
||||
UploadControllerContextProps['uploadControllerMap']
|
||||
>({});
|
||||
const createControllerAndUpload: UploadControllerContextProps['createControllerAndUpload'] =
|
||||
param => {
|
||||
uploadControllerMap.current[param.fileId] = new UploadController(param);
|
||||
uploadControllerMap.current[param.fileId] = new UploadController({
|
||||
...param,
|
||||
multiUploadPlugin: configs.uploadPlugin,
|
||||
});
|
||||
};
|
||||
const cancelUploadById: UploadControllerContextProps['cancelUploadById'] =
|
||||
id => {
|
||||
|
||||
@@ -301,3 +301,5 @@ export {
|
||||
} from '@coze-common/chat-core';
|
||||
export { type OnboardingSelectChangeCallback } from './context/chat-area-context/chat-area-callback';
|
||||
export { ChatInputArea } from './components/chat-input';
|
||||
export { type ChatMessage } from '@coze-arch/bot-api/developer_api';
|
||||
export { useBuiltinButtonStatus } from './hooks/uikit/use-builtin-button-status';
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import { type ComponentType } from 'react';
|
||||
|
||||
import { type MessageGroup } from '../../../store/types';
|
||||
import { type MessageBoxProps } from '../../../components/types';
|
||||
import {
|
||||
type CustomSendMessageBox,
|
||||
@@ -44,6 +45,8 @@ export interface CustomComponent {
|
||||
MessageBox: ComponentType<MessageBoxProps>;
|
||||
MessageBoxFooter: CustomMessageBoxFooter;
|
||||
MessageBoxHoverSlot: ComponentType;
|
||||
MessageGroupFooter: ComponentType<{ messageGroup: MessageGroup }>;
|
||||
|
||||
UIKitMessageBoxPlugin: ComponentType<CustomUiKitMessageBoxProps>;
|
||||
UIKitOnBoardingPlugin: ComponentType;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface UploadControllerProps {
|
||||
fileId: string;
|
||||
file: File;
|
||||
userId: string;
|
||||
multiUploadPlugin?: typeof UploadPlugin;
|
||||
onProgress: (event: EventPayloadMap['progress'], fileId: string) => void;
|
||||
onComplete: (event: EventPayloadMap['complete'], fileId: string) => void;
|
||||
onError: (event: EventPayloadMap['error'], fileId: string) => void;
|
||||
@@ -39,9 +40,10 @@ export class UploadController {
|
||||
onComplete,
|
||||
onError,
|
||||
onReady,
|
||||
multiUploadPlugin = UploadPlugin,
|
||||
}: UploadControllerProps) {
|
||||
this.fileId = fileId;
|
||||
this.uploadPlugin = new UploadPlugin({
|
||||
this.uploadPlugin = new multiUploadPlugin({
|
||||
file,
|
||||
userId,
|
||||
type: isImage(file) ? 'image' : 'object',
|
||||
|
||||
@@ -42,7 +42,6 @@ import { MessageManager } from '@/message/message-manager';
|
||||
import { ChunkProcessor, PreSendLocalMessageFactory } from '@/message';
|
||||
import { HttpChunk } from '@/channel/http-chunk';
|
||||
|
||||
import { type TokenManager } from '../credential';
|
||||
import {
|
||||
type ChatASRParams,
|
||||
type BreakMessageParams,
|
||||
@@ -70,6 +69,7 @@ import { MessageManagerService } from './services/message-manager-service';
|
||||
import { HttpChunkService } from './services/http-chunk-service';
|
||||
import { CreateMessageService } from './services/create-message-service';
|
||||
import { ReportEventsTracer, SlardarEvents } from './events/slardar-events';
|
||||
import { type TokenManager } from '../credential';
|
||||
|
||||
export default class ChatSDK {
|
||||
private static instances: Map<string, ChatSDK> = new Map();
|
||||
@@ -523,4 +523,10 @@ export default class ChatSDK {
|
||||
}
|
||||
return this.messageManagerService.chatASR(params);
|
||||
}
|
||||
|
||||
updateConversationId(conversationId: string) {
|
||||
this.conversation_id = conversationId;
|
||||
this.messageManagerService.conversation_id = conversationId;
|
||||
this.preSendLocalMessageFactory.conversation_id = conversationId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ export class RequestManager {
|
||||
// Execute incoming unified hooks
|
||||
const onCommonAfterResponse = async (
|
||||
response: AxiosResponse,
|
||||
hooksName: 'onAfterResponse' | 'onErrrorResponse' = 'onAfterResponse',
|
||||
hooksName: 'onAfterResponse' | 'onErrorResponse' = 'onAfterResponse',
|
||||
): Promise<AxiosResponse> => {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- temporary variable, quite normal
|
||||
let _response: AxiosResponse | Promise<AxiosResponse> = response;
|
||||
@@ -154,7 +154,7 @@ export class RequestManager {
|
||||
// Execute hooks for each scene
|
||||
const onSceneAfterResponse = async (
|
||||
response: AxiosResponse,
|
||||
hooksName: 'onAfterResponse' | 'onErrrorResponse' = 'onAfterResponse',
|
||||
hooksName: 'onAfterResponse' | 'onErrorResponse' = 'onAfterResponse',
|
||||
): Promise<AxiosResponse> => {
|
||||
const { scenes } = this.mergedBaseOptions;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- temporary variable, quite normal
|
||||
@@ -188,9 +188,9 @@ export class RequestManager {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- temporary variable, quite normal
|
||||
const _response = await onCommonAfterResponse(
|
||||
response,
|
||||
'onErrrorResponse',
|
||||
'onErrorResponse',
|
||||
);
|
||||
return await onSceneAfterResponse(_response, 'onErrrorResponse');
|
||||
return await onSceneAfterResponse(_response, 'onErrorResponse');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ interface Hooks {
|
||||
onGetMessageStreamParser?: (
|
||||
requestMessageRawBody: Record<string, unknown>,
|
||||
) => FetchSteamConfig<ParsedEvent>['streamParser'];
|
||||
onErrrorResponse?: Array<(response: AxiosResponse) => Promise<AxiosResponse>>;
|
||||
onErrorResponse?: Array<(response: AxiosResponse) => Promise<AxiosResponse>>;
|
||||
}
|
||||
|
||||
export enum RequestScene {
|
||||
|
||||
@@ -127,10 +127,15 @@ export interface IChatInputProps {
|
||||
leftActions?: ReactNode;
|
||||
|
||||
/**
|
||||
* Right Slot
|
||||
* Right Actions
|
||||
*/
|
||||
rightActions?: ReactNode;
|
||||
|
||||
/**
|
||||
* right slot
|
||||
*/
|
||||
rightSlot?: ReactNode;
|
||||
|
||||
/**
|
||||
* Custom send button
|
||||
*/
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import {
|
||||
FILE_TYPE_CONFIG,
|
||||
FileTypeEnum,
|
||||
} from '@coze-common/chat-core/shared/const';
|
||||
import { Toast, Upload } from '@coze-arch/coze-design';
|
||||
import {
|
||||
type IChatUploadCopywritingConfig,
|
||||
DEFAULT_MAX_FILE_SIZE,
|
||||
UploadType,
|
||||
} from '@coze-common/chat-uikit-shared';
|
||||
import {
|
||||
FILE_TYPE_CONFIG,
|
||||
FileTypeEnum,
|
||||
} from '@coze-common/chat-core/shared/const';
|
||||
import { Toast, Upload } from '@coze-arch/coze-design';
|
||||
|
||||
interface IChatUploadProps {
|
||||
/**
|
||||
@@ -132,6 +132,7 @@ export const ChatUpload: FC<IChatUploadProps> = props => {
|
||||
onFileChange={handleUpload}
|
||||
disabled={isDisabled}
|
||||
multiple={limitFileCount > 1}
|
||||
uploadTrigger={'custom'}
|
||||
>
|
||||
{children}
|
||||
</Upload>
|
||||
|
||||
@@ -40,7 +40,8 @@ export const MessageBox: FC<
|
||||
|
||||
messageBubbleClassname,
|
||||
messageBubbleWrapperClassname,
|
||||
messageBoxWraperClassname,
|
||||
messageBoxWrapperClassname,
|
||||
messageHoverWrapperClassName,
|
||||
messageErrorWrapperClassname,
|
||||
isHoverShowUserInfo,
|
||||
|
||||
@@ -69,7 +70,8 @@ export const MessageBox: FC<
|
||||
classname={classname}
|
||||
messageBubbleWrapperClassname={messageBubbleWrapperClassname}
|
||||
messageBubbleClassname={messageBubbleClassname}
|
||||
messageBoxWraperClassname={messageBoxWraperClassname}
|
||||
messageBoxWrapperClassname={messageBoxWrapperClassname}
|
||||
messageHoverWrapperClassName={messageHoverWrapperClassName}
|
||||
messageErrorWrapperClassname={messageErrorWrapperClassname}
|
||||
isHoverShowUserInfo={isHoverShowUserInfo}
|
||||
layout={layout}
|
||||
|
||||
@@ -24,13 +24,13 @@ import {
|
||||
|
||||
import classnames from 'classnames';
|
||||
import { useClickAway, useHover, useUpdateEffect } from 'ahooks';
|
||||
import { ErrorBoundary } from '@coze-arch/logger';
|
||||
import {
|
||||
Layout,
|
||||
UIKitEvents,
|
||||
useUiKitEventCenter,
|
||||
} from '@coze-common/chat-uikit-shared';
|
||||
import { useEventCallback } from '@coze-common/chat-hooks';
|
||||
import { ErrorBoundary } from '@coze-arch/logger';
|
||||
import { Avatar, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
import { UserLabel, UserName } from '../user-label';
|
||||
@@ -67,7 +67,8 @@ export const MessageBoxWrap: FC<
|
||||
classname,
|
||||
messageBubbleClassname,
|
||||
messageBubbleWrapperClassname,
|
||||
messageBoxWraperClassname,
|
||||
messageBoxWrapperClassname,
|
||||
messageHoverWrapperClassName,
|
||||
messageErrorWrapperClassname,
|
||||
isHoverShowUserInfo = true,
|
||||
layout,
|
||||
@@ -165,7 +166,7 @@ export const MessageBoxWrap: FC<
|
||||
// chat-uikit-message-box-container chat-uikit-message-box-container-pc
|
||||
className={classnames(
|
||||
messageBoxContainerVariants({ isMobileLayout }),
|
||||
messageBoxWraperClassname,
|
||||
messageBoxWrapperClassname,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -292,6 +293,17 @@ export const MessageBoxWrap: FC<
|
||||
>
|
||||
{right}
|
||||
</div>
|
||||
{isHovering || hoverContentVisible ? (
|
||||
<div
|
||||
// chat-uikit-message-box-container__message__message-box__hover-container
|
||||
className={classnames(
|
||||
'absolute right-[-12px] bottom-[-20px]',
|
||||
messageHoverWrapperClassName,
|
||||
)}
|
||||
>
|
||||
{hoverContent}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{/* Please read the refreshContainerWidthConditionally above before changing the style of this dom */}
|
||||
<div
|
||||
@@ -301,14 +313,6 @@ export const MessageBoxWrap: FC<
|
||||
>
|
||||
{renderFooter?.(refreshContainerWidthConditionally)}
|
||||
</div>
|
||||
{isHovering || hoverContentVisible ? (
|
||||
<div
|
||||
// chat-uikit-message-box-container__message__message-box__hover-container
|
||||
className="absolute right-[-12px] bottom-[-20px]"
|
||||
>
|
||||
{hoverContent}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -91,7 +91,8 @@ interface MessageBoxBasicProps {
|
||||
classname?: string;
|
||||
|
||||
messageBubbleWrapperClassname?: string;
|
||||
messageBoxWraperClassname?: string; // Direct father style of message box
|
||||
messageBoxWrapperClassname?: string; // Direct father style of message box
|
||||
messageHoverWrapperClassName?: string; // Direct hover style of message box
|
||||
messageBubbleClassname?: string; // Message The style of the message bubble
|
||||
messageErrorWrapperClassname?: string; // Message wrong father style
|
||||
isHoverShowUserInfo?: boolean; // Whether to display user details when hovering
|
||||
@@ -160,7 +161,9 @@ export interface MessageBoxWrapProps {
|
||||
contentTime: number | undefined;
|
||||
classname?: string;
|
||||
|
||||
messageBoxWraperClassname?: string; // Direct father style of message box
|
||||
messageBoxWrapperClassname?: string; // Direct father style of message box
|
||||
messageHoverWrapperClassName?: string; // Direct hover style of message box
|
||||
|
||||
messageBubbleClassname?: string; // Message The style of the message bubble
|
||||
messageBubbleWrapperClassname?: string; // Message message bubble father style
|
||||
messageErrorWrapperClassname?: string; // Message wrong father style
|
||||
|
||||
@@ -54,7 +54,6 @@ export const TextContent: FC<IMessageContentProps> = props => {
|
||||
} = props;
|
||||
const MdBoxLazy = LazyCozeMdBox;
|
||||
const contentRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const { content } = message;
|
||||
|
||||
if (!isText(content)) {
|
||||
@@ -63,7 +62,6 @@ export const TextContent: FC<IMessageContentProps> = props => {
|
||||
|
||||
const isStreaming = !message.is_finish;
|
||||
const text = content.slice(0, message.broken_pos ?? Infinity);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="chat-uikit-text-content"
|
||||
|
||||
@@ -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 { memo, useMemo } from 'react';
|
||||
|
||||
import { isEqual, isFunction, omitBy } from 'lodash-es';
|
||||
|
||||
import { extractChatflowMessage } from './utils';
|
||||
import { type ChatflowNodeData, type RenderNodeEntryProps } from './type';
|
||||
import { QuestionNodeRender } from './question-node-render';
|
||||
import { InputNodeRender } from './input-node-render';
|
||||
|
||||
const BaseComponent: React.FC<RenderNodeEntryProps> = ({
|
||||
message,
|
||||
...restProps
|
||||
}) => {
|
||||
const chatflowNodeData: ChatflowNodeData | undefined = useMemo(
|
||||
() => extractChatflowMessage(message),
|
||||
[message],
|
||||
);
|
||||
if (!chatflowNodeData) {
|
||||
return null;
|
||||
}
|
||||
if (chatflowNodeData.card_type === 'INPUT') {
|
||||
return (
|
||||
<InputNodeRender
|
||||
data={chatflowNodeData}
|
||||
message={message}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
} else if (chatflowNodeData.card_type === 'QUESTION') {
|
||||
return (
|
||||
<QuestionNodeRender
|
||||
data={chatflowNodeData}
|
||||
message={message}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return 'content type is not supported';
|
||||
}
|
||||
};
|
||||
|
||||
export const WorkflowRenderEntry = memo(BaseComponent, (prevProps, nextProps) =>
|
||||
isEqual(omitBy(prevProps, isFunction), omitBy(nextProps, isFunction)),
|
||||
);
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 { produce } from 'immer';
|
||||
import {
|
||||
type IEventCallbacks,
|
||||
type IMessage,
|
||||
} from '@coze-common/chat-uikit-shared';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button, Input, Space, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
import { type ChatflowNodeData } from './type';
|
||||
import { NodeWrapperUI } from './node-wrapper-ui';
|
||||
|
||||
export const InputNodeRender = ({
|
||||
data,
|
||||
onCardSendMsg,
|
||||
readonly,
|
||||
isDisable,
|
||||
message,
|
||||
}: {
|
||||
data: ChatflowNodeData;
|
||||
onCardSendMsg?: IEventCallbacks['onCardSendMsg'];
|
||||
readonly?: boolean;
|
||||
isDisable?: boolean;
|
||||
message: IMessage;
|
||||
}) => {
|
||||
const [inputData, setInputData] = useState<Record<string, string>>({});
|
||||
const [hasSend, setHasSend] = useState(false);
|
||||
const disabled = readonly || isDisable || hasSend;
|
||||
|
||||
return (
|
||||
<NodeWrapperUI>
|
||||
<Space spacing={12} vertical className="w-full">
|
||||
{data.input_card_data?.map((item, index) => (
|
||||
<Space
|
||||
align="start"
|
||||
className="w-full"
|
||||
spacing={6}
|
||||
vertical
|
||||
key={item?.name + index}
|
||||
>
|
||||
<Typography.Text ellipsis className="text-lg !font-medium">
|
||||
{item?.name}
|
||||
</Typography.Text>
|
||||
<Input
|
||||
disabled={disabled || hasSend}
|
||||
value={inputData[item.name]}
|
||||
onChange={value => {
|
||||
setInputData(
|
||||
produce(draft => {
|
||||
draft[item.name] = value;
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
))}
|
||||
|
||||
<Button
|
||||
className="w-full"
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
setHasSend(true);
|
||||
onCardSendMsg?.({
|
||||
message,
|
||||
extra: {
|
||||
msg:
|
||||
data.input_card_data
|
||||
?.map(item => `${item.name}:${inputData[item.name] || ''}`)
|
||||
.join('\n') || '',
|
||||
mentionList: message.sender_id
|
||||
? [{ id: message.sender_id }]
|
||||
: [],
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{I18n.t('workflow_detail_title_testrun_submit')}
|
||||
</Button>
|
||||
</Space>
|
||||
</NodeWrapperUI>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 PropsWithChildren } from 'react';
|
||||
|
||||
export const NodeWrapperUI: React.FC<PropsWithChildren> = ({ children }) => (
|
||||
<div className="overflow-hidden w-full min-w-[282px] max-w-[546px] p-[16px] coz-bg-primary">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 IEventCallbacks,
|
||||
type IMessage,
|
||||
} from '@coze-common/chat-uikit-shared';
|
||||
import { Button, Space, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
import { type ChatflowNodeData } from './type';
|
||||
import { NodeWrapperUI } from './node-wrapper-ui';
|
||||
|
||||
export const QuestionNodeRender = ({
|
||||
data,
|
||||
onCardSendMsg,
|
||||
readonly,
|
||||
isDisable,
|
||||
message,
|
||||
}: {
|
||||
data: ChatflowNodeData;
|
||||
onCardSendMsg?: IEventCallbacks['onCardSendMsg'];
|
||||
readonly?: boolean;
|
||||
isDisable?: boolean;
|
||||
message: IMessage;
|
||||
}) => {
|
||||
const disabled = readonly || isDisable;
|
||||
return (
|
||||
<NodeWrapperUI>
|
||||
<Space className="w-full" vertical spacing={12} align="start">
|
||||
<Typography.Text ellipsis className="text-18px">
|
||||
{data.question_card_data?.Title}
|
||||
</Typography.Text>
|
||||
<Space className="w-full" vertical spacing={16}>
|
||||
{data.question_card_data?.Options?.map((option, index) => (
|
||||
<Button
|
||||
key={option.name + index}
|
||||
className="w-full"
|
||||
color="primary"
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
onCardSendMsg?.({
|
||||
message,
|
||||
extra: { msg: option.name, mentionList: [] },
|
||||
})
|
||||
}
|
||||
>
|
||||
{option.name}
|
||||
</Button>
|
||||
))}
|
||||
</Space>
|
||||
</Space>
|
||||
</NodeWrapperUI>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 IEventCallbacks,
|
||||
type IMessage,
|
||||
} from '@coze-common/chat-uikit-shared';
|
||||
|
||||
interface RenderNodeBaseProps extends Pick<IEventCallbacks, 'onCardSendMsg'> {
|
||||
isDisable: boolean | undefined;
|
||||
readonly: boolean | undefined;
|
||||
}
|
||||
export interface RenderNodeEntryProps extends RenderNodeBaseProps {
|
||||
message: IMessage;
|
||||
}
|
||||
export interface ChatflowNodeData {
|
||||
card_type: 'QUESTION' | 'INPUT';
|
||||
input_card_data?: {
|
||||
type: string;
|
||||
name: string;
|
||||
}[];
|
||||
question_card_data?: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Title: string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Options: { name: string }[];
|
||||
};
|
||||
}
|
||||
export interface ChatflowNodeData {
|
||||
card_type: 'QUESTION' | 'INPUT';
|
||||
input_card_data?: {
|
||||
type: string;
|
||||
name: string;
|
||||
}[];
|
||||
question_card_data?: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Title: string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Options: { name: string }[];
|
||||
};
|
||||
}
|
||||
@@ -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 { type IMessage } from '@coze-common/chat-uikit-shared';
|
||||
import { safeJSONParse } from '@coze-common/chat-uikit';
|
||||
|
||||
import { type ChatflowNodeData } from './type';
|
||||
|
||||
export const extractChatflowMessage = (message: IMessage) => {
|
||||
if (message.content_type === 'card') {
|
||||
const contentStruct = safeJSONParse(message.content) as {
|
||||
x_properties: {
|
||||
workflow_card_info: string;
|
||||
};
|
||||
};
|
||||
const workflowDataStr = contentStruct?.x_properties?.workflow_card_info;
|
||||
if (workflowDataStr) {
|
||||
const cardData = safeJSONParse(workflowDataStr) as ChatflowNodeData;
|
||||
if (cardData?.card_type === 'QUESTION' && cardData?.question_card_data) {
|
||||
return cardData;
|
||||
}
|
||||
if (cardData?.card_type === 'INPUT' && cardData?.input_card_data) {
|
||||
return cardData;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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 { ContentBoxType } from '@coze-common/chat-uikit-shared';
|
||||
import {
|
||||
ContentBox,
|
||||
type EnhancedContentConfig,
|
||||
ContentType,
|
||||
} from '@coze-common/chat-uikit';
|
||||
import {
|
||||
PluginScopeContextProvider,
|
||||
usePluginCustomComponents,
|
||||
type ComponentTypesMap,
|
||||
} from '@coze-common/chat-area';
|
||||
|
||||
import { WorkflowRenderEntry } from './components';
|
||||
|
||||
const defaultEnable = (value?: boolean) => {
|
||||
if (typeof value === 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
export const ChatFlowRender: ComponentTypesMap['contentBox'] = props => {
|
||||
const customTextMessageInnerTopSlotList = usePluginCustomComponents(
|
||||
'TextMessageInnerTopSlot',
|
||||
);
|
||||
const enhancedContentConfigList: EnhancedContentConfig[] = [
|
||||
{
|
||||
rule: ({ contentType, contentConfigs }) => {
|
||||
const isCardEnable = defaultEnable(
|
||||
contentConfigs?.[ContentBoxType.CARD]?.enable,
|
||||
);
|
||||
return contentType === ContentType.Card && isCardEnable;
|
||||
},
|
||||
render: ({ message, eventCallbacks, options }) => {
|
||||
const { isCardDisabled, readonly } = options;
|
||||
|
||||
const { onCardSendMsg } = eventCallbacks ?? {};
|
||||
|
||||
return (
|
||||
<WorkflowRenderEntry
|
||||
message={message}
|
||||
onCardSendMsg={onCardSendMsg}
|
||||
readonly={readonly}
|
||||
isDisable={isCardDisabled}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
return (
|
||||
<ContentBox
|
||||
enhancedContentConfigList={enhancedContentConfigList}
|
||||
multimodalTextContentAddonTop={
|
||||
<>
|
||||
{customTextMessageInnerTopSlotList.map(
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- matches the expected naming
|
||||
({ pluginName, Component }, index) => (
|
||||
<PluginScopeContextProvider
|
||||
pluginName={pluginName}
|
||||
key={pluginName}
|
||||
>
|
||||
<Component key={index} message={props.message} />
|
||||
</PluginScopeContextProvider>
|
||||
),
|
||||
)}
|
||||
</>
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -15,3 +15,4 @@
|
||||
*/
|
||||
|
||||
export { WorkflowRender } from './components/workflow-render';
|
||||
export { ChatFlowRender } from './components/chat-flow-render';
|
||||
|
||||
@@ -48,5 +48,6 @@ export const createChatBackgroundPlugin = () => {
|
||||
};
|
||||
return {
|
||||
ChatBackgroundPlugin,
|
||||
chatBackgroundEvent,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -117,7 +117,9 @@ export const useSendUseToolMessage = () => {
|
||||
componentsFormValues: Record<string, TValue>;
|
||||
options?: SendMessageOptions;
|
||||
onBeforeSendTemplateShortcut?: (
|
||||
params: OnBeforeSendTemplateShortcutParams,
|
||||
params: OnBeforeSendTemplateShortcutParams & {
|
||||
shortcut: ShortCutCommand;
|
||||
},
|
||||
) => OnBeforeSendTemplateShortcutParams;
|
||||
withoutComponentsList?: boolean;
|
||||
}) => {
|
||||
@@ -170,6 +172,7 @@ export const useSendUseToolMessage = () => {
|
||||
const handledParams = onBeforeSendTemplateShortcut?.({
|
||||
message: cloneDeep(message),
|
||||
options: cloneDeep(options),
|
||||
shortcut,
|
||||
}) || {
|
||||
message,
|
||||
options,
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
import { type CSSProperties, type FC, useRef, useState } from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { type ShortCutCommand } from '@coze-agent-ide/tool-config';
|
||||
import { useMessageWidth } from '@coze-common/chat-area';
|
||||
import { OverflowList, Popover } from '@coze-arch/bot-semi';
|
||||
import { SendType } from '@coze-arch/bot-api/playground_api';
|
||||
import { type ShortCutCommand } from '@coze-agent-ide/tool-config';
|
||||
|
||||
import {
|
||||
enableSendTypePanelHideTemplate,
|
||||
@@ -53,7 +53,9 @@ interface ChatShortCutBarProps {
|
||||
wrapperStyle?: CSSProperties;
|
||||
toolTipFooterSlot?: React.ReactNode;
|
||||
onBeforeSendTemplateShortcut?: (
|
||||
params: OnBeforeSendTemplateShortcutParams,
|
||||
params: OnBeforeSendTemplateShortcutParams & {
|
||||
shortcut: ShortCutCommand;
|
||||
},
|
||||
) => OnBeforeSendTemplateShortcutParams;
|
||||
onBeforeSendTextMessage?: (
|
||||
params: OnBeforeSendQueryShortcutParams,
|
||||
|
||||
@@ -19,7 +19,7 @@ import { exhaustiveCheckSimple } from '@coze-common/chat-area-utils';
|
||||
import { type UIMode } from '../shortcut-bar/types';
|
||||
|
||||
export const getUIModeByBizScene: (props: {
|
||||
bizScene: 'debug' | 'store' | 'home' | 'agentApp';
|
||||
bizScene: 'debug' | 'store' | 'home' | 'agentApp' | 'websdk';
|
||||
showBackground: boolean;
|
||||
}) => UIMode = ({ bizScene, showBackground }) => {
|
||||
if (bizScene === 'agentApp') {
|
||||
@@ -32,7 +32,7 @@ export const getUIModeByBizScene: (props: {
|
||||
return 'white';
|
||||
}
|
||||
|
||||
if (bizScene === 'store' || bizScene === 'debug') {
|
||||
if (bizScene === 'store' || bizScene === 'debug' || bizScene === 'websdk') {
|
||||
if (showBackground) {
|
||||
return 'blur';
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ export const EditorFullInputInner = forwardRef<EditorHandle, EditorInputProps>(
|
||||
...restProps
|
||||
} = props;
|
||||
const [value, setValue] = useState(propsValue);
|
||||
const [isComposing, setIsComposing] = useState(false);
|
||||
|
||||
// Create a mutable reference to store the latest value
|
||||
const valueRef = useRef(value);
|
||||
@@ -104,8 +105,16 @@ export const EditorFullInputInner = forwardRef<EditorHandle, EditorInputProps>(
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
if (isComposing) {
|
||||
return;
|
||||
}
|
||||
propsOnChange?.(v);
|
||||
}}
|
||||
onCompositionStart={() => setIsComposing(true)}
|
||||
onCompositionEnd={e => {
|
||||
setIsComposing(false);
|
||||
propsOnChange?.(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
import React, { Suspense, lazy, useMemo } from 'react';
|
||||
|
||||
import { userStoreService } from '@coze-studio/user-store';
|
||||
import type { IProject } from '@coze-studio/open-chat';
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
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';
|
||||
@@ -81,8 +81,8 @@ export const ChatHistory: React.FC<ChatHistoryProps> = ({
|
||||
|
||||
const chatUserInfo = {
|
||||
id: userInfo?.user_id_str || '',
|
||||
name: userInfo?.name || '',
|
||||
avatar: userInfo?.avatar_url || '',
|
||||
nickname: userInfo?.name || '',
|
||||
url: userInfo?.avatar_url || '',
|
||||
};
|
||||
|
||||
if (
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
// Default session unique_id
|
||||
export const DEFAULT_UNIQUE_ID = '0';
|
||||
|
||||
export const DEFAULT_CONVERSATION_NAME = 'Default';
|
||||
|
||||
export const MAX_LIMIT = 1000;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { useIDEGlobalStore } from '@coze-project-ide/framework';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozChat } from '@coze-arch/coze-design/icons';
|
||||
import { Modal, Select, Typography, Toast } from '@coze-arch/coze-design';
|
||||
@@ -24,9 +25,8 @@ import {
|
||||
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 { DEFAULT_CONVERSATION_NAME } from '../../constants';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
@@ -154,7 +154,7 @@ export const useDeleteChat = ({
|
||||
style={{ width: '50%' }}
|
||||
dropdownStyle={{ width: 220 }}
|
||||
size="small"
|
||||
defaultValue={DEFAULT_UNIQUE_ID}
|
||||
defaultValue={optionList[0]?.value}
|
||||
optionList={optionList}
|
||||
onChange={value => {
|
||||
const selectItem = staticList.find(
|
||||
|
||||
@@ -30,7 +30,7 @@ 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 { type ErrorCode } from '../constants';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
@@ -140,9 +140,7 @@ export const StaticChatList = ({
|
||||
{item.conversation_name}
|
||||
</Text>
|
||||
)}
|
||||
{editingUniqueId === item.unique_id ||
|
||||
item.unique_id === DEFAULT_UNIQUE_ID ||
|
||||
!canEdit ? null : (
|
||||
{editingUniqueId === item.unique_id || !canEdit ? null : (
|
||||
<div className={commonStyles.icons}>
|
||||
<IconButton
|
||||
size="small"
|
||||
|
||||
@@ -41,10 +41,10 @@ import {
|
||||
usePrimarySidebarStore,
|
||||
} from '@coze-project-ide/biz-components';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
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';
|
||||
@@ -233,33 +233,29 @@ export const useWorkflowResource = (): UseWorkflowResourceReturn => {
|
||||
subType: WorkflowMode.Workflow,
|
||||
tooltip: <WorkflowTooltip flowMode={WorkflowMode.Workflow} />,
|
||||
},
|
||||
// The open-source version does not currently support conversation streaming
|
||||
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} />,
|
||||
},
|
||||
{
|
||||
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
|
||||
]
|
||||
}
|
||||
</>
|
||||
),
|
||||
({ resource }) => (
|
||||
<>
|
||||
{
|
||||
WORKFLOW_SUB_TYPE_ICON_MAP[
|
||||
resource.res_sub_type || WorkflowMode.Workflow
|
||||
]
|
||||
}
|
||||
</>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"@coze-studio/publish-manage-hooks": "workspace:*",
|
||||
"@coze-studio/user-store": "workspace:*",
|
||||
"@coze-workflow/base": "workspace:*",
|
||||
"@coze/api": "1.1.0-beta.4",
|
||||
"@coze/api": "1.3.5",
|
||||
"ahooks": "^3.7.8",
|
||||
"classnames": "^2.3.2",
|
||||
"dayjs": "^1.11.7",
|
||||
|
||||
@@ -90,22 +90,19 @@ export const Configuration = () => {
|
||||
onClick={handleSwitchExpand}
|
||||
/>
|
||||
</div>
|
||||
{/* will support soon */}
|
||||
{IS_OPEN_SOURCE ? null : (
|
||||
<div
|
||||
className={classnames(
|
||||
styles.item,
|
||||
compareURI(context?.uri, SESSION_CONFIG_URI) && styles.activate,
|
||||
)}
|
||||
onClick={handleOpenSession}
|
||||
>
|
||||
<IconCozChatSetting
|
||||
className="coz-fg-plus"
|
||||
style={{ marginRight: 4 }}
|
||||
/>
|
||||
{I18n.t('wf_chatflow_101')}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={classnames(
|
||||
styles.item,
|
||||
compareURI(context?.uri, SESSION_CONFIG_URI) && styles.activate,
|
||||
)}
|
||||
onClick={handleOpenSession}
|
||||
>
|
||||
<IconCozChatSetting
|
||||
className="coz-fg-plus"
|
||||
style={{ marginRight: 4 }}
|
||||
/>
|
||||
{I18n.t('wf_chatflow_101')}
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
styles.item,
|
||||
|
||||
@@ -73,8 +73,7 @@ const ProjectIDE: React.FC<ProjectIDEProps> = memo(
|
||||
() => ({
|
||||
view: {
|
||||
widgetRegistries: [
|
||||
// will support soon
|
||||
...(IS_OPEN_SOURCE ? [] : [ConversationRegistry]),
|
||||
ConversationRegistry,
|
||||
WorkflowWidgetRegistry,
|
||||
DatabaseWidgetRegistry,
|
||||
KnowledgeWidgetRegistry,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
CHAT_APP_CHATFLOW_COZE_APP_ID=""
|
||||
CHAT_APP_CHATFLOW_COZE_WORKFLOW_ID=""
|
||||
CHAT_APP_INDEX_COZE_BOT_ID=""
|
||||
|
||||
CHAT_APP_COZE_TOKEN=""
|
||||
CHAT_APP_COZE_BOT_USER_URL=""
|
||||
@@ -0,0 +1,6 @@
|
||||
CHAT_APP_CHATFLOW_COZE_APP_ID="7542447949096157184"
|
||||
CHAT_APP_CHATFLOW_COZE_WORKFLOW_ID="7542447968176046080"
|
||||
CHAT_APP_INDEX_COZE_BOT_ID=""
|
||||
|
||||
CHAT_APP_COZE_TOKEN="pat_aec5209b90cdac8883547dff09fe0000e6c1b296347c977c16cdb70f094a55ca"
|
||||
CHAT_APP_COZE_BOT_USER_URL=""
|
||||
4
frontend/packages/studio/open-platform/chat-app-sdk/.gitignore
vendored
Normal file
4
frontend/packages/studio/open-platform/chat-app-sdk/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
inhouse
|
||||
libs
|
||||
dist_ignore
|
||||
.env.local*
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
const { defineConfig } = require('@coze-arch/stylelint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
extends: [],
|
||||
rules: {
|
||||
'order/properties-order': null,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
reviewers:
|
||||
- sunkuo
|
||||
- gaoyuanhan.duty
|
||||
- gaoding.devingao
|
||||
- shenxiaojie.316
|
||||
- zhangyingdong
|
||||
- shanrenkai
|
||||
- yangyu.1
|
||||
approvals_required: 1
|
||||
@@ -0,0 +1,3 @@
|
||||
# @flow-platform/chat-app-sdk
|
||||
|
||||
https://bytedance.larkoffice.com/wiki/IdTkw7Kd5iahWLkv6FKcIo53nBe
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "build",
|
||||
"outputFolderNames": ["dist", "inhouse", "libs"]
|
||||
},
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./dist_ignore"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"codecov": {
|
||||
"coverage": 30,
|
||||
"incrementCoverage": 60
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {
|
||||
'@coze-arch/max-line-per-function': [
|
||||
'error',
|
||||
{
|
||||
max: 300,
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': 'off',
|
||||
},
|
||||
ignores: ['**/inhouse', '**/libs', '**/dist_*'],
|
||||
});
|
||||
126
frontend/packages/studio/open-platform/chat-app-sdk/package.json
Normal file
126
frontend/packages/studio/open-platform/chat-app-sdk/package.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"name": "@coze-studio/chat-app-sdk",
|
||||
"version": "1.2.0-beta.17",
|
||||
"description": "Coze Web ChatApp SDK",
|
||||
"license": "Apache-2.0",
|
||||
"author": "yangyu.1@bytedance.com",
|
||||
"maintainers": [
|
||||
"liuyuhang.0@bytedance.com",
|
||||
"yangyu.1@bytedance.com"
|
||||
],
|
||||
"sideEffects": [
|
||||
"**/*.css",
|
||||
"**/*.less",
|
||||
"**/*.scss"
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./rspack": "./rspack-config/export.ts"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"unpkg": true,
|
||||
"types": "./src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"rspack": [
|
||||
"./rspack-config/export.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"inhouse",
|
||||
"libs",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE_MODE=true pnpm build:inhouse:cn --analyze",
|
||||
"analyze:perf": "PERFSEE=true npm run build:release:cn",
|
||||
"bam": "bam update",
|
||||
"build": "IS_OPEN_SOURCE=true rm -rf dist_ignore inhouse libs && concurrently \"npm:build:*\"",
|
||||
"build:inhouse:boe": "CUSTOM_VERSION=inhouse BUILD_TYPE=offline REGION=cn npm run rsbuild",
|
||||
"build:inhouse:cn": "CUSTOM_VERSION=inhouse BUILD_TYPE=online REGION=cn npm run rsbuild",
|
||||
"build:inhouse:sg": "CUSTOM_VERSION=inhouse BUILD_TYPE=online REGION=sg npm run rsbuild",
|
||||
"build:release:cn": "CUSTOM_VERSION=release BUILD_TYPE=online REGION=cn npm run rsbuild",
|
||||
"build:release:oversea": "CUSTOM_VERSION=release BUILD_TYPE=online REGION=sg npm run rsbuild",
|
||||
"build:ts": "tsc -b tsconfig.build.json",
|
||||
"dev": "IS_OPEN_SOURCE=true npm run dev:cn:rl",
|
||||
"dev:boe": "CUSTOM_VERSION=inhouse REGION=cn BUILD_TYPE=offline pnpm rsdev",
|
||||
"dev:cn": "CUSTOM_VERSION=inhouse REGION=cn BUILD_TYPE=online pnpm rsdev",
|
||||
"dev:cn:rl": "CUSTOM_VERSION=release REGION=cn BUILD_TYPE=online pnpm rsdev",
|
||||
"dev:sg": "CUSTOM_VERSION=inhouse REGION=sg BUILD_TYPE=online pnpm rsdev",
|
||||
"dev:sg:rl": "CUSTOM_VERSION=release REGION=sg BUILD_TYPE=online pnpm rsdev",
|
||||
"lint": "eslint ./ --cache",
|
||||
"rsbuild": "NODE_ENV=production rspack build -c ./rspack-config/build.config.ts",
|
||||
"rsdev": "NODE_ENV=development rspack serve -c ./rspack-config/dev.config.ts",
|
||||
"test": "vitest --run --passWithNoTests",
|
||||
"test:cov": "vitest run --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coze-common/assets": "workspace:*",
|
||||
"classnames": "^2.3.2",
|
||||
"core-js": "^3.37.1",
|
||||
"immer": "^10.0.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
"react": "~18.2.0",
|
||||
"react-device-detect": "2.2.3",
|
||||
"react-dom": "~18.2.0",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-env": "workspace:*",
|
||||
"@coze-arch/bot-semi": "workspace:*",
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/bot-utils": "workspace:*",
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-arch/pkg-root-webpack-plugin": "workspace:*",
|
||||
"@coze-arch/postcss-config": "workspace:*",
|
||||
"@coze-arch/stylelint-config": "workspace:*",
|
||||
"@coze-arch/tailwind-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@coze-studio/bot-env-adapter": "workspace:*",
|
||||
"@coze-studio/open-chat": "workspace:*",
|
||||
"@douyinfe/semi-rspack-plugin": "2.61.0",
|
||||
"@rspack/cli": "0.6.0",
|
||||
"@rspack/core": "0.6.0",
|
||||
"@rspack/plugin-react-refresh": "0.6.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@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",
|
||||
"@types/postcss-js": "^4.0.2",
|
||||
"@types/react": "18.2.37",
|
||||
"@types/react-dom": "18.2.15",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"concurrently": "~8.2.2",
|
||||
"css-loader": "^6.10.0",
|
||||
"debug": "^4.3.4",
|
||||
"file-loader": "^6.2.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "~11.1.3",
|
||||
"postcss": "^8.4.32",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"react-refresh": "0.14.0",
|
||||
"react-router-dom": "^6.11.1",
|
||||
"rspack-plugin-dotenv": "^0.0.3",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^14.1.0",
|
||||
"style-loader": "^3.3.4",
|
||||
"tailwindcss": "~3.3.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~5.8.2",
|
||||
"vitest": "~3.0.5",
|
||||
"webpack": "~5.91.0"
|
||||
},
|
||||
"// deps": "debug@^4.3.4 为脚本自动补齐,请勿改动",
|
||||
"botPublishConfig": {
|
||||
"main": "dist/index.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { configs as GLOBAL_ENVS } from '@coze-studio/bot-env-adapter/configs';
|
||||
|
||||
import { openSdkDefineEnvs } from './env';
|
||||
import { IS_OVERSEA } from './base';
|
||||
|
||||
export const getRspackAppDefineEnvs = () => ({
|
||||
...openSdkDefineEnvs,
|
||||
/**
|
||||
* ChatArea 依赖
|
||||
*/
|
||||
IS_OVERSEA,
|
||||
CARD_BUILDER_ENV_STR: JSON.stringify(GLOBAL_ENVS.CARD_BUILDER_ENV_STR),
|
||||
SAMI_WS_ORIGIN: JSON.stringify(GLOBAL_ENVS.SAMI_WS_ORIGIN),
|
||||
SAMI_APP_KEY: JSON.stringify(GLOBAL_ENVS.SAMI_APP_KEY),
|
||||
SAMI_CHAT_WS_URL: JSON.stringify(GLOBAL_ENVS.SAMI_CHAT_WS_URL),
|
||||
COZE_API_TTS_BASE_URL: JSON.stringify(GLOBAL_ENVS.COZE_API_TTS_BASE_URL),
|
||||
FEATURE_ENABLE_MSG_DEBUG: false,
|
||||
APP_ID: '""',
|
||||
COZE_DOMAIN: JSON.stringify(GLOBAL_ENVS.COZE_DOMAIN),
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const {
|
||||
REGION,
|
||||
BUILD_TYPE,
|
||||
CUSTOM_VERSION,
|
||||
NODE_ENV: ENV,
|
||||
ANALYZE_MODE,
|
||||
PERFSEE,
|
||||
IS_OPEN_SOURCE,
|
||||
} = process.env;
|
||||
|
||||
const NODE_ENV = ENV as 'development' | 'production';
|
||||
const IS_DEV_MODE = NODE_ENV !== 'production';
|
||||
const IS_BOE = BUILD_TYPE === 'offline';
|
||||
const IS_RELEASE_VERSION = CUSTOM_VERSION === 'release';
|
||||
const IS_OVERSEA = REGION !== 'cn';
|
||||
const IS_ANALYZE_MODE = ANALYZE_MODE === 'true';
|
||||
const IS_PERFSEE = PERFSEE === 'true';
|
||||
|
||||
export {
|
||||
IS_PERFSEE,
|
||||
IS_DEV_MODE,
|
||||
IS_BOE,
|
||||
IS_RELEASE_VERSION,
|
||||
IS_OVERSEA,
|
||||
CUSTOM_VERSION,
|
||||
NODE_ENV,
|
||||
REGION,
|
||||
IS_ANALYZE_MODE,
|
||||
IS_OPEN_SOURCE,
|
||||
};
|
||||
|
||||
type EnvVar = boolean | string;
|
||||
|
||||
export const getEnvConfig = (
|
||||
config: {
|
||||
cn: {
|
||||
boe?: EnvVar;
|
||||
inhouse?: EnvVar;
|
||||
release?: EnvVar;
|
||||
};
|
||||
sg: {
|
||||
inhouse: EnvVar;
|
||||
release: EnvVar;
|
||||
};
|
||||
va: {
|
||||
release: EnvVar;
|
||||
};
|
||||
},
|
||||
defaultVal: EnvVar = '',
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
): EnvVar => config[REGION]?.[IS_BOE ? 'boe' : CUSTOM_VERSION] ?? defaultVal;
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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 path from 'path';
|
||||
|
||||
import { DefinePlugin, ProgressPlugin, type Configuration } from '@rspack/core';
|
||||
import { SemiRspackPlugin } from '@douyinfe/semi-rspack-plugin';
|
||||
import PkgRootWebpackPlugin from '@coze-arch/pkg-root-webpack-plugin';
|
||||
|
||||
import { PREFIX_CLASS } from './semi-css-var-postcss-plugin';
|
||||
import { cssLoaders, sideEffectsRules, swcTsLoader } from './rules';
|
||||
import { openSdkUnPkgDirName } from './env';
|
||||
import { IS_ANALYZE_MODE } from './base';
|
||||
import { getRspackAppDefineEnvs } from './app';
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- __dirname
|
||||
const __rootName = path.resolve(__dirname, '../');
|
||||
|
||||
const config: Configuration = {
|
||||
mode: 'production',
|
||||
context: __rootName,
|
||||
optimization: {
|
||||
splitChunks: false,
|
||||
...(IS_ANALYZE_MODE
|
||||
? {
|
||||
minimize: false,
|
||||
chunkIds: 'named',
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
entry: {
|
||||
main: ['./src/index.ts'],
|
||||
ui: './src/export-ui/index.ts',
|
||||
},
|
||||
experiments: {
|
||||
css: false,
|
||||
},
|
||||
output: {
|
||||
path: openSdkUnPkgDirName,
|
||||
filename: pathData =>
|
||||
pathData.chunk?.name === 'main' ? 'index.js' : '[name].js',
|
||||
library: {
|
||||
name: 'CozeWebSDK[name]',
|
||||
type: 'umd',
|
||||
},
|
||||
},
|
||||
target: ['web'],
|
||||
resolve: {
|
||||
tsConfigPath: path.resolve(__rootName, './tsconfig.json'), // https://www.rspack.dev/config/resolve.html#resolvetsconfigpath
|
||||
alias: {
|
||||
'@coze-arch/i18n$': path.resolve(
|
||||
__rootName,
|
||||
'./node_modules/@coze-arch/i18n/src/raw/index.ts',
|
||||
),
|
||||
/**
|
||||
* swc.env.mode='usage'
|
||||
*/
|
||||
'core-js': path.dirname(require.resolve('core-js')),
|
||||
},
|
||||
extensions: ['...', '.tsx', '.ts', '.jsx'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
...sideEffectsRules,
|
||||
{
|
||||
test: /\.svg$/,
|
||||
issuer: /\.[jt]sx?$/,
|
||||
use: [
|
||||
{
|
||||
loader: '@svgr/webpack',
|
||||
options: {
|
||||
svgoConfig: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'preset-default',
|
||||
params: {
|
||||
overrides: {
|
||||
removeViewBox: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
native: false,
|
||||
},
|
||||
},
|
||||
'file-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(png|gif|jpg|jpeg|woff2)$/,
|
||||
type: 'asset',
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
...cssLoaders,
|
||||
{
|
||||
loader: 'less-loader',
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
...cssLoaders,
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sassOptions: {
|
||||
silenceDeprecations: [
|
||||
'mixed-decls',
|
||||
'import',
|
||||
'function-units',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: cssLoaders,
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: {
|
||||
and: [/\/node_modules\//, /^((?!@byted\/mojito-safe-fund).)*$/],
|
||||
},
|
||||
use: swcTsLoader,
|
||||
},
|
||||
],
|
||||
},
|
||||
builtins: {
|
||||
treeShaking: true,
|
||||
},
|
||||
plugins: [
|
||||
new DefinePlugin(getRspackAppDefineEnvs()),
|
||||
new ProgressPlugin({}),
|
||||
new PkgRootWebpackPlugin({}),
|
||||
new SemiRspackPlugin({
|
||||
prefixCls: PREFIX_CLASS,
|
||||
}),
|
||||
].filter(Boolean) as Configuration['plugins'],
|
||||
devServer: {
|
||||
allowedHosts: 'all',
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
},
|
||||
devtool: false,
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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 path from 'path';
|
||||
|
||||
import { DotenvPlugin } from 'rspack-plugin-dotenv';
|
||||
import refreshPlugin from '@rspack/plugin-react-refresh';
|
||||
import {
|
||||
DefinePlugin,
|
||||
ProgressPlugin,
|
||||
type Configuration,
|
||||
HtmlRspackPlugin,
|
||||
} from '@rspack/core';
|
||||
import { SemiRspackPlugin } from '@douyinfe/semi-rspack-plugin';
|
||||
import PkgRootWebpackPlugin from '@coze-arch/pkg-root-webpack-plugin';
|
||||
|
||||
import { devCssLoaders, swcTsLoader } from './rules';
|
||||
import { devDefineEnvs } from './dev';
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- __dirname
|
||||
const __rootName = path.resolve(__dirname, '../');
|
||||
|
||||
const config: Configuration = {
|
||||
mode: 'development',
|
||||
context: __rootName,
|
||||
entry: {
|
||||
main: ['./src/dev-app/index.tsx'],
|
||||
},
|
||||
experiments: {
|
||||
css: true,
|
||||
},
|
||||
target: ['web'],
|
||||
resolve: {
|
||||
tsConfigPath: path.resolve(__rootName, 'tsconfig.json'), // https://www.rspack.dev/config/resolve.html#resolvetsconfigpath
|
||||
alias: {
|
||||
'@coze-arch/i18n$': path.resolve(
|
||||
__rootName,
|
||||
'./node_modules/@coze-arch/i18n/src/raw/index.ts',
|
||||
),
|
||||
/**
|
||||
* swc.env.mode='usage'
|
||||
*/
|
||||
'core-js': path.dirname(require.resolve('core-js')),
|
||||
},
|
||||
extensions: ['...', '.tsx', '.ts', '.jsx'],
|
||||
},
|
||||
module: {
|
||||
parser: {
|
||||
'css/auto': {
|
||||
namedExports: false,
|
||||
},
|
||||
},
|
||||
generator: {
|
||||
'css/auto': {
|
||||
exportsConvention: 'camel-case',
|
||||
localIdentName: '[hash]-[local]',
|
||||
},
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
...devCssLoaders,
|
||||
{
|
||||
loader: 'less-loader',
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
type: 'css/auto',
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
...devCssLoaders,
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sassOptions: {
|
||||
silenceDeprecations: [
|
||||
'mixed-decls',
|
||||
'import',
|
||||
'function-units',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'css/auto',
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: devCssLoaders,
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
issuer: /\.[jt]sx?$/,
|
||||
use: [
|
||||
{
|
||||
loader: '@svgr/webpack',
|
||||
options: {
|
||||
native: false,
|
||||
svgoConfig: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'preset-default',
|
||||
params: {
|
||||
overrides: {
|
||||
removeViewBox: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
'file-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(png|gif|jpg|jpeg|woff2)$/,
|
||||
use: 'file-loader',
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: {
|
||||
and: [/\/node_modules\//, /^((?!@byted\/mojito-safe-fund).)*$/],
|
||||
},
|
||||
use: swcTsLoader,
|
||||
},
|
||||
],
|
||||
},
|
||||
builtins: {
|
||||
treeShaking: true,
|
||||
},
|
||||
plugins: [
|
||||
new DotenvPlugin({
|
||||
path: path.resolve(
|
||||
__rootName,
|
||||
devDefineEnvs.IS_BOE ? '.env.local.boe' : '.env.local',
|
||||
),
|
||||
systemvars: false,
|
||||
defaults: true,
|
||||
allowEmptyValues: true,
|
||||
}),
|
||||
new DefinePlugin({
|
||||
...devDefineEnvs,
|
||||
IS_PROD: !devDefineEnvs.IS_BOE,
|
||||
}),
|
||||
new ProgressPlugin({}),
|
||||
new PkgRootWebpackPlugin({}),
|
||||
new SemiRspackPlugin({
|
||||
prefixCls: 'coze-chat-sdk-semi',
|
||||
}),
|
||||
new HtmlRspackPlugin(),
|
||||
new refreshPlugin(),
|
||||
] as Configuration['plugins'],
|
||||
stats: false,
|
||||
devServer: {
|
||||
allowedHosts: 'all',
|
||||
compress: false,
|
||||
historyApiFallback: true,
|
||||
port: '8081',
|
||||
hot: true,
|
||||
proxy: [
|
||||
{
|
||||
context: ['/api'],
|
||||
target: 'http://localhost:8888',
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
},
|
||||
{
|
||||
context: ['/v1'],
|
||||
target: 'http://localhost:8888',
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
},
|
||||
{
|
||||
context: ['/v3'],
|
||||
target: 'http://localhost:8888',
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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 DefinePluginOptions } from '@rspack/core';
|
||||
|
||||
import { getRspackAppDefineEnvs } from './app';
|
||||
|
||||
export const devDefineEnvs: DefinePluginOptions = getRspackAppDefineEnvs();
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 {
|
||||
IS_RELEASE_VERSION,
|
||||
IS_DEV_MODE,
|
||||
NODE_ENV,
|
||||
REGION,
|
||||
IS_BOE,
|
||||
getEnvConfig,
|
||||
IS_OVERSEA,
|
||||
IS_OPEN_SOURCE,
|
||||
} from './base';
|
||||
|
||||
export const openSdkDefineEnvs = {
|
||||
IS_BOE,
|
||||
IS_DEV_MODE,
|
||||
REGION: JSON.stringify(REGION),
|
||||
IS_RELEASE_VERSION,
|
||||
IS_OVERSEA,
|
||||
FEATURE_ENABLE_TEA_UG: false,
|
||||
IS_PROD: !IS_BOE,
|
||||
IS_OPEN_SOURCE,
|
||||
};
|
||||
|
||||
const getUnPkgDirName = () => {
|
||||
if (IS_BOE) {
|
||||
return 'inhouse/boe';
|
||||
}
|
||||
|
||||
let name = '';
|
||||
|
||||
if (IS_RELEASE_VERSION) {
|
||||
switch (REGION) {
|
||||
case 'sg':
|
||||
case 'va':
|
||||
name = 'oversea';
|
||||
break;
|
||||
case 'cn':
|
||||
name = 'cn';
|
||||
break;
|
||||
default:
|
||||
name = '';
|
||||
}
|
||||
|
||||
return `libs/${name}`;
|
||||
}
|
||||
|
||||
return `inhouse/${REGION}`;
|
||||
};
|
||||
export const openSdkUnPkgDirName = getUnPkgDirName();
|
||||
|
||||
const slardarVaPath = '/maliva';
|
||||
const slardarSgPath = '/sg';
|
||||
export const openSdkSlardarRegion = getEnvConfig({
|
||||
cn: {
|
||||
boe: '',
|
||||
inhouse: '',
|
||||
release: '',
|
||||
},
|
||||
sg: {
|
||||
inhouse: slardarSgPath,
|
||||
release: slardarSgPath,
|
||||
},
|
||||
va: {
|
||||
release: slardarVaPath,
|
||||
},
|
||||
});
|
||||
|
||||
console.debug(
|
||||
'open-sdk',
|
||||
NODE_ENV,
|
||||
'\nopenSdkDefineEnvs:',
|
||||
openSdkDefineEnvs,
|
||||
'\nopenSdkSlardarRegion:',
|
||||
openSdkSlardarRegion,
|
||||
);
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 { swcTsLoader, devCssLoaders } from './rules';
|
||||
export { getRspackAppDefineEnvs } from './app';
|
||||
export { openSdkSlardarRegion } from './env';
|
||||
export {
|
||||
IS_DEV_MODE,
|
||||
IS_RELEASE_VERSION,
|
||||
CUSTOM_VERSION,
|
||||
NODE_ENV,
|
||||
REGION,
|
||||
IS_BOE,
|
||||
} from './base';
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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 RuleSetRule } from '@rspack/core';
|
||||
|
||||
import semiCssVarPrefixPlugin from './semi-css-var-postcss-plugin';
|
||||
import { IS_DEV_MODE } from './base';
|
||||
|
||||
type UseLoaders = Extract<RuleSetRule['use'], unknown[]>;
|
||||
|
||||
export const cssLoaders: UseLoaders = [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: IS_DEV_MODE,
|
||||
modules: {
|
||||
auto: true,
|
||||
exportLocalsConvention: 'camelCase',
|
||||
localIdentName: !IS_DEV_MODE ? '[hash]' : '[path][name][ext]__[local]',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
plugins: [
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('tailwindcss')(),
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('autoprefixer')(),
|
||||
|
||||
semiCssVarPrefixPlugin(),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 已经标记 sideEffects: false,无需覆盖 的pkg:
|
||||
* chat-open
|
||||
*/
|
||||
export const sideEffectsRules: RuleSetRule[] = [
|
||||
{
|
||||
test: /packages\/components\/bot-icons/,
|
||||
sideEffects: false,
|
||||
},
|
||||
{
|
||||
test: /packages\/components\/bot-semi/,
|
||||
sideEffects: false,
|
||||
},
|
||||
{
|
||||
test: /packages\/studio\/chat-area/,
|
||||
sideEffects: false,
|
||||
},
|
||||
{
|
||||
test: /packages\/studio\/chat-core/,
|
||||
sideEffects: false,
|
||||
},
|
||||
{
|
||||
test: /packages\/arch\/i18n/,
|
||||
sideEffects: false,
|
||||
},
|
||||
].filter(r => r);
|
||||
|
||||
export const swcTsLoader: UseLoaders = [
|
||||
{
|
||||
loader: 'builtin:swc-loader',
|
||||
options: {
|
||||
sourceMap: IS_DEV_MODE,
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
development: IS_DEV_MODE,
|
||||
refresh: IS_DEV_MODE,
|
||||
},
|
||||
},
|
||||
},
|
||||
env: {
|
||||
mode: 'usage',
|
||||
coreJs: '3.37.1',
|
||||
targets: [
|
||||
'chrome >= 87',
|
||||
'edge >= 88',
|
||||
'firefox >= 78',
|
||||
'safari >= 14',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const devCssLoaders: UseLoaders = [
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
plugins: [
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('tailwindcss')(),
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('autoprefixer')(),
|
||||
|
||||
semiCssVarPrefixPlugin(),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* PostCSS 插件:为 Semi 组件类名和 CSS 变量添加前缀
|
||||
* 解决 coze-design 里 hardcode 的 .semi-xxx 类名与 prefixCls 不匹配导致样式失效问题
|
||||
* 兼容多类名、嵌套、伪类、组合选择器等复杂情况
|
||||
*
|
||||
* 注意:本插件应在 coze-design 的样式被引入后生效,确保所有 .semi-xxx 都能被正确加前缀
|
||||
*
|
||||
* 已添加调试代码,可通过环境变量 DEBUG_SEMI_CSS_VAR_PLUGIN 控制输出
|
||||
*/
|
||||
|
||||
import type { PluginCreator } from 'postcss';
|
||||
|
||||
export const PREFIX_CLASS = 'coze-chat-sdk-semi';
|
||||
const CSS_VAR_PREFIX = `${PREFIX_CLASS}-`;
|
||||
const SEMI_CLASS_PREFIX = 'semi-';
|
||||
const CUSTOM_CLASS_PREFIX = `${PREFIX_CLASS}-`;
|
||||
|
||||
// 处理选择器,将 .semi-xxx 替换为 .coze-chat-sdk-semi-xxx
|
||||
function processSelector(selector: string): string {
|
||||
// 只处理 .semi-xxx(不管前面有无其它类名、伪类、组合等)
|
||||
// 例如:.semi-button、.semi-button-primary:hover、.foo .semi-button.bar
|
||||
// 注意:不要重复加前缀
|
||||
// 兼容 :is(.semi-button), :not(.semi-button), .semi-button:hover, .semi-button.foo
|
||||
// 兼容多个选择器用逗号分隔的情况
|
||||
const replaced = selector.replace(
|
||||
/\.semi-([a-zA-Z0-9_-]+)/g,
|
||||
(match, className) => {
|
||||
// 已经有前缀的不处理
|
||||
if (match.includes(`.${CUSTOM_CLASS_PREFIX}`)) {
|
||||
return match;
|
||||
}
|
||||
return `.${CUSTOM_CLASS_PREFIX}${className}`;
|
||||
},
|
||||
);
|
||||
return replaced;
|
||||
}
|
||||
|
||||
const semiCssVarPrefixPlugin: PluginCreator<void> = () => ({
|
||||
postcssPlugin: 'semi-css-var-prefix',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Rule(rule) {
|
||||
// 只要选择器里有 .semi-,就处理
|
||||
if (rule.selector && rule.selector.includes(`.${SEMI_CLASS_PREFIX}`)) {
|
||||
rule.selector = processSelector(rule.selector);
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Declaration(decl) {
|
||||
// 处理 CSS 变量定义
|
||||
if (decl.prop && decl.prop.startsWith('--semi-')) {
|
||||
decl.prop = decl.prop.replace(/^--semi-/, `--${CSS_VAR_PREFIX}`);
|
||||
}
|
||||
|
||||
// 处理 CSS 变量引用
|
||||
if (decl.value && decl.value.includes('var(--semi-')) {
|
||||
decl.value = decl.value.replace(
|
||||
/var\(--semi-([a-zA-Z0-9_-]+)\)/g,
|
||||
`var(--${CSS_VAR_PREFIX}$1)`,
|
||||
);
|
||||
}
|
||||
|
||||
// 处理 rgba(var(--semi-xxx), ...)
|
||||
if (decl.value && decl.value.includes('rgba(var(--semi-')) {
|
||||
decl.value = decl.value.replace(
|
||||
/rgba\(var\(--semi-([a-zA-Z0-9_-]+)\)/g,
|
||||
`rgba(var(--${CSS_VAR_PREFIX}$1)`,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
semiCssVarPrefixPlugin.postcss = true;
|
||||
|
||||
export default semiCssVarPrefixPlugin;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -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 { expect, describe, test, vi } from 'vitest';
|
||||
import { Layout } from '@coze-studio/open-chat/types';
|
||||
|
||||
import { type CozeChatOptions } from '@/types/client';
|
||||
|
||||
import { AuthClient } from '../auth';
|
||||
import type * as ClientModule from '..';
|
||||
|
||||
vi.hoisted(() => {
|
||||
// @ts-expect-error -- 将 IS_OVERSEA 提升到最外层
|
||||
global.IS_OVERSEA = false;
|
||||
});
|
||||
|
||||
vi.mock('@/components/widget', () => ({
|
||||
default: vi.fn(),
|
||||
}));
|
||||
|
||||
const testBotId = '7313780910216806444';
|
||||
|
||||
const config = {
|
||||
config: {
|
||||
botId: testBotId,
|
||||
},
|
||||
auth: {
|
||||
type: 'token',
|
||||
onRefreshToken: () => Promise.resolve('test'),
|
||||
token: 'Test',
|
||||
},
|
||||
componentProps: {
|
||||
title: '历史学教授',
|
||||
},
|
||||
};
|
||||
|
||||
const config2: CozeChatOptions = {
|
||||
config: {
|
||||
bot_id: testBotId,
|
||||
},
|
||||
auth: {
|
||||
// @ts-expect-error -- 测试兼容逻辑
|
||||
type: 'token',
|
||||
onRefreshToken: () => Promise.resolve('test'),
|
||||
token: 'Test',
|
||||
},
|
||||
componentProps: {
|
||||
title: '历史学教授',
|
||||
},
|
||||
};
|
||||
const config3: CozeChatOptions = {
|
||||
config: {
|
||||
bot_id: testBotId,
|
||||
},
|
||||
auth: {
|
||||
onRefreshToken: () => Promise.resolve('test'),
|
||||
token: 'Test',
|
||||
},
|
||||
componentProps: {
|
||||
title: '历史学教授',
|
||||
},
|
||||
};
|
||||
|
||||
const config4: CozeChatOptions = {
|
||||
config: {
|
||||
bot_id: testBotId,
|
||||
},
|
||||
auth: {
|
||||
// @ts-expect-error -- 测试兼容逻辑
|
||||
type: 'token',
|
||||
onRefreshToken: () => Promise.resolve(''),
|
||||
token: '',
|
||||
},
|
||||
componentProps: {
|
||||
title: '历史学教授',
|
||||
},
|
||||
};
|
||||
|
||||
describe('client', async () => {
|
||||
const { WebChatClient } = await vi.importActual<typeof ClientModule>('..');
|
||||
|
||||
test('client list', () => {
|
||||
const client1 = new WebChatClient(config);
|
||||
const client2 = new WebChatClient({
|
||||
...config,
|
||||
el: document.createElement('div'),
|
||||
});
|
||||
|
||||
client1.destroy();
|
||||
|
||||
expect(WebChatClient.clients.length).toBe(1);
|
||||
expect(!WebChatClient.clients.includes(client1)).toBe(true);
|
||||
|
||||
client2.destroy();
|
||||
|
||||
expect(WebChatClient.clients.length).toBe(0);
|
||||
expect(!WebChatClient.clients.includes(client2)).toBe(true);
|
||||
});
|
||||
|
||||
test('client mount', () => {
|
||||
const client = new WebChatClient(config2);
|
||||
const client2 = new WebChatClient({
|
||||
...config2,
|
||||
el: document.createElement('div'),
|
||||
});
|
||||
|
||||
// @ts-expect-error -- ut
|
||||
expect(!!client.defaultRoot).toBe(true);
|
||||
|
||||
// @ts-expect-error -- ut
|
||||
expect(!!client2.defaultRoot).toBe(false);
|
||||
});
|
||||
|
||||
test('init layout mobile', () => {
|
||||
vi.mock('react-device-detect', () => ({ isMobileOnly: true }));
|
||||
const client = new WebChatClient(config2);
|
||||
|
||||
expect(client.options?.ui?.base?.layout).toBe(Layout.MOBILE);
|
||||
});
|
||||
|
||||
test('init aut', async () => {
|
||||
const auth2 = new AuthClient(config2);
|
||||
|
||||
expect(await auth2.initToken()).toBe(true);
|
||||
expect(auth2.checkOptions()).toBe(true);
|
||||
|
||||
const auth3 = new AuthClient(config3);
|
||||
expect(await auth3.initToken()).toBe(true);
|
||||
expect(auth3.checkOptions()).toBe(false);
|
||||
|
||||
const auth4 = new AuthClient(config4);
|
||||
expect(await auth4.initToken()).toBe(false);
|
||||
expect(auth4.checkOptions()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 { AuthType } from '@coze-studio/open-chat/types';
|
||||
|
||||
import { type CozeChatOptions } from '@/types/client';
|
||||
|
||||
export class AuthClient {
|
||||
readonly options: CozeChatOptions;
|
||||
public constructor(options: CozeChatOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
public async initToken() {
|
||||
try {
|
||||
if (
|
||||
this.options.auth?.type === AuthType.TOKEN &&
|
||||
!this.options.auth?.token
|
||||
) {
|
||||
const token = await this.options.auth?.onRefreshToken?.('');
|
||||
this.options.auth.token = token;
|
||||
if (!token) {
|
||||
alert(
|
||||
'The access token is missing. Please check the configuration information.',
|
||||
);
|
||||
}
|
||||
return !!token;
|
||||
}
|
||||
} catch (_) {
|
||||
console.error('[WebSdk Error] initToken error');
|
||||
alert(
|
||||
'The access token is missing. Please check the configuration information.',
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public checkOptions() {
|
||||
if (this.options.auth?.type !== AuthType.TOKEN) {
|
||||
console.error("Non-Token is unsupported; auth's type must be token");
|
||||
alert(
|
||||
"The auth type (unauth) is unsupported yet; auth's type must be token",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (this.options.auth?.type === AuthType.TOKEN) {
|
||||
if (!this.options.auth.onRefreshToken) {
|
||||
console.error('[WebSdk Error] onRefreshToken must be provided');
|
||||
alert('onRefreshToken must be provided');
|
||||
return false;
|
||||
}
|
||||
if (typeof this.options.auth.onRefreshToken !== 'function') {
|
||||
console.error('[WebSdk Error] onRefreshToken must be a function');
|
||||
alert('onRefreshToken must be a function');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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 complexity */
|
||||
import { isMobileOnly } from 'react-device-detect';
|
||||
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { Language, Layout, AuthType } from '@coze-studio/open-chat/types';
|
||||
|
||||
import { type CozeChatOptions } from '@/types/client';
|
||||
import { createGlobalStore, type ClientStore } from '@/store/global';
|
||||
import CozeClientWidget from '@/components/widget';
|
||||
import '@coze-common/assets/style/index.less';
|
||||
import './main.less';
|
||||
|
||||
import { AuthClient } from './auth';
|
||||
const formatOptions = (optionsRaw: CozeChatOptions) => {
|
||||
const options: CozeChatOptions = optionsRaw;
|
||||
const layoutDefault = isMobileOnly ? Layout.MOBILE : Layout.PC;
|
||||
options.config = options.config || {};
|
||||
options.config.botId =
|
||||
options.config.botInfo?.botId ||
|
||||
options.config.botId ||
|
||||
options.config.bot_id ||
|
||||
'';
|
||||
options.ui = optionsRaw.ui || {};
|
||||
|
||||
// 小助手 ui基础配置
|
||||
options.ui.base = Object.assign(
|
||||
{
|
||||
layout: optionsRaw.componentProps?.layout || layoutDefault,
|
||||
lang: optionsRaw.componentProps?.lang || Language.EN,
|
||||
zIndex: optionsRaw.componentProps?.zIndex,
|
||||
icon: optionsRaw.componentProps?.icon,
|
||||
},
|
||||
optionsRaw.ui?.base || {},
|
||||
);
|
||||
|
||||
// chatBot 配置格式化
|
||||
options.ui.chatBot = Object.assign(
|
||||
{
|
||||
title: optionsRaw.componentProps?.title,
|
||||
width: optionsRaw.componentProps?.width,
|
||||
uploadable: optionsRaw.componentProps?.uploadable ?? true,
|
||||
},
|
||||
optionsRaw.ui?.chatBot || {},
|
||||
);
|
||||
|
||||
options.ui.asstBtn = Object.assign(
|
||||
{
|
||||
isNeed: true,
|
||||
},
|
||||
options.ui.asstBtn || {},
|
||||
);
|
||||
options.ui.header = Object.assign(
|
||||
{
|
||||
isShow: true,
|
||||
isNeedClose: true,
|
||||
},
|
||||
options.ui.header || {},
|
||||
);
|
||||
|
||||
return options;
|
||||
};
|
||||
export class WebChatClient {
|
||||
static clients: WebChatClient[] = [];
|
||||
private root: ReturnType<typeof createRoot> | undefined;
|
||||
private readonly defaultRoot?: HTMLDivElement;
|
||||
private readonly globalStore: ClientStore;
|
||||
readonly authClient: AuthClient;
|
||||
readonly chatClientId = nanoid();
|
||||
readonly options: CozeChatOptions;
|
||||
readonly senderName: string;
|
||||
|
||||
public constructor(options: CozeChatOptions) {
|
||||
console.info('WebChatClient constructorxxx', options);
|
||||
this.senderName = `chat-app-sdk-${Date.now()}`;
|
||||
this.options = formatOptions(options);
|
||||
this.authClient = new AuthClient(options);
|
||||
|
||||
const { el } = this.options;
|
||||
|
||||
this.globalStore = createGlobalStore(this);
|
||||
if (!this.authClient.checkOptions()) {
|
||||
return;
|
||||
}
|
||||
let renderEl: HTMLElement;
|
||||
if (!el) {
|
||||
this.defaultRoot = document.createElement('div');
|
||||
document.body.appendChild(this.defaultRoot);
|
||||
renderEl = this.defaultRoot;
|
||||
} else {
|
||||
renderEl = el;
|
||||
}
|
||||
|
||||
this.root = createRoot(renderEl);
|
||||
this.root.render(
|
||||
<CozeClientWidget
|
||||
client={this}
|
||||
globalStore={this.globalStore}
|
||||
position={el ? 'static' : undefined}
|
||||
/>,
|
||||
);
|
||||
|
||||
WebChatClient.clients.push(this);
|
||||
}
|
||||
|
||||
public showChatBot() {
|
||||
this.globalStore.getState().setChatVisible(true);
|
||||
}
|
||||
public hideChatBot() {
|
||||
this.globalStore.getState().setChatVisible(false);
|
||||
}
|
||||
public async getToken() {
|
||||
if (this.options.auth?.type === AuthType.TOKEN) {
|
||||
return await this.options.auth?.onRefreshToken?.('');
|
||||
}
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.root?.unmount();
|
||||
if (this.defaultRoot) {
|
||||
this.defaultRoot.remove();
|
||||
}
|
||||
|
||||
WebChatClient.clients = WebChatClient.clients.filter(c => c !== this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
.coze-chat-sdk {
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
textarea {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
input:focus,
|
||||
p:focus,
|
||||
svg:focus,
|
||||
li:focus,
|
||||
div:focus,
|
||||
textarea:focus,
|
||||
a:focus-visible,
|
||||
input:focus-visible,
|
||||
p:focus-visible,
|
||||
li:focus-visible,
|
||||
div:focus-visible {
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 cls from 'classnames';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const Close = ({
|
||||
classNames,
|
||||
onClick,
|
||||
themeType = 'dark',
|
||||
}: {
|
||||
classNames?: string;
|
||||
onClick: () => void;
|
||||
themeType?: 'dark' | 'light';
|
||||
}) => (
|
||||
<div
|
||||
className={cls(styles.close, classNames, themeType && styles[themeType])}
|
||||
onClick={onClick}
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M4.96977 17.7929C4.57925 18.1834 4.57925 18.8166 4.96977 19.2071C5.3603 19.5976 5.99346 19.5976 6.38399 19.2071L12.1769 13.4142L17.9698 19.2071C18.3603 19.5976 18.9935 19.5976 19.384 19.2071C19.7745 18.8166 19.7745 18.1834 19.384 17.7929L13.5911 12L19.384 6.20711C19.7745 5.81658 19.7745 5.18342 19.384 4.79289C18.9935 4.40237 18.3603 4.40237 17.9698 4.79289L12.1769 10.5858L6.38399 4.79289C5.99347 4.40237 5.3603 4.40237 4.96978 4.79289C4.57925 5.18342 4.57925 5.81658 4.96978 6.20711L10.7627 12L4.96977 17.7929Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
.close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
color: rgba(28,31,35, 80%);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
&.light {
|
||||
color: #FFF;
|
||||
|
||||
&:hover{
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animation-rotate {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation: animation-rotate .6s linear infinite;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 cls from 'classnames';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const Spin = ({ classNames }: { classNames?: string }) => (
|
||||
<div
|
||||
className={cls(styles.spin, classNames)}
|
||||
style={{
|
||||
color: 'rgba(0,100,250, 1)',
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
data-icon="spin"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
x1="0%"
|
||||
y1="100%"
|
||||
x2="100%"
|
||||
y2="100%"
|
||||
id="linearGradient-17"
|
||||
>
|
||||
<stop stop-color="currentColor" stop-opacity="0" offset="0%"></stop>
|
||||
<stop
|
||||
stop-color="currentColor"
|
||||
stop-opacity="0.50"
|
||||
offset="39.9430698%"
|
||||
></stop>
|
||||
<stop stop-color="currentColor" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect
|
||||
fill-opacity="0.01"
|
||||
fill="none"
|
||||
x="0"
|
||||
y="0"
|
||||
width="36"
|
||||
height="36"
|
||||
></rect>
|
||||
<path
|
||||
d="M34,18 C34,9.163444 26.836556,2 18,2 C11.6597233,2 6.18078805,5.68784135 3.59122325,11.0354951"
|
||||
stroke="url(#linearGradient-17)"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 } from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { Layout } from '@coze-studio/open-chat/types';
|
||||
|
||||
import { getCssVars } from '@/util/style';
|
||||
import { type AstBtnProps } from '@/types/chat';
|
||||
import { useGlobalStore } from '@/store/context';
|
||||
import WidgetPng from '@/assets/widget.png';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const AstBtn: FC<AstBtnProps> = ({ position = 'fixed', client }) => {
|
||||
const { chatVisible, setChatVisible, layout } = useGlobalStore(s => ({
|
||||
chatVisible: s.chatVisible,
|
||||
setChatVisible: s.setChatVisible,
|
||||
layout: s.layout,
|
||||
}));
|
||||
|
||||
const { base: baseConf, asstBtn: asstBtnConf } = client?.options?.ui || {};
|
||||
const iconUrl = baseConf?.icon;
|
||||
const zIndex = baseConf?.zIndex;
|
||||
const zIndexStyle = getCssVars({ zIndex });
|
||||
if (chatVisible || !asstBtnConf?.isNeed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ position, ...zIndexStyle }}
|
||||
className={cls(styles['coze-ast-btn'], {
|
||||
[styles.mobile]: layout === Layout.MOBILE,
|
||||
})}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setChatVisible(true);
|
||||
}}
|
||||
>
|
||||
<img alt="logo" src={iconUrl || WidgetPng} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 { createPortal } from 'react-dom';
|
||||
import { useState, type FC, useEffect } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import cls from 'classnames';
|
||||
import { Layout } from '@coze-studio/open-chat/types';
|
||||
|
||||
import { getCssVars } from '@/util/style';
|
||||
import { type ChatContentProps } from '@/types/chat';
|
||||
import { useGlobalStore } from '@/store/context';
|
||||
|
||||
import { Close } from '../icons/close';
|
||||
import { ChatNonIframe } from './chat-non-iframe';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
const ChatSlot: FC<
|
||||
ChatContentProps & { isNewCreated: boolean }
|
||||
// eslint-disable-next-line complexity
|
||||
> = ({ client, isNewCreated }) => {
|
||||
const { chatVisible, setChatVisible, layout, themeType } =
|
||||
useGlobalStore(
|
||||
useShallow(s => ({
|
||||
layout: s.layout,
|
||||
setIframe: s.setIframe,
|
||||
senderName: s.senderName,
|
||||
chatVisible: s.chatVisible,
|
||||
setChatVisible: s.setChatVisible,
|
||||
themeType: s.themeType,
|
||||
})),
|
||||
);
|
||||
const {
|
||||
base: baseConf,
|
||||
chatBot: chatBotConf,
|
||||
header: headerConf,
|
||||
} = client?.options?.ui || {};
|
||||
|
||||
const zIndex = baseConf?.zIndex;
|
||||
const zIndexStyle = getCssVars({ zIndex });
|
||||
const width =
|
||||
layout === Layout.MOBILE ? undefined : chatBotConf?.width || 460;
|
||||
if (!chatVisible) {
|
||||
// 不显示chat框
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cls(styles.iframeWrapper, 'coze-chat-sdk', {
|
||||
[styles.mobile]: layout === Layout.MOBILE,
|
||||
[styles.autoFixContainer]: !isNewCreated,
|
||||
})}
|
||||
style={{
|
||||
display: chatVisible ? 'block' : 'none',
|
||||
width,
|
||||
...zIndexStyle,
|
||||
}}
|
||||
>
|
||||
{headerConf?.isNeedClose !== false ? (
|
||||
<Close
|
||||
onClick={() => {
|
||||
setChatVisible(false);
|
||||
}}
|
||||
classNames={styles.closeBtn}
|
||||
themeType={themeType === 'bg-theme' ? 'light' : 'dark'}
|
||||
/>
|
||||
) : null}
|
||||
<ChatNonIframe client={client} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChatContent: FC<ChatContentProps> = ({ client }) => {
|
||||
const { el } = client?.options?.ui?.chatBot || {};
|
||||
const [chatContentEl] = useState(() => {
|
||||
if (el) {
|
||||
return el;
|
||||
}
|
||||
const elCreated = document.createElement('div');
|
||||
document.body.appendChild(elCreated);
|
||||
return elCreated;
|
||||
});
|
||||
const isNewCreated = chatContentEl !== el;
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (isNewCreated) {
|
||||
document.body.removeChild(chatContentEl);
|
||||
}
|
||||
},
|
||||
[el, chatContentEl],
|
||||
);
|
||||
|
||||
return createPortal(
|
||||
<ChatSlot client={client} isNewCreated={isNewCreated} />,
|
||||
chatContentEl,
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 { useCallback, useEffect, useState, type FC } from 'react';
|
||||
|
||||
import { Language } from '@coze-studio/open-chat/types';
|
||||
import { initI18nInstance, I18n } from '@coze-arch/i18n/raw';
|
||||
import { I18nProvider } from '@coze-arch/i18n/i18n-provider';
|
||||
import {
|
||||
zhCN,
|
||||
enUS,
|
||||
ConfigProvider,
|
||||
LocaleProvider,
|
||||
} from '@coze-arch/bot-semi';
|
||||
|
||||
import { type ChatContentProps } from '@/types/chat';
|
||||
import { useGlobalStore } from '@/store';
|
||||
|
||||
import { NonIframeBot } from './non-iframe-bot';
|
||||
import { NonIframeApp } from './non-iframe-app';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const ChatNonIframe: FC<ChatContentProps> = ({ client }) => {
|
||||
const options = client?.options;
|
||||
const setImagePreview = useGlobalStore(s => s.setImagePreview);
|
||||
const setIframeLoaded = useGlobalStore(s => s.setIframeLoaded);
|
||||
const lang = options?.ui?.base?.lang || Language.EN;
|
||||
const [i18nReady, setI18nReady] = useState(false);
|
||||
const locale = lang === Language.ZH_CN ? zhCN : enUS;
|
||||
|
||||
const onImageClick = useCallback((extra: { url: string }) => {
|
||||
setImagePreview(preview => {
|
||||
preview.url = extra.url;
|
||||
preview.visible = true;
|
||||
});
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
setIframeLoaded(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
initI18nInstance({ lng: lang }).then(() => setI18nReady(true));
|
||||
}, [lang]);
|
||||
|
||||
if (!i18nReady) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<I18nProvider i18n={I18n}>
|
||||
<ConfigProvider>
|
||||
<LocaleProvider locale={locale}>
|
||||
<div className={styles.cozeIframe}>
|
||||
{options?.config?.type === 'app' ? (
|
||||
<NonIframeApp client={client} onImageClick={onImageClick} />
|
||||
) : (
|
||||
<NonIframeBot client={client} onImageClick={onImageClick} />
|
||||
)}
|
||||
</div>
|
||||
</LocaleProvider>
|
||||
</ConfigProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { ImagePreview as ImagePreviewSemi } from '@coze-arch/bot-semi';
|
||||
|
||||
import { useGlobalStore } from '@/store';
|
||||
|
||||
interface PreviewProps {
|
||||
zIndex: number;
|
||||
className?: string;
|
||||
}
|
||||
export const ImagePreview: React.FC<PreviewProps> = ({ zIndex, className }) => {
|
||||
const { imagePreviewUrl, imagePreviewVisible, setImagePreview } =
|
||||
useGlobalStore(
|
||||
useShallow(s => ({
|
||||
imagePreviewVisible: s.imagePreview.visible,
|
||||
imagePreviewUrl: s.imagePreview.url,
|
||||
setImagePreview: s.setImagePreview,
|
||||
})),
|
||||
);
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
setImagePreview(preview => (preview.visible = visible));
|
||||
};
|
||||
const [imageUrl, setImageUrl] = useState(imagePreviewUrl);
|
||||
useEffect(() => {
|
||||
setImageUrl(imagePreviewUrl);
|
||||
(async () => {
|
||||
if (imagePreviewUrl?.startsWith('blob:')) {
|
||||
const base64Url = await revertBlobUrlToBase64(imagePreviewUrl);
|
||||
if (base64Url) {
|
||||
setImageUrl(base64Url);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, [imagePreviewUrl]);
|
||||
return (
|
||||
<ImagePreviewSemi
|
||||
previewCls={className}
|
||||
zIndex={zIndex}
|
||||
src={imageUrl}
|
||||
visible={imagePreviewVisible}
|
||||
onVisibleChange={onVisibleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const revertBlobUrlToBase64 = (blobUrl: string): Promise<string | null> =>
|
||||
new Promise((resolve, reject) => {
|
||||
(async () => {
|
||||
try {
|
||||
const response = await fetch(blobUrl);
|
||||
const blob = await response.blob();
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onloadend = () => {
|
||||
const base64data = reader.result;
|
||||
resolve(base64data as string);
|
||||
};
|
||||
|
||||
reader.onerror = error => {
|
||||
console.error('转换过程中出现错误:', error);
|
||||
resolve(null);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
} catch (error) {
|
||||
console.error('转换过程中出现错误:', error);
|
||||
resolve(null);
|
||||
}
|
||||
})();
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
.coze-ast-btn {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
cursor: pointer;
|
||||
z-index: var(--coze-z-index-iframe);
|
||||
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.16);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.mobile {
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
|
||||
&:active {
|
||||
transform: scale(1.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.iframe-wrapper {
|
||||
position: fixed;
|
||||
z-index: calc(var(--coze-z-index-iframe) - 1);
|
||||
background-color: #fff;
|
||||
|
||||
&:not(.mobile) {
|
||||
height: calc(100% - 40px);
|
||||
min-height: 400px;
|
||||
max-height: 1200px;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 6px 8px 0 rgb(29 28 35 / 6%), 0 0 2px 0 rgb(29 28 35 / 18%);
|
||||
}
|
||||
|
||||
.loading {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 12px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.extra-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.coze-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.mobile {
|
||||
inset: 0;
|
||||
|
||||
.close-btn {
|
||||
right: 12px;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
.coze-iframe {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.auto-fix-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
min-height: auto;
|
||||
max-height: auto;
|
||||
}
|
||||
}
|
||||
@@ -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 { type FC } from 'react';
|
||||
|
||||
import { getCssVars } from '@/util/style';
|
||||
import { type CozeWidgetProps, type WidgetAdapterProps } from '@/types/chat';
|
||||
import { GlobalStoreProvider } from '@/store/context';
|
||||
import { useMessageInteract } from '@/hooks/use-message-interact';
|
||||
import { useImagePreview } from '@/hooks/use-image-preview';
|
||||
|
||||
import { ImagePreview } from './image-preview';
|
||||
import { ChatContent } from './chat-content';
|
||||
import { AstBtn } from './ast-btn';
|
||||
|
||||
const IFRAME_INDEX = 2;
|
||||
|
||||
const WidgetAdapter: FC<WidgetAdapterProps> = ({ client, position }) => {
|
||||
useImagePreview(client);
|
||||
useMessageInteract(client.chatClientId, client.options);
|
||||
|
||||
const { base: baseConf } = client?.options?.ui || {};
|
||||
const zIndex = baseConf?.zIndex;
|
||||
const zIndexStyle = getCssVars({ zIndex });
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChatContent client={client} />
|
||||
<ImagePreview
|
||||
zIndex={zIndexStyle['--coze-z-index-iframe'] + IFRAME_INDEX}
|
||||
/>
|
||||
<AstBtn client={client} position={position} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CozeClientWidget: FC<CozeWidgetProps> = props => (
|
||||
<GlobalStoreProvider globalStore={props.globalStore}>
|
||||
<WidgetAdapter {...props} />
|
||||
</GlobalStoreProvider>
|
||||
);
|
||||
|
||||
export default CozeClientWidget;
|
||||
@@ -0,0 +1,4 @@
|
||||
.extra-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 complexity */
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { BuilderChat } from '@coze-studio/open-chat';
|
||||
|
||||
import { type ChatContentProps } from '@/types/chat';
|
||||
import { useGlobalStore } from '@/store';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
type IOnImageClick = (extra: { url: string }) => void;
|
||||
|
||||
export const NonIframeApp: FC<
|
||||
ChatContentProps & { onImageClick: IOnImageClick }
|
||||
> = ({ client, onImageClick }) => {
|
||||
const options = client?.options;
|
||||
const setThemeType = useGlobalStore(s => s.setThemeType);
|
||||
const isNeedExtra = options?.ui?.header?.isNeedClose ?? true;
|
||||
const areaUi = {
|
||||
showInputArea: true,
|
||||
isDisabled: false,
|
||||
uploadable: options?.ui?.chatBot?.uploadable,
|
||||
isNeedClearContext: options?.ui?.chatBot?.isNeedClearContext ?? true,
|
||||
isNeedClearMessage: false,
|
||||
isNeedAddNewConversation:
|
||||
options?.ui?.chatBot?.isNeedAddNewConversation ?? true,
|
||||
isNeedFunctionCallMessage:
|
||||
options?.ui?.chatBot?.isNeedFunctionCallMessage ?? true,
|
||||
isNeedQuote: options?.ui?.chatBot?.isNeedQuote,
|
||||
feedback: options?.ui?.chatBot?.feedback,
|
||||
header: {
|
||||
isShow: true,
|
||||
title: options?.ui?.chatBot?.title,
|
||||
icon: options?.ui?.base?.icon,
|
||||
...options?.ui?.header,
|
||||
extra: isNeedExtra ? <div className={styles['extra-close']} /> : null,
|
||||
},
|
||||
conversations: options?.ui?.conversations,
|
||||
input: {
|
||||
isNeedAudio: options?.ui?.chatBot?.isNeedAudio,
|
||||
},
|
||||
footer: options?.ui?.footer,
|
||||
};
|
||||
return (
|
||||
<BuilderChat
|
||||
workflow={{
|
||||
id: options?.config?.appInfo?.workflowId,
|
||||
parameters: {
|
||||
...options?.config?.appInfo?.parameters,
|
||||
},
|
||||
}}
|
||||
project={{
|
||||
type: 'app',
|
||||
mode: 'websdk',
|
||||
id: options?.config?.appInfo?.appId || '',
|
||||
conversationName: 'Default', // 走兜底逻辑
|
||||
layout: options?.ui?.base?.layout,
|
||||
version: options?.config.appInfo?.version,
|
||||
}}
|
||||
userInfo={{
|
||||
url: options?.userInfo?.url || '',
|
||||
nickname: options?.userInfo?.nickname || '',
|
||||
id: options?.userInfo?.id || '',
|
||||
}}
|
||||
areaUi={areaUi}
|
||||
auth={{
|
||||
type: 'external',
|
||||
token: options?.auth?.token,
|
||||
refreshToken: options?.auth?.onRefreshToken,
|
||||
}}
|
||||
eventCallbacks={{
|
||||
onImageClick,
|
||||
onThemeChange: setThemeType,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
.chat-app-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.extra-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 } from 'react';
|
||||
|
||||
import { WebSdkChat } from '@coze-studio/open-chat';
|
||||
|
||||
import { getChatConfig } from '@/util/get-chat-config';
|
||||
import { type ChatContentProps } from '@/types/chat';
|
||||
import { useGlobalStore } from '@/store';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
type IOnImageClick = (extra: { url: string }) => void;
|
||||
|
||||
export const NonIframeBot: FC<
|
||||
ChatContentProps & { onImageClick: IOnImageClick }
|
||||
> = props => {
|
||||
const title = props.client.options.ui?.chatBot?.title;
|
||||
const icon = props.client.options.ui?.base?.icon;
|
||||
const headerExtra = props.client.options.ui?.header?.isNeedClose ? (
|
||||
<div className={styles['extra-close']} />
|
||||
) : null;
|
||||
const layout = props.client.options.ui?.base?.layout;
|
||||
const { onImageClick } = props;
|
||||
const { userInfo } = props.client.options;
|
||||
const setThemeType = useGlobalStore(s => s.setThemeType);
|
||||
|
||||
const iframeParams = getChatConfig(
|
||||
props.client.chatClientId,
|
||||
props.client.options,
|
||||
);
|
||||
if (iframeParams.chatConfig.auth) {
|
||||
iframeParams.chatConfig.auth.onRefreshToken =
|
||||
props.client.options.auth?.onRefreshToken;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.chatAppWrapper}>
|
||||
<WebSdkChat
|
||||
title={title || ''}
|
||||
icon={icon}
|
||||
chatConfig={iframeParams.chatConfig}
|
||||
headerExtra={headerExtra}
|
||||
layout={layout}
|
||||
style={{
|
||||
height: '100%',
|
||||
}}
|
||||
onImageClick={onImageClick}
|
||||
onThemeChange={setThemeType}
|
||||
userInfo={userInfo}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -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 { RouterProvider } from 'react-router-dom';
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
|
||||
import { initI18nInstance, I18n } from '@coze-arch/i18n/raw';
|
||||
import { I18nProvider } from '@coze-arch/i18n/i18n-provider';
|
||||
|
||||
import { devRouter } from './routes';
|
||||
|
||||
const DevApp: FC = () => {
|
||||
const [i18nReady, setI18nReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
initI18nInstance().then(() => setI18nReady(true));
|
||||
}, []);
|
||||
|
||||
if (!i18nReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<I18nProvider i18n={I18n}>
|
||||
<RouterProvider router={devRouter} />
|
||||
</I18nProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default DevApp;
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 './main.less';
|
||||
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import DevApp from './App';
|
||||
|
||||
const rootEl = document.createElement('div');
|
||||
rootEl.setAttribute('className', 'coze-chat-sdk');
|
||||
document.body.append(rootEl);
|
||||
|
||||
const root = createRoot(rootEl);
|
||||
root.render(<DevApp />);
|
||||
@@ -0,0 +1,11 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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, useState } from 'react';
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import { OpenApiSource } from '@coze-studio/open-chat/types';
|
||||
import { WebSdkChat } from '@coze-studio/open-chat';
|
||||
|
||||
const uid = nanoid();
|
||||
|
||||
const botConfig = {
|
||||
bot_id: process.env.CHAT_APP_INDEX_COZE_BOT_ID || '',
|
||||
user: uid,
|
||||
conversation_id: uid,
|
||||
source: OpenApiSource.WebSdk,
|
||||
};
|
||||
|
||||
const TestAppWidget: FC = () => {
|
||||
const [visible] = useState(false);
|
||||
// 触发更新
|
||||
return (
|
||||
<>
|
||||
{visible ? (
|
||||
<WebSdkChat
|
||||
title="客服小助手"
|
||||
chatConfig={botConfig}
|
||||
style={{ height: 800 }}
|
||||
useInIframe={false}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TestAppWidget;
|
||||
@@ -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 FC } from 'react';
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import { OpenApiSource } from '@coze-studio/open-chat/types';
|
||||
import { WebSdkChat } from '@coze-studio/open-chat';
|
||||
|
||||
const uid = nanoid();
|
||||
|
||||
const botConfig = {
|
||||
user: uid,
|
||||
conversation_id: uid,
|
||||
bot_id: process.env.CHAT_APP_INDEX_COZE_BOT_ID || '',
|
||||
source: OpenApiSource.WebSdk,
|
||||
};
|
||||
|
||||
const TestChatDemo: FC = () => (
|
||||
<WebSdkChat
|
||||
title="客服小助手"
|
||||
chatConfig={botConfig}
|
||||
className="absolute top-[50px]"
|
||||
useInIframe={false}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 50,
|
||||
top: 50,
|
||||
width: 460,
|
||||
height: 700,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default TestChatDemo;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user