Theme switch support (#3568)

### What problem does this PR solve?
- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: Yingfeng <yingfeng.zhang@gmail.com>
Co-authored-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
so95
2024-12-10 10:42:04 +07:00
committed by GitHub
parent 7d4f1c0645
commit d5a322a352
85 changed files with 1041 additions and 520 deletions

View File

@ -1,5 +1,5 @@
.contextMenu {
background: white;
background: rgba(255, 255, 255, 0.1);
border-style: solid;
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
position: absolute;
@ -13,6 +13,6 @@
}
button:hover {
background: white;
background: rgba(255, 255, 255, 0.1);
}
}

View File

@ -13,3 +13,19 @@
.edgeButton:hover {
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08);
}
.edgeButtonDark {
width: 14px;
height: 14px;
background: #0e0c0c;
border: 1px solid #fff;
padding: 0;
cursor: pointer;
border-radius: 50%;
font-size: 10px;
line-height: 1;
}
.edgeButtonDark:hover {
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08);
}

View File

@ -6,6 +6,7 @@ import {
} from 'reactflow';
import useGraphStore from '../../store';
import { useTheme } from '@/components/theme-provider';
import { useFetchFlow } from '@/hooks/flow-hooks';
import { useMemo } from 'react';
import styles from './index.less';
@ -33,7 +34,7 @@ export function ButtonEdge({
targetY,
targetPosition,
});
const { theme } = useTheme();
const selectedStyle = useMemo(() => {
return selected ? { strokeWidth: 2, stroke: '#1677ff' } : {};
}, [selected]);
@ -93,7 +94,9 @@ export function ButtonEdge({
className="nodrag nopan"
>
<button
className={styles.edgeButton}
className={
theme === 'dark' ? styles.edgeButtonDark : styles.edgeButton
}
type="button"
onClick={onEdgeClick}
>

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import get from 'lodash/get';
@ -18,12 +19,16 @@ import styles from './index.less';
export function BeginNode({ selected, data }: NodeProps<NodeData>) {
const { t } = useTranslation();
const query: BeginQuery[] = get(data, 'form.query', []);
const { theme } = useTheme();
return (
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.ragNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="source"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -11,12 +12,16 @@ import NodeHeader from './node-header';
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
const { positions } = useBuildCategorizeHandlePositions({ data, id });
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="target"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -17,12 +18,16 @@ export function GenerateNode({
}: NodeProps<NodeData>) {
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
const getLabel = useGetComponentLabelByValue(id);
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -10,9 +10,11 @@
width: 200px;
}
.dark {
background: rgb(63, 63, 63) !important;
}
.ragNode {
.commonNode();
.nodeName {
font-size: 10px;
color: black;
@ -95,7 +97,7 @@
min-width: 140px;
width: auto;
height: 100%;
padding: 0;
padding: 8px;
border-radius: 10px;
min-height: 128px;
.noteTitle {
@ -105,6 +107,13 @@
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.noteTitleDark {
background-color: #edfcff;
font-size: 12px;
padding: 6px 6px 4px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.noteForm {
margin-top: 4px;
height: calc(100% - 50px);

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
@ -11,11 +12,16 @@ export function RagNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.ragNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -15,12 +16,17 @@ export function InvokeNode({
selected,
}: NodeProps<NodeData>) {
const { t } = useTranslation();
const { theme } = useTheme();
const url = get(data, 'form.url');
return (
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.ragNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
@ -13,11 +14,16 @@ export function KeywordNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
@ -11,11 +12,16 @@ export function LogicNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -14,12 +15,16 @@ export function MessageNode({
selected,
}: NodeProps<NodeData>) {
const messages: string[] = get(data, 'form.messages', []);
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -5,6 +5,7 @@ import { NodeData } from '../../interface';
import NodeDropdown from './dropdown';
import SvgIcon from '@/components/svg-icon';
import { useTheme } from '@/components/theme-provider';
import { memo, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
@ -23,6 +24,7 @@ const controlStyle = {
function NoteNode({ data, id }: NodeProps<NodeData>) {
const { t } = useTranslation();
const [form] = Form.useForm();
const { theme } = useTheme();
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
id,
@ -48,10 +50,15 @@ function NoteNode({ data, id }: NodeProps<NodeData>) {
}}
></SvgIcon>
</NodeResizeControl>
<section className={styles.noteNode}>
<section
className={classNames(
styles.noteNode,
theme === 'dark' ? styles.dark : '',
)}
>
<Flex
justify={'space-between'}
className={classNames(styles.noteTitle, 'note-drag-handle')}
className={classNames('note-drag-handle')}
align="center"
gap={6}
>

View File

@ -5,6 +5,7 @@ import JsonView from 'react18-json-view';
import 'react18-json-view/src/style.css';
import { useGetComponentLabelByValue, useReplaceIdWithText } from '../../hooks';
import { useTheme } from '@/components/theme-provider';
import {
Popover,
PopoverContent,
@ -29,6 +30,7 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
const { t } = useTranslate('flow');
const { data } = useFetchFlow();
const { theme } = useTheme();
const component = useMemo(() => {
return get(data, ['dsl', 'components', nodeId], {});
}, [nodeId, data]);
@ -64,7 +66,16 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
<div className="flex w-full gap-4 flex-col">
<div className="flex flex-col space-y-1.5">
<span className="font-semibold text-[14px]">{t('input')}</span>
<div className="bg-gray-100 p-1 rounded">
<div
style={
theme === 'dark'
? {
backgroundColor: 'rgba(150, 150, 150, 0.2)',
}
: {}
}
className={`bg-gray-100 p-1 rounded`}
>
<Table>
<TableHeader>
<TableRow>
@ -85,7 +96,16 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
</div>
<div className="flex flex-col space-y-1.5">
<span className="font-semibold text-[14px]">{t('output')}</span>
<div className="bg-gray-100 p-1 rounded">
<div
style={
theme === 'dark'
? {
backgroundColor: 'rgba(150, 150, 150, 0.2)',
}
: {}
}
className="bg-gray-100 p-1 rounded"
>
<JsonView
src={replacedOutput}
displaySize={30}

View File

@ -4,6 +4,7 @@ import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
import { RightHandleStyle } from './handle-icon';
import { useTheme } from '@/components/theme-provider';
import { get } from 'lodash';
import { useReplaceIdWithName } from '../../hooks';
import styles from './index.less';
@ -13,12 +14,16 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
const yes = get(data, 'form.yes');
const no = get(data, 'form.no');
const replaceIdWithName = useReplaceIdWithName();
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="target"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { UserOutlined } from '@ant-design/icons';
import { Avatar, Flex } from 'antd';
@ -17,6 +18,7 @@ export function RetrievalNode({
selected,
}: NodeProps<NodeData>) {
const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []);
const { theme } = useTheme();
const { list: knowledgeList } = useFetchKnowledgeList(true);
const knowledgeBases = useMemo(() => {
return knowledgeBaseIds.map((x) => {
@ -31,9 +33,13 @@ export function RetrievalNode({
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
@ -13,11 +14,16 @@ export function RewriteNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Divider, Flex } from 'antd';
import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
@ -55,12 +56,16 @@ const ConditionBlock = ({
export function SwitchNode({ id, data, selected }: NodeProps<NodeData>) {
const { positions } = useBuildSwitchHandlePositions({ data, id });
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="target"

View File

@ -7,6 +7,7 @@ import { IGenerateParameter, NodeData } from '../../interface';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import NodeHeader from './node-header';
import { useTheme } from '@/components/theme-provider';
import styles from './index.less';
export function TemplateNode({
@ -17,12 +18,17 @@ export function TemplateNode({
}: NodeProps<NodeData>) {
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
const getLabel = useGetComponentLabelByValue(id);
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,9 @@
import {
GitHubIcon,
KeywordIcon,
QWeatherIcon,
WikipediaIcon,
} from '@/assets/icon/Icon';
import { ReactComponent as AkShareIcon } from '@/assets/svg/akshare.svg';
import { ReactComponent as ArXivIcon } from '@/assets/svg/arxiv.svg';
import { ReactComponent as baiduFanyiIcon } from '@/assets/svg/baidu-fanyi.svg';
@ -10,20 +16,16 @@ import { ReactComponent as DeepLIcon } from '@/assets/svg/deepl.svg';
import { ReactComponent as DuckIcon } from '@/assets/svg/duck.svg';
import { ReactComponent as EmailIcon } from '@/assets/svg/email.svg';
import { ReactComponent as ExeSqlIcon } from '@/assets/svg/exesql.svg';
import { ReactComponent as GithubIcon } from '@/assets/svg/github.svg';
import { ReactComponent as GoogleScholarIcon } from '@/assets/svg/google-scholar.svg';
import { ReactComponent as GoogleIcon } from '@/assets/svg/google.svg';
import { ReactComponent as InvokeIcon } from '@/assets/svg/invoke-ai.svg';
import { ReactComponent as Jin10Icon } from '@/assets/svg/jin10.svg';
import { ReactComponent as KeywordIcon } from '@/assets/svg/keyword.svg';
import { ReactComponent as NoteIcon } from '@/assets/svg/note.svg';
import { ReactComponent as PubMedIcon } from '@/assets/svg/pubmed.svg';
import { ReactComponent as QWeatherIcon } from '@/assets/svg/qweather.svg';
import { ReactComponent as SwitchIcon } from '@/assets/svg/switch.svg';
import { ReactComponent as TemplateIcon } from '@/assets/svg/template.svg';
import { ReactComponent as TuShareIcon } from '@/assets/svg/tushare.svg';
import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg';
import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg';
import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg';
// 邮件功能
@ -116,7 +118,7 @@ export const operatorIconMap = {
[Operator.Bing]: BingIcon,
[Operator.GoogleScholar]: GoogleScholarIcon,
[Operator.DeepL]: DeepLIcon,
[Operator.GitHub]: GithubIcon,
[Operator.GitHub]: GitHubIcon,
[Operator.BaiduFanyi]: baiduFanyiIcon,
[Operator.QWeather]: QWeatherIcon,
[Operator.ExeSQL]: ExeSqlIcon,
@ -193,11 +195,10 @@ export const operatorMap: Record<
[Operator.KeywordExtract]: {
width: 70,
height: 70,
backgroundColor: '#0f0e0f',
color: '#0f0e0f',
backgroundColor: '#6E5494',
color: '#6E5494',
fontSize: 12,
iconWidth: 16,
// iconFontSize: 16,
},
[Operator.DuckDuckGo]: {
backgroundColor: '#e7e389',
@ -235,10 +236,14 @@ export const operatorMap: Record<
backgroundColor: '#f5e8e6',
},
[Operator.GitHub]: {
backgroundColor: '#c7c7f8',
backgroundColor: 'purple',
color: 'purple',
},
[Operator.BaiduFanyi]: { backgroundColor: '#e5f2d3' },
[Operator.QWeather]: { backgroundColor: '#a4bbf3' },
[Operator.QWeather]: {
backgroundColor: '#a4bbf3',
color: '#a4bbf3',
},
[Operator.ExeSQL]: { backgroundColor: '#b9efe8' },
[Operator.Switch]: { backgroundColor: '#dbaff6', color: '#dbaff6' },
[Operator.WenCai]: { backgroundColor: '#faac5b' },

View File

@ -1,7 +1,7 @@
.dynamicInputVariable {
background-color: #ebe9e9;
background-color: #ebe9e950;
:global(.ant-collapse-content) {
background-color: #f6f6f6;
background-color: #f6f6f657;
}
:global(.ant-collapse-content-box) {
padding: 0 !important;

View File

@ -1,7 +1,7 @@
.dynamicInputVariable {
background-color: #ebe9e9;
background-color: #ebe9e950;
:global(.ant-collapse-content) {
background-color: #f6f6f6;
background-color: #f6f6f657;
}
margin-bottom: 20px;
.title {

View File

@ -18,9 +18,9 @@
}
.dynamicParameterVariable {
background-color: #ebe9e9;
background-color: #ebe9e950;
:global(.ant-collapse-content) {
background-color: #f6f6f6;
background-color: #f6f6f634;
}
:global(.ant-collapse-content-box) {
padding: 0 !important;

View File

@ -27,7 +27,8 @@
.card {
border-radius: 12px;
border: 1px solid rgba(234, 236, 240, 1);
border: 1px solid rgba(0, 0, 0, 0.3);
background-color: rgba(255, 255, 255, 0.1);
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
padding: 24px;
width: 300px;
@ -39,14 +40,12 @@
font-size: 24px;
line-height: 32px;
font-weight: 600;
color: rgba(0, 0, 0, 0.88);
word-break: break-all;
}
.description {
font-size: 12px;
font-weight: 600;
line-height: 20px;
color: rgba(0, 0, 0, 0.45);
}
}
@ -72,7 +71,6 @@
.rightText {
font-size: 12px;
font-weight: 600;
color: rgba(0, 0, 0, 0.65);
vertical-align: middle;
}
}