setup(frontend): rename frontend-react

This commit is contained in:
Nex Zhu
2025-11-20 09:41:20 +08:00
parent e40cdd1dee
commit 4fa5504697
195 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,178 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import Box from '@mui/material/Box';
// import EditIcon from '@mui/icons-material/Edit';
import TextField from '@mui/material/TextField';
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
import { IRichSentence } from '@/storage/plan';
import { globalStorage } from '@/storage';
interface IAgentDetailCardProps {
id: string;
node: IRichSentence;
render: () => void;
}
export default observer(({ id, node, render }: IAgentDetailCardProps) => {
const editContentRef = React.useRef('');
const [edit, setEdit] = React.useState(false);
React.useEffect(() => {
render();
}, [edit]);
return (
<Box
component="span"
sx={{
cursor: 'pointer',
marginRight: '4px',
userSelect: 'none',
transition: 'all 200ms ease-out',
borderRadius: '0',
display: edit ? 'flex' : 'inline',
flexDirection: 'column',
border: edit ? '2px solid' : undefined,
'& .edit-button': {
display: 'none',
},
'&:hover': {
filter: edit ? undefined : 'brightness(1.1)',
backgroundColor: edit
? undefined
: ((node?.style ?? { borderColor: '#0003' }) as any)!.borderColor,
'& .edit-button': {
display: edit ? 'none' : 'inline-flex',
},
},
'&:active': {
filter: edit ? undefined : 'brightness(1)',
backgroundColor: edit
? undefined
: ((node?.style ?? { borderColor: '#0003' }) as any)!.borderColor,
},
lineHeight: '1.4rem',
padding: edit ? '6px' : undefined,
marginBottom: edit ? '6px' : undefined,
marginTop: edit ? '6px' : undefined,
borderBottom: `2px solid ${(node.style as any).borderColor}`,
backgroundImage: edit
? 'linear-gradient(0deg, #FFF7, #FFF7)'
: undefined,
...(edit ? node.whoStyle ?? {} : node.style),
}}
onDoubleClick={() => {
editContentRef.current = node.content;
setEdit(true);
}}
title="Double click to edit"
>
<Box
component="span"
sx={{
userSelect: 'none',
padding: edit ? '0 6px' : '0 2px',
borderRadius: '14px',
border: '2px solid #0005',
transition: 'filter 100ms',
fontWeight: edit ? 800 : undefined,
...node.whoStyle,
}}
>
{node.who}
</Box>
{edit ? (
<>
<TextField
variant="outlined"
multiline
inputRef={ref => {
if (ref) {
ref.value = editContentRef.current;
}
}}
sx={{
'& > .MuiInputBase-root': {
padding: '6px',
'& > fieldset': {
border: 'none',
},
background: '#FFF8',
borderRadius: '12px',
},
borderRadius: '12px',
border: '2px solid #0002',
marginTop: '4px',
'*': {
fontSize: '14px',
},
}}
onChange={event => (editContentRef.current = event.target.value)}
/>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'end',
paddingTop: '4px',
}}
>
<Box
onClick={() => {
if (!editContentRef.current) {
return;
}
setEdit(false);
const t = editContentRef.current;
globalStorage.updateAgentActionNodeContent(
id,
t[0].toUpperCase() + t.slice(1),
);
}}
title="Save change"
sx={{
cursor: 'pointer',
userSelect: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#3ec807',
borderRadius: '6px',
marginLeft: '4px',
padding: '0 4px',
'&:hover': {
filter: 'contrast(1.3)',
},
}}
>
<CheckIcon sx={{ fontSize: '18px', color: 'white' }} />
</Box>
<Box
onClick={() => setEdit(false)}
title="Cancel change"
sx={{
cursor: 'pointer',
userSelect: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#d56464',
borderRadius: '6px',
marginLeft: '4px',
padding: '0 4px',
'&:hover': {
filter: 'contrast(1.3)',
},
}}
>
<CloseIcon sx={{ fontSize: '18px', color: 'white' }} />
</Box>
</Box>
</>
) : (
// <>&nbsp;{node.content}</>
<span>&nbsp;{node.content}</span>
)}
</Box>
);
});

View File

