From 1f12df360f154192dbba0ebd558f6223eedd7e95 Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Sun, 26 May 2024 22:36:18 +0000 Subject: [PATCH 01/21] write db chat color theme --- api/controllers/console/app/site.py | 4 ++ api/fields/app_fields.py | 2 + api/models/model.py | 2 + web/.vscode/settings.example.json | 2 +- .../app/overview/settings/index.tsx | 71 +++++++++++++++++-- web/i18n/en-US/app-overview.ts | 4 ++ web/types/app.ts | 6 ++ 7 files changed, 86 insertions(+), 5 deletions(-) diff --git a/api/controllers/console/app/site.py b/api/controllers/console/app/site.py index 2024db65b29975..6aa9f0b475f161 100644 --- a/api/controllers/console/app/site.py +++ b/api/controllers/console/app/site.py @@ -20,6 +20,8 @@ def parse_app_site_args(): parser.add_argument('icon_background', type=str, required=False, location='json') parser.add_argument('description', type=str, required=False, location='json') parser.add_argument('default_language', type=supported_language, required=False, location='json') + parser.add_argument('chat_color_theme', type=str, required=False, location='json') + parser.add_argument('chat_color_theme_inverted', type=bool, required=False, location='json') parser.add_argument('customize_domain', type=str, required=False, location='json') parser.add_argument('copyright', type=str, required=False, location='json') parser.add_argument('privacy_policy', type=str, required=False, location='json') @@ -55,6 +57,8 @@ def post(self, app_model): 'icon_background', 'description', 'default_language', + 'chat_color_theme', + 'chat_color_theme_inverted', 'customize_domain', 'copyright', 'privacy_policy', diff --git a/api/fields/app_fields.py b/api/fields/app_fields.py index 9de578544140f4..83045f5c64d4bb 100644 --- a/api/fields/app_fields.py +++ b/api/fields/app_fields.py @@ -111,6 +111,8 @@ 'icon_background': fields.String, 'description': fields.String, 'default_language': fields.String, + 'chat_color_theme': fields.String, + 'chat_color_theme_inverted': fields.Boolean, 'customize_domain': fields.String, 'copyright': fields.String, 'privacy_policy': fields.String, diff --git a/api/models/model.py b/api/models/model.py index c7768acbf3b4ea..ecb89861db9410 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -1042,6 +1042,8 @@ class Site(db.Model): icon_background = db.Column(db.String(255)) description = db.Column(db.Text) default_language = db.Column(db.String(255), nullable=False) + chat_color_theme = db.Column(db.String(255)) + chat_color_theme_inverted = db.Column(db.Boolean, nullable=False, server_default=db.text('false')) copyright = db.Column(db.String(255)) privacy_policy = db.Column(db.String(255)) show_workflow_steps = db.Column(db.Boolean, nullable=False, server_default=db.text('true')) diff --git a/web/.vscode/settings.example.json b/web/.vscode/settings.example.json index 6162d021d0f8e6..a2dfe7c6694559 100644 --- a/web/.vscode/settings.example.json +++ b/web/.vscode/settings.example.json @@ -22,4 +22,4 @@ }, "typescript.tsdk": "node_modules/typescript/lib", "typescript.enablePromptUseWorkspaceTsdk": true -} \ No newline at end of file +} diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 6994d9c280d7f6..af7e5afdfe91f0 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -28,6 +28,8 @@ export type ConfigParams = { title: string description: string default_language: string + chat_color_theme: string + chat_color_theme_inverted: boolean prompt_public: boolean copyright: string privacy_policy: string @@ -48,8 +50,27 @@ const SettingsModal: FC = ({ const { notify } = useToastContext() const [isShowMore, setIsShowMore] = useState(false) const { icon, icon_background } = appInfo - const { title, description, copyright, privacy_policy, custom_disclaimer, default_language, show_workflow_steps } = appInfo.site - const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps }) + const { + title, + description, + chat_color_theme, + chat_color_theme_inverted, + copyright, + privacy_policy, + custom_disclaimer, + default_language, + show_workflow_steps + } = appInfo.site + const [inputInfo, setInputInfo] = useState({ + title, + desc: description, + chatColorTheme: chat_color_theme, + chatColorThemeInverted: chat_color_theme_inverted, + copyright, + privacyPolicy: privacy_policy, + customDisclaimer: custom_disclaimer, + show_workflow_steps + }) const [language, setLanguage] = useState(default_language) const [saveLoading, setSaveLoading] = useState(false) const { t } = useTranslation() @@ -58,7 +79,16 @@ const SettingsModal: FC = ({ const [emoji, setEmoji] = useState({ icon, icon_background }) useEffect(() => { - setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps }) + setInputInfo({ + title, + desc: description, + chatColorTheme: chat_color_theme, + chatColorThemeInverted: chat_color_theme_inverted, + copyright, + privacyPolicy: privacy_policy, + customDisclaimer: custom_disclaimer, + show_workflow_steps + }) setLanguage(default_language) setEmoji({ icon, icon_background }) }, [appInfo]) @@ -75,11 +105,27 @@ const SettingsModal: FC = ({ notify({ type: 'error', message: t('app.newApp.nameNotEmpty') }) return } + + const validateColorHex = (hex: string) => { + const regex = /#([A-Fa-f0-9]{6})/ + const check = regex.test(hex) + return check + } + + if (inputInfo !== null) { + if (!validateColorHex(inputInfo.chatColorTheme)) { + notify({ type: 'error', message: t(`${prefixSettings}.invalidHexMessage`) }) + return + } + } + setSaveLoading(true) const params = { title: inputInfo.title, description: inputInfo.desc, default_language: language, + chat_color_theme: inputInfo.chatColorTheme, + chat_color_theme_inverted: inputInfo.chatColorThemeInverted, prompt_public: false, copyright: inputInfo.copyright, privacy_policy: inputInfo.privacyPolicy, @@ -95,7 +141,13 @@ const SettingsModal: FC = ({ const onChange = (field: string) => { return (e: React.ChangeEvent) => { - setInputInfo(item => ({ ...item, [field]: e.target.value })) + let value: string | boolean + if (e.target.type === 'checkbox') + value = (e.target as HTMLInputElement).checked + else + value = e.target.value + + setInputInfo(item => ({ ...item, [field]: value })) } } @@ -144,6 +196,17 @@ const SettingsModal: FC = ({ onSelect={item => setInputInfo({ ...inputInfo, show_workflow_steps: item.value === 'true' })} /> } +
{t(`${prefixSettings}.chatColorTheme`)}
+

{t(`${prefixSettings}.chatColorThemeDesc`)}

+ +
+ +

{t(`${prefixSettings}.chatColorThemeInverted`)}

+
{!isShowMore &&
setIsShowMore(true)}>
{t(`${prefixSettings}.more.entry`)}
diff --git a/web/i18n/en-US/app-overview.ts b/web/i18n/en-US/app-overview.ts index 0887ef25b78c8c..b8c7999415d596 100644 --- a/web/i18n/en-US/app-overview.ts +++ b/web/i18n/en-US/app-overview.ts @@ -49,6 +49,10 @@ const translation = { show: 'Show', hide: 'Hide', }, + chatColorTheme: 'Chat color theme', + chatColorThemeDesc: 'Set the color theme of the chatbot', + chatColorThemeInverted: 'Inverted', + invalidHexMessage: 'Invalid hex value', more: { entry: 'Show more settings', copyright: 'Copyright', diff --git a/web/types/app.ts b/web/types/app.ts index 3ad4592e67e923..294d2980a86867 100644 --- a/web/types/app.ts +++ b/web/types/app.ts @@ -246,6 +246,12 @@ export type SiteConfig = { title: string /** Application Description will be shown in the Client */ description: string + /** Define the color in hex for different elements of the chatbot, such as: + * The header, the button , etc. + */ + chat_color_theme: string + /** Invert the color of the theme set in chat_color_theme */ + chat_color_theme_inverted: boolean /** Author */ author: string /** User Support Email Address */ From 2e1e21d21a7942059881972b5bc30b74d9790ef4 Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Mon, 27 May 2024 05:14:08 +0000 Subject: [PATCH 02/21] theme class & context --- api/controllers/web/site.py | 2 + .../components/share/chatbot/theme-context.ts | 61 +++++++++++++++++++ web/models/share.ts | 2 + 3 files changed, 65 insertions(+) create mode 100644 web/app/components/share/chatbot/theme-context.ts diff --git a/api/controllers/web/site.py b/api/controllers/web/site.py index c5c70d810a3a5b..c307959b204459 100644 --- a/api/controllers/web/site.py +++ b/api/controllers/web/site.py @@ -26,6 +26,8 @@ class AppSiteApi(WebApiResource): site_fields = { 'title': fields.String, + 'chat_color_theme': fields.String, + 'chat_color_theme_inverted': fields.Boolean, 'icon': fields.String, 'icon_background': fields.String, 'description': fields.String, diff --git a/web/app/components/share/chatbot/theme-context.ts b/web/app/components/share/chatbot/theme-context.ts new file mode 100644 index 00000000000000..ba0f53d05e9084 --- /dev/null +++ b/web/app/components/share/chatbot/theme-context.ts @@ -0,0 +1,61 @@ +import type { Context } from 'use-context-selector' +import { createContext, useContext } from 'use-context-selector' + +export class Theme { + private chatColorTheme: string | null + private chatColorThemeInverted: boolean + + public primaryColor = '#1C64F2' + public backgroundHeaderColorClass = 'bg-gradient-to-r from-blue-600 to-sky-500' + public headerBorderBottomClass = '' + public colorFontOnHeaderClass = 'text-white' + public colorPathOnHeader = 'white' + public backgroundButtonDefaultColorClass = 'btn-primary' + public roundedBackgroundColorClass = 'bg-indigo-25' + public chatBubbleColorClass = 'bg-blue-500' + public themeContext?: Context + + constructor(chatColorTheme: string | null = null, chatColorThemeInverted = false) { + this.chatColorTheme = chatColorTheme + this.chatColorThemeInverted = chatColorThemeInverted + this.configCustomColor() + this.configInvertedColor() + } + + private configCustomColor() { + if (this.chatColorTheme !== null) { + this.primaryColor = this.chatColorTheme + this.backgroundHeaderColorClass = `bg-[${this.primaryColor}]` + this.backgroundButtonDefaultColorClass = `bg-[${this.primaryColor}]` + this.roundedBackgroundColorClass = `bg-[${this.primaryColor}]/5` + this.chatBubbleColorClass = `bg-[${this.primaryColor}]/10` + } + } + + private configInvertedColor() { + if (this.chatColorThemeInverted) { + this.backgroundHeaderColorClass = 'bg-white' + this.colorFontOnHeaderClass = `text-[${this.primaryColor}]` + this.headerBorderBottomClass = 'border-b border-gray-200' + this.colorPathOnHeader = this.primaryColor + } + } +} + +export class ThemeBuilder { + public theme: Theme | null = null + private buildChecker = false + + public buildTheme(chatColorTheme: string | null = null, chatColorThemeInverted = false) { + if (!this.buildChecker) { + this.theme = new Theme(chatColorTheme, chatColorThemeInverted) + this.buildChecker = true + } + else { + throw new Error('Theme already built') + } + } +} + +const ThemeContext = createContext(new ThemeBuilder()) +export const useThemeContext = () => useContext(ThemeContext) diff --git a/web/models/share.ts b/web/models/share.ts index 32b565eb654311..11ac17a4985a97 100644 --- a/web/models/share.ts +++ b/web/models/share.ts @@ -11,6 +11,8 @@ export type ConversationItem = { export type SiteInfo = { title: string + chat_color_theme: string + chat_color_theme_inverted: boolean icon?: string icon_background?: string description?: string From 69ed4d3eff368dee4ac5ed061d1ef6683ddb449d Mon Sep 17 00:00:00 2001 From: crazywoola <427733928@qq.com> Date: Tue, 18 Jun 2024 13:21:26 +0800 Subject: [PATCH 03/21] resolve --- web/app/components/base/button/index.tsx | 9 ++++-- .../embedded-chatbot/theme}/theme-context.ts | 32 ++++++++----------- .../base/chat/embedded-chatbot/theme/utils.ts | 29 +++++++++++++++++ 3 files changed, 49 insertions(+), 21 deletions(-) rename web/app/components/{share/chatbot => base/chat/embedded-chatbot/theme}/theme-context.ts (55%) create mode 100644 web/app/components/base/chat/embedded-chatbot/theme/utils.ts diff --git a/web/app/components/base/button/index.tsx b/web/app/components/base/button/index.tsx index ee73eba481f82f..281fe36f5a9f58 100644 --- a/web/app/components/base/button/index.tsx +++ b/web/app/components/base/button/index.tsx @@ -1,8 +1,9 @@ -import React from 'react' +import React, { CSSProperties } from 'react' import { type VariantProps, cva } from 'class-variance-authority' import classNames from 'classnames' import Spinner from '../spinner' + const buttonVariants = cva( 'btn disabled:btn-disabled', { @@ -28,16 +29,18 @@ const buttonVariants = cva( ) export type ButtonProps = { - loading?: boolean + loading?: boolean, + styleCss? : CSSProperties } & React.ButtonHTMLAttributes & VariantProps const Button = React.forwardRef( - ({ className, variant, size, loading, children, ...props }, ref) => { + ({ className, variant, size, loading, styleCss, children, ...props }, ref) => { return (
-
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv)}
+
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeContext.theme?.primaryColor ?? 'rgb(21, 94, 239) ', isTestEnv)}
diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index af7e5afdfe91f0..5e464241d67f9b 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -106,7 +106,10 @@ const SettingsModal: FC = ({ return } - const validateColorHex = (hex: string) => { + const validateColorHex = (hex: string | null) => { + if (hex === null) + return true + const regex = /#([A-Fa-f0-9]{6})/ const check = regex.test(hex) return check diff --git a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts index 8e98b3f0099c5a..b09de5957da346 100644 --- a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts +++ b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts @@ -13,6 +13,7 @@ export class Theme { public backgroundButtonDefaultColorStyle = 'background-color: #1C64F2' public roundedBackgroundColorStyle = 'background-color: rgb(245 248 255)' public chatBubbleColorStyle = 'background-color: rgb(225 239 254)' + public chatBubbleColor = 'rgb(225 239 254)' constructor(chatColorTheme: string | null = null, chatColorThemeInverted = false) { this.chatColorTheme = chatColorTheme @@ -28,6 +29,7 @@ export class Theme { this.backgroundButtonDefaultColorStyle = `background-color: ${this.primaryColor}` this.roundedBackgroundColorStyle = `background-color: ${hexToRGBA(this.primaryColor, 0.05)}` this.chatBubbleColorStyle = `background-color: ${hexToRGBA(this.primaryColor, 0.15)}` + this.chatBubbleColor = `${hexToRGBA(this.primaryColor, 0.15)}` } } From dd262f19565e371a5a859e7db36202c0fe8442a2 Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Wed, 29 May 2024 06:55:02 +0000 Subject: [PATCH 05/21] fix --- .../app/overview/embedded/index.tsx | 4 ++-- .../app/overview/settings/index.tsx | 4 ++-- .../embedded-chatbot/theme/theme-context.ts | 24 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx index e3a0824f1c8cac..88879a81764b41 100644 --- a/web/app/components/app/overview/embedded/index.tsx +++ b/web/app/components/app/overview/embedded/index.tsx @@ -84,7 +84,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam copy(splitUrl[1]) } else { - copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeContext.theme?.primaryColor ?? 'rgb(21, 94, 239)', isTestEnv)) + copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeContext.theme?.primaryColor ?? '#1C64F2', isTestEnv)) } setIsCopied({ ...isCopied, [option]: true }) } @@ -164,7 +164,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam
-
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeContext.theme?.primaryColor ?? 'rgb(21, 94, 239) ', isTestEnv)}
+
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeContext.theme?.primaryColor ?? '#1C64F2', isTestEnv)}
diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 5e464241d67f9b..957a888e970e73 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -107,7 +107,7 @@ const SettingsModal: FC = ({ } const validateColorHex = (hex: string | null) => { - if (hex === null) + if (hex === null || hex.length === 0) return true const regex = /#([A-Fa-f0-9]{6})/ @@ -202,7 +202,7 @@ const SettingsModal: FC = ({
{t(`${prefixSettings}.chatColorTheme`)}

{t(`${prefixSettings}.chatColorThemeDesc`)}

diff --git a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts index b09de5957da346..a18a47f6ac2834 100644 --- a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts +++ b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts @@ -6,13 +6,13 @@ export class Theme { private chatColorThemeInverted: boolean public primaryColor = '#1C64F2' - public backgroundHeaderColorStyle = 'background-image: linear-gradient(to right, #2563eb, #0ea5e9)' + public backgroundHeaderColorStyle = 'backgroundImage: linear-gradient(to right, #2563eb, #0ea5e9)' public headerBorderBottomStyle = '' public colorFontOnHeaderStyle = 'color: white' public colorPathOnHeader = 'white' - public backgroundButtonDefaultColorStyle = 'background-color: #1C64F2' - public roundedBackgroundColorStyle = 'background-color: rgb(245 248 255)' - public chatBubbleColorStyle = 'background-color: rgb(225 239 254)' + public backgroundButtonDefaultColorStyle = 'backgroundColor: #1C64F2' + public roundedBackgroundColorStyle = 'backgroundColor: rgb(245 248 255)' + public chatBubbleColorStyle = 'backgroundColor: rgb(225 239 254)' public chatBubbleColor = 'rgb(225 239 254)' constructor(chatColorTheme: string | null = null, chatColorThemeInverted = false) { @@ -23,21 +23,21 @@ export class Theme { } private configCustomColor() { - if (this.chatColorTheme !== null) { - this.primaryColor = this.chatColorTheme - this.backgroundHeaderColorStyle = `background-color: ${this.primaryColor}` - this.backgroundButtonDefaultColorStyle = `background-color: ${this.primaryColor}` - this.roundedBackgroundColorStyle = `background-color: ${hexToRGBA(this.primaryColor, 0.05)}` - this.chatBubbleColorStyle = `background-color: ${hexToRGBA(this.primaryColor, 0.15)}` + if (this.chatColorTheme !== null && this.chatColorTheme !== '') { + this.primaryColor = this.chatColorTheme ?? '#1C64F2' + this.backgroundHeaderColorStyle = `backgroundColor: ${this.primaryColor}` + this.backgroundButtonDefaultColorStyle = `backgroundColor: ${this.primaryColor}` + this.roundedBackgroundColorStyle = `backgroundColor: ${hexToRGBA(this.primaryColor, 0.05)}` + this.chatBubbleColorStyle = `backgroundColor: ${hexToRGBA(this.primaryColor, 0.15)}` this.chatBubbleColor = `${hexToRGBA(this.primaryColor, 0.15)}` } } private configInvertedColor() { if (this.chatColorThemeInverted) { - this.backgroundHeaderColorStyle = 'background-color: #ffffff' + this.backgroundHeaderColorStyle = 'backgroundColor: #ffffff' this.colorFontOnHeaderStyle = `color: ${this.primaryColor}` - this.headerBorderBottomStyle = 'border: 1px solid #ccc' + this.headerBorderBottomStyle = 'borderBottom: 1px solid #ccc' this.colorPathOnHeader = this.primaryColor } } From 8fcfd4b4596246f598228c8b36bf14321485a0bf Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Thu, 30 May 2024 20:31:38 +0000 Subject: [PATCH 06/21] fix: handle undefined values --- web/app/components/app/overview/embedded/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx index 88879a81764b41..f7bb1c00f518ae 100644 --- a/web/app/components/app/overview/embedded/index.tsx +++ b/web/app/components/app/overview/embedded/index.tsx @@ -12,7 +12,7 @@ import type { SiteInfo } from '@/models/share' import { useThemeContext } from '@/app/components/base/chat/embedded-chatbot/theme/theme-context' type Props = { - siteInfo: SiteInfo + siteInfo?: SiteInfo isShow: boolean onClose: () => void accessToken: string @@ -75,7 +75,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam const { langeniusVersionInfo } = useAppContext() const themeContext = useThemeContext() - themeContext.buildTheme(siteInfo.chat_color_theme, siteInfo.chat_color_theme_inverted) + themeContext.buildTheme(siteInfo?.chat_color_theme ?? '#1C64F2', siteInfo?.chat_color_theme_inverted ?? false) const isTestEnv = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT' const onClickCopy = () => { if (option === 'chromePlugin') { From 82ec4d6569d5098e04cbe1dbc0992819049d41cf Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Mon, 3 Jun 2024 05:37:45 +0000 Subject: [PATCH 07/21] update migrations --- .../bb8273f667ad_add_chatbot_color_theme.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 api/migrations/versions/bb8273f667ad_add_chatbot_color_theme.py diff --git a/api/migrations/versions/bb8273f667ad_add_chatbot_color_theme.py b/api/migrations/versions/bb8273f667ad_add_chatbot_color_theme.py new file mode 100644 index 00000000000000..9ca29b222c3c20 --- /dev/null +++ b/api/migrations/versions/bb8273f667ad_add_chatbot_color_theme.py @@ -0,0 +1,33 @@ +"""add chatbot color theme + +Revision ID: bb8273f667ad +Revises: 4ff534e1eb11 +Create Date: 2024-06-23 17:09:36.733294 + +""" +import sqlalchemy as sa +from alembic import op + +import models as models + +# revision identifiers, used by Alembic. +revision = 'bb8273f667ad' +down_revision = '4ff534e1eb11' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('sites', schema=None) as batch_op: + batch_op.add_column(sa.Column('show_workflow_steps', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('sites', schema=None) as batch_op: + batch_op.drop_column('show_workflow_steps') + + # ### end Alembic commands ### From cf60d44917b54ff21396b5ae9053cb1703b30078 Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Mon, 3 Jun 2024 05:58:09 +0000 Subject: [PATCH 08/21] polish --- web/app/components/app/app-publisher/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index c330b4c270ce43..d7e9856ce46cce 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -226,6 +226,7 @@ const AppPublisher = ({ setEmbeddingModalOpen(false)} appBaseUrl={appBaseURL} From 67d6c5b389555be107a9e7d6f712d7d3cb718daa Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Wed, 5 Jun 2024 23:23:45 -0500 Subject: [PATCH 09/21] isChat conditional rendering --- web/app/components/app/overview/appCard.tsx | 1 + .../app/overview/settings/index.tsx | 24 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/web/app/components/app/overview/appCard.tsx b/web/app/components/app/overview/appCard.tsx index 31bcff7f3a2da3..4f5f93f22b9ac4 100644 --- a/web/app/components/app/overview/appCard.tsx +++ b/web/app/components/app/overview/appCard.tsx @@ -247,6 +247,7 @@ function AppCard({ ? ( <> setShowSettingsModal(false)} diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 957a888e970e73..67e37e9cbfef6b 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -17,6 +17,7 @@ import { useToastContext } from '@/app/components/base/toast' import { languages } from '@/i18n/language' export type ISettingsModalProps = { + isChat: boolean appInfo: AppDetailResponse isShow: boolean defaultValue?: string @@ -42,6 +43,7 @@ export type ConfigParams = { const prefixSettings = 'appOverview.overview.appInfo.settings' const SettingsModal: FC = ({ + isChat, appInfo, isShow = false, onClose, @@ -199,17 +201,17 @@ const SettingsModal: FC = ({ onSelect={item => setInputInfo({ ...inputInfo, show_workflow_steps: item.value === 'true' })} /> } -
{t(`${prefixSettings}.chatColorTheme`)}
-

{t(`${prefixSettings}.chatColorThemeDesc`)}

- -
- -

{t(`${prefixSettings}.chatColorThemeInverted`)}

-
+ {isChat && <>
{t(`${prefixSettings}.chatColorTheme`)}
+

{t(`${prefixSettings}.chatColorThemeDesc`)}

+ +
+ +

{t(`${prefixSettings}.chatColorThemeInverted`)}

+
} {!isShowMore &&
setIsShowMore(true)}>
{t(`${prefixSettings}.more.entry`)}
From f87ab144f05b943479b7cdf0f7292a02dc0dd40b Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Wed, 5 Jun 2024 23:46:35 -0500 Subject: [PATCH 10/21] copy style of StepTwo checkbox --- web/app/components/app/overview/settings/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 67e37e9cbfef6b..cd106a43fa52a0 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -209,8 +209,13 @@ const SettingsModal: FC = ({ placeholder= 'E.g #A020F0' />
- -

{t(`${prefixSettings}.chatColorThemeInverted`)}

+ + +

{t(`${prefixSettings}.chatColorThemeInverted`)}

} {!isShowMore &&
setIsShowMore(true)}>
From 1d0371d611d9fd58b2a8463946abe0a5787a2632 Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Thu, 6 Jun 2024 14:25:08 -0500 Subject: [PATCH 11/21] fix: theme not being updated --- .../base/chat/embedded-chatbot/theme/theme-context.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts index a18a47f6ac2834..0f7cfa41b1a2f4 100644 --- a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts +++ b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts @@ -2,8 +2,8 @@ import { createContext, useContext } from 'use-context-selector' import { hexToRGBA } from './utils' export class Theme { - private chatColorTheme: string | null - private chatColorThemeInverted: boolean + public chatColorTheme: string | null + public chatColorThemeInverted: boolean public primaryColor = '#1C64F2' public backgroundHeaderColorStyle = 'backgroundImage: linear-gradient(to right, #2563eb, #0ea5e9)' @@ -52,6 +52,12 @@ export class ThemeBuilder { this.theme = new Theme(chatColorTheme, chatColorThemeInverted) this.buildChecker = true } + else { + if (this.theme?.chatColorTheme !== chatColorTheme || this.theme?.chatColorThemeInverted !== chatColorThemeInverted) { + this.theme = new Theme(chatColorTheme, chatColorThemeInverted) + this.buildChecker = true + } + } } } From 7d6c32a8bbdaf85f9b82cf2f424a4a645b5b4738 Mon Sep 17 00:00:00 2001 From: crazywoola <427733928@qq.com> Date: Tue, 18 Jun 2024 13:36:58 +0800 Subject: [PATCH 12/21] fix: lint --- .../versions/81ae43883c56_merge_branches.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 api/migrations/versions/81ae43883c56_merge_branches.py diff --git a/api/migrations/versions/81ae43883c56_merge_branches.py b/api/migrations/versions/81ae43883c56_merge_branches.py new file mode 100644 index 00000000000000..6f6a4db545fea1 --- /dev/null +++ b/api/migrations/versions/81ae43883c56_merge_branches.py @@ -0,0 +1,22 @@ +"""merge branches + +Revision ID: 81ae43883c56 +Revises: 35816820a63d, 7b45942e39bb +Create Date: 2024-06-18 05:26:30.668016 + +""" +import models as models + +# revision identifiers, used by Alembic. +revision = '81ae43883c56' +down_revision = ('35816820a63d', '7b45942e39bb') +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass From 4a0dff1124a336d4690fb0f6e0bd9a6d5e30edf0 Mon Sep 17 00:00:00 2001 From: dromerolovo Date: Mon, 24 Jun 2024 11:50:33 -0500 Subject: [PATCH 13/21] apply theme to new components --- .../components/app/overview/embedded/index.tsx | 8 ++++---- web/app/components/base/chat/chat/chat-input.tsx | 15 +++++++++++++++ web/app/components/base/chat/chat/index.tsx | 5 +++++ web/app/components/base/chat/chat/question.tsx | 15 ++++++++++++--- .../base/chat/embedded-chatbot/chat-wrapper.tsx | 2 ++ .../chat/embedded-chatbot/config-panel/index.tsx | 7 +++++++ .../base/chat/embedded-chatbot/context.tsx | 3 +++ .../base/chat/embedded-chatbot/header.tsx | 10 +++++++--- .../base/chat/embedded-chatbot/index.tsx | 6 ++++++ 9 files changed, 61 insertions(+), 10 deletions(-) diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx index f7bb1c00f518ae..a28d8d922f0ab7 100644 --- a/web/app/components/app/overview/embedded/index.tsx +++ b/web/app/components/app/overview/embedded/index.tsx @@ -74,8 +74,8 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam const [isCopied, setIsCopied] = useState({ iframe: false, scripts: false, chromePlugin: false }) const { langeniusVersionInfo } = useAppContext() - const themeContext = useThemeContext() - themeContext.buildTheme(siteInfo?.chat_color_theme ?? '#1C64F2', siteInfo?.chat_color_theme_inverted ?? false) + const themeBuilder = useThemeContext() + themeBuilder.buildTheme(siteInfo?.chat_color_theme ?? '#1C64F2', siteInfo?.chat_color_theme_inverted ?? false) const isTestEnv = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT' const onClickCopy = () => { if (option === 'chromePlugin') { @@ -84,7 +84,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam copy(splitUrl[1]) } else { - copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeContext.theme?.primaryColor ?? '#1C64F2', isTestEnv)) + copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv)) } setIsCopied({ ...isCopied, [option]: true }) } @@ -164,7 +164,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam
-
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeContext.theme?.primaryColor ?? '#1C64F2', isTestEnv)}
+
{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv)}
diff --git a/web/app/components/base/chat/chat/chat-input.tsx b/web/app/components/base/chat/chat/chat-input.tsx index 17e316062d0f67..e83d579b25fa52 100644 --- a/web/app/components/base/chat/chat/chat-input.tsx +++ b/web/app/components/base/chat/chat/chat-input.tsx @@ -30,16 +30,20 @@ import { useDraggableUploader, useImageFiles, } from '@/app/components/base/image-uploader/hooks' +import { ThemeBuilder } from '../embedded-chatbot/theme/theme-context' +import { CssTransform } from '../embedded-chatbot/theme/utils' type ChatInputProps = { visionConfig?: VisionConfig speechToTextConfig?: EnableType onSend?: OnSend + themeBuilder: ThemeBuilder | null } const ChatInput: FC = ({ visionConfig, speechToTextConfig, onSend, + themeBuilder, }) => { const { appData } = useChatWithHistoryContext() const { t } = useTranslation() @@ -112,14 +116,25 @@ const ChatInput: FC = ({ }) } + const [isActiveIconFocused, setActiveIconFocused] = useState(false) + const media = useBreakpoints() const isMobile = media === MediaType.mobile + const sendIconThemeStyle = themeBuilder?.theme + ? { + color: (isActiveIconFocused || query || (query.trim() !== '')) ? themeBuilder.theme.primaryColor : '#d1d5db', + } + : {} const sendBtn = (
setActiveIconFocused(true)} + onMouseLeave={() => setActiveIconFocused(false)} onClick={handleSend} + style={isActiveIconFocused ? CssTransform(themeBuilder?.theme?.chatBubbleColorStyle ?? '') : {}} > = ({ appData, @@ -85,6 +87,7 @@ const Chat: FC = ({ chatAnswerContainerInner, hideProcessDetail, hideLogModal, + themeBuilder }) => { const { t } = useTranslation() const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({ @@ -221,6 +224,7 @@ const Chat: FC = ({ key={item.id} item={item} questionIcon={questionIcon} + theme={themeBuilder?.theme} /> ) }) @@ -262,6 +266,7 @@ const Chat: FC = ({ visionConfig={config?.file_upload?.image} speechToTextConfig={config?.speech_to_text} onSend={onSend} + themeBuilder={themeBuilder} /> ) } diff --git a/web/app/components/base/chat/chat/question.tsx b/web/app/components/base/chat/chat/question.tsx index f8eaad1ce6328e..584b750d8d8b5d 100644 --- a/web/app/components/base/chat/chat/question.tsx +++ b/web/app/components/base/chat/chat/question.tsx @@ -10,14 +10,18 @@ import { QuestionTriangle } from '@/app/components/base/icons/src/vender/solid/g import { User } from '@/app/components/base/icons/src/public/avatar' import { Markdown } from '@/app/components/base/markdown' import ImageGallery from '@/app/components/base/image-gallery' +import { Theme } from '../embedded-chatbot/theme/theme-context' +import { CssTransform } from '../embedded-chatbot/theme/utils' type QuestionProps = { item: ChatItem questionIcon?: ReactNode + theme: Theme | null | undefined } const Question: FC = ({ item, questionIcon, + theme, }) => { const { content, @@ -25,12 +29,17 @@ const Question: FC = ({ } = item const imgSrcs = message_files?.length ? message_files.map(item => item.url) : [] - return (
- -
+ +
{ !!imgSrcs.length && ( diff --git a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx index 41dcbb8eb18303..6b895ae319666d 100644 --- a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx +++ b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx @@ -32,6 +32,7 @@ const ChatWrapper = () => { appMeta, handleFeedback, currentChatInstanceRef, + themeBuilder, } = useEmbeddedChatbotContext() const appConfig = useMemo(() => { const config = appParams || {} @@ -130,6 +131,7 @@ const ChatWrapper = () => { suggestedQuestions={suggestedQuestions} answerIcon={isDify() ? : null} hideProcessDetail + themeBuilder={themeBuilder} /> ) } diff --git a/web/app/components/base/chat/embedded-chatbot/config-panel/index.tsx b/web/app/components/base/chat/embedded-chatbot/config-panel/index.tsx index 59ca2bbea26035..f58b1505f2df07 100644 --- a/web/app/components/base/chat/embedded-chatbot/config-panel/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/config-panel/index.tsx @@ -9,6 +9,8 @@ import { MessageDotsCircle } from '@/app/components/base/icons/src/vender/solid/ import { Edit02 } from '@/app/components/base/icons/src/vender/line/general' import { Star06 } from '@/app/components/base/icons/src/vender/solid/shapes' import LogoSite from '@/app/components/base/logo/logo-site' +import { useThemeContext } from '../theme/theme-context' +import { CssTransform } from '../theme/utils' const ConfigPanel = () => { const { t } = useTranslation() @@ -22,6 +24,7 @@ const ConfigPanel = () => { const [collapsed, setCollapsed] = useState(true) const customConfig = appData?.custom_config const site = appData?.site + const themeBuilder = useThemeContext() return (
@@ -34,6 +37,7 @@ const ConfigPanel = () => { )} >
{ {t('share.chat.configStatusDes')}