@@ -0,0 +1,184 @@
import React from 'react';
import throttle from 'lodash/throttle';
import { observer } from 'mobx-react-lite';
import Box from '@mui/material/Box';
import AgentDetailCard from './AgentDetailCard';
import { globalStorage } from '@/storage';
import { StepTaskNode } from '@/storage/plan';
import { useResize } from '@/utils/resize-hook';
import BranchIcon from '@/icons/BranchIcon';
export default observer(({ step }: { step: StepTaskNode }) => {
const card = step.descriptionCard;
const [expand, setExpand] = React.useState(false);
const expandRef = React.useRef(expand);
React.useEffect(() => {
globalStorage.renderLines({ delay: 1, repeat: 20 });
}, [expand]);
const render = React.useMemo(
() =>
throttle(
() =>
requestAnimationFrame(() => {
if (refDetail.current) {
refDetail.current.style.height = expandRef.current
? `${
refDetail.current.querySelector('.description-detail')!
.scrollHeight + 12
}px`
: '0px';
}
}),
5,
{
leading: false,
trailing: true,
},
),
[],
);
React.useEffect(() => {
expandRef.current = expand;
render();
}, [expand]);
React.useEffect(() => {
setExpand(globalStorage.focusingStepTaskId === step.id);
}, [globalStorage.focusingStepTaskId]);
const refDetail = useResize<HTMLDivElement>(render);
return (
<Box sx={{ position: 'relative' }}>
<Box
sx={{
position: 'relative',
fontSize: '14px',
flexDirection: 'column',
background: '#F6F6F6',
borderRadius: '8px',
padding: '8px 4px',
margin: '2px 0',
cursor: 'pointer',
border: '2px solid #E5E5E5',
transition: 'all 80ms ease-in-out',
'&:hover': {
border: '2px solid #cdcdcd',
backgroundImage: 'linear-gradient(0, #00000008, #00000008)',
},
display: step.brief.template ? 'flex' : 'none',
}}
ref={card.ref}
onClick={() => globalStorage.setFocusingStepTaskId(step.id)}
>
<Box sx={{ marginLeft: '4px' }}>
{card.brief.map(({ text, style }, index) =>
style ? (
<Box
component="span"
// eslint-disable-next-line react/no-array-index-key
key={index}
sx={{
userSelect: 'none',
padding: '0 4px',
fontWeight: 600,
transition: 'filter 100ms',
...style,
}}
>
{text}
</Box>
) : (
<Box
component="span"
key={index}
sx={{ userSelect: 'none', lineHeight: '1.43rem' }}
>
{text}
</Box>
),
)}
</Box>
<Box
ref={refDetail}
sx={{
overflow: 'hidden',
transition: 'height 200ms ease-out', // 添加过渡效果
}}
>
{expand ? (
<Box
sx={{
marginTop: '5px',
borderRadius: '12px',
padding: '4px 8px',
background: '#E4F0F0',
border: '3px solid #A9C6C5',
cursor: 'auto',
}}
className="description-detail"
>
<Box
sx={{
color: '#4F8A87',
fontWeight: 800,
fontSize: '16px',
marginBottom: '4px',
}}
>
Specification:
</Box>
{card.detail.map((node, index) => (
<AgentDetailCard
key={index}
node={node[0] as any}
id={node[1] as any}
render={render}
/>
))}
</Box>
) : (
<></>
)}
</Box>
</Box>
{globalStorage.focusingStepTaskId === step.id ? (
<Box
sx={{
cursor: 'pointer',
userSelect: 'none',
position: 'absolute',
right: '-4px',
bottom: '2px',
width: '32px',
height: '32px',
bgcolor: 'primary.main',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '10px 0 0 0',
'&:hover': {
filter: 'brightness(0.9)',
},
}}
onClick={() => (globalStorage.taskProcessModificationWindow = true)}
>
<BranchIcon />
<Box
component="span"
sx={{
fontSize: '12px',
position: 'absolute',
right: '3px',
bottom: 0,
color: 'white',
fontWeight: 800,
textAlign: 'right',
}}
>
{step.agentSelection?.leaves?.length ?? 0}
</Box>
</Box>
) : (
<></>
)}
</Box>
);
});

View File

@@ -0,0 +1,57 @@
import { observer } from 'mobx-react-lite';
import { SxProps } from '@mui/material';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import DescriptionCard from './DescriptionCard';
import { globalStorage } from '@/storage';
import LoadingMask from '@/components/LoadingMask';
import Title from '@/components/Title';
export default observer(({ style = {} }: { style?: SxProps }) => {
return (
<Box
sx={{
background: '#FFF',
border: '3px solid #E1E1E1',
display: 'flex',
overflow: 'hidden',
flexDirection: 'column',
...style,
}}
>
<Title title="Task Process" />
<Stack
spacing={1}
sx={{
position: 'relative',
padding: '6px 12px',
paddingBottom: '44px',
borderRadius: '10px',
height: 0,
flexGrow: 1,
overflowY: 'auto',
}}
onScroll={() => {
globalStorage.renderLines({ delay: 0, repeat: 2 });
}}
>
{globalStorage.planManager.currentPlan.map(step => (
<DescriptionCard key={step.name} step={step} />
))}
{globalStorage.api.planGenerating ? (
<LoadingMask
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
) : (
<></>
)}
</Stack>
</Box>
);
});