mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 15:00:57 +03:00
Feature/seq agents (#2798)
* update build functions * sequential agents * update langchain to 0.2, added sequential agent nodes * add marketplace templates * update howto wordings * Merge branch 'main' into feature/Seq-Agents # Conflicts: # pnpm-lock.yaml * update deprecated functions and add new sequential nodes * add marketplace templates * update marketplace templates, add structured output to llm node * add multi agents template * update llm node with bindmodels * update cypress version * update templates sticky note wordings * update tool node to include human in loop action * update structured outputs error from models * update cohere package to resolve google genai pipeThrough bug * update mistral package version, added message reconstruction before invoke seq agent * add HITL to agent * update state messages restructuring * update load and split methods for s3 directory
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
// MUI
|
||||
import { Button, Dialog, DialogActions, DialogContent } from '@mui/material'
|
||||
import { Tabs } from '@mui/base/Tabs'
|
||||
|
||||
// Project Import
|
||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
||||
import { TabPanel } from '@/ui-component/tabs/TabPanel'
|
||||
import { TabsList } from '@/ui-component/tabs/TabsList'
|
||||
import { Tab } from '@/ui-component/tabs/Tab'
|
||||
import NodeInputHandler from '@/views/canvas/NodeInputHandler'
|
||||
|
||||
// Store
|
||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
|
||||
|
||||
const ConditionDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [inputParam, setInputParam] = useState(null)
|
||||
const [tabValue, setTabValue] = useState(0)
|
||||
const [data, setData] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.inputParam) {
|
||||
setInputParam(dialogProps.inputParam)
|
||||
}
|
||||
|
||||
if (dialogProps.data) setData(dialogProps.data)
|
||||
|
||||
return () => {
|
||||
setInputParam(null)
|
||||
setData({})
|
||||
}
|
||||
}, [dialogProps])
|
||||
|
||||
useEffect(() => {
|
||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
}, [show, dispatch])
|
||||
|
||||
const component = show ? (
|
||||
<Dialog open={show} fullWidth maxWidth='md' aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description'>
|
||||
<DialogContent>
|
||||
<>
|
||||
{inputParam && inputParam.type.includes('conditionFunction') && (
|
||||
<>
|
||||
<Tabs value={tabValue} onChange={(event, val) => setTabValue(val)} aria-label='tabs' variant='fullWidth'>
|
||||
<TabsList>
|
||||
{inputParam.tabs.map((inputChildParam, index) => (
|
||||
<Tab key={index}>{inputChildParam.label}</Tab>
|
||||
))}
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
{inputParam.tabs.map((inputChildParam, index) => (
|
||||
<TabPanel key={index} value={tabValue} index={index}>
|
||||
<NodeInputHandler
|
||||
disabled={inputChildParam.disabled}
|
||||
inputParam={inputChildParam}
|
||||
data={data}
|
||||
isAdditionalParams={true}
|
||||
disablePadding={true}
|
||||
/>
|
||||
</TabPanel>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>
|
||||
<StyledButton disabled={dialogProps.disabled} variant='contained' onClick={() => onConfirm(data, inputParam, tabValue)}>
|
||||
{dialogProps.confirmButtonName}
|
||||
</StyledButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
ConditionDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onConfirm: PropTypes.func
|
||||
}
|
||||
|
||||
export default ConditionDialog
|
||||
@@ -22,7 +22,7 @@ import useApi from '@/hooks/useApi'
|
||||
|
||||
import './ExpandTextDialog.css'
|
||||
|
||||
const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
const ExpandTextDialog = ({ show, dialogProps, onCancel, onInputHintDialogClicked, onConfirm }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const theme = useTheme()
|
||||
@@ -38,13 +38,18 @@ const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
const executeCustomFunctionNodeApi = useApi(nodesApi.executeCustomFunctionNode)
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.value) setInputValue(dialogProps.value)
|
||||
if (dialogProps.value) {
|
||||
setInputValue(dialogProps.value)
|
||||
}
|
||||
if (dialogProps.inputParam) {
|
||||
setInputParam(dialogProps.inputParam)
|
||||
if (dialogProps.inputParam.type === 'code') {
|
||||
setLanguageType('js')
|
||||
}
|
||||
}
|
||||
if (dialogProps.languageType) {
|
||||
setLanguageType(dialogProps.languageType)
|
||||
}
|
||||
|
||||
return () => {
|
||||
setInputValue('')
|
||||
@@ -91,9 +96,22 @@ const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
{inputParam && (inputParam.type === 'string' || inputParam.type === 'code') && (
|
||||
<div style={{ flex: 70 }}>
|
||||
<Typography sx={{ mb: 2, ml: 1 }} variant='h4'>
|
||||
{inputParam.label}
|
||||
</Typography>
|
||||
<div style={{ marginBottom: '10px', display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography variant='h4'>{inputParam.label}</Typography>
|
||||
<div style={{ flex: 1 }} />
|
||||
{inputParam.hint && (
|
||||
<Button
|
||||
sx={{ p: 0, px: 2 }}
|
||||
color='secondary'
|
||||
variant='text'
|
||||
onClick={() => {
|
||||
onInputHintDialogClicked(inputParam.hint)
|
||||
}}
|
||||
>
|
||||
{inputParam.hint.label}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<PerfectScrollbar
|
||||
style={{
|
||||
border: '1px solid',
|
||||
@@ -114,7 +132,12 @@ const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
placeholder={inputParam.placeholder}
|
||||
basicSetup={
|
||||
languageType !== 'js'
|
||||
? { lineNumbers: false, foldGutter: false, autocompletion: false, highlightActiveLine: false }
|
||||
? {
|
||||
lineNumbers: false,
|
||||
foldGutter: false,
|
||||
autocompletion: false,
|
||||
highlightActiveLine: false
|
||||
}
|
||||
: {}
|
||||
}
|
||||
onValueChange={(code) => setInputValue(code)}
|
||||
@@ -123,7 +146,7 @@ const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{languageType === 'js' && (
|
||||
{languageType === 'js' && !inputParam.hideCodeExecute && (
|
||||
<LoadingButton
|
||||
sx={{
|
||||
mt: 2,
|
||||
@@ -177,7 +200,8 @@ ExpandTextDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onConfirm: PropTypes.func
|
||||
onConfirm: PropTypes.func,
|
||||
onInputHintDialogClicked: PropTypes.func
|
||||
}
|
||||
|
||||
export default ExpandTextDialog
|
||||
|
||||
@@ -46,6 +46,7 @@ const FormatPromptValuesDialog = ({ show, dialogProps, onChange, onCancel }) =>
|
||||
nodes={dialogProps.nodes}
|
||||
edges={dialogProps.edges}
|
||||
nodeId={dialogProps.nodeId}
|
||||
isSequentialAgent={dialogProps.data.category === 'Sequential Agents'}
|
||||
/>
|
||||
</PerfectScrollbar>
|
||||
</DialogContent>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import PropTypes from 'prop-types'
|
||||
import rehypeMathjax from 'rehype-mathjax'
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import remarkMath from 'remark-math'
|
||||
import { MemoizedReactMarkdown } from '@/ui-component/markdown/MemoizedReactMarkdown'
|
||||
import { CodeBlock } from '@/ui-component/markdown/CodeBlock'
|
||||
import { Dialog, DialogContent, DialogTitle } from '@mui/material'
|
||||
|
||||
const InputHintDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
onClose={onCancel}
|
||||
open={show}
|
||||
fullWidth
|
||||
maxWidth='sm'
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
||||
{dialogProps.label}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<MemoizedReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeMathjax, rehypeRaw]}
|
||||
components={{
|
||||
code({ inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
return !inline ? (
|
||||
<CodeBlock
|
||||
isDialog={true}
|
||||
language={(match && match[1]) || ''}
|
||||
value={String(children).replace(/\n$/, '')}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<code className={className} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{dialogProps?.value}
|
||||
</MemoizedReactMarkdown>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
InputHintDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func
|
||||
}
|
||||
|
||||
export default InputHintDialog
|
||||
@@ -34,7 +34,7 @@ import userPNG from '@/assets/images/account.png'
|
||||
import msgEmptySVG from '@/assets/images/message_empty.svg'
|
||||
import multiagent_supervisorPNG from '@/assets/images/multiagent_supervisor.png'
|
||||
import multiagent_workerPNG from '@/assets/images/multiagent_worker.png'
|
||||
import { IconFileExport, IconEraser, IconX, IconDownload } from '@tabler/icons-react'
|
||||
import { IconTool, IconDeviceSdCard, IconFileExport, IconEraser, IconX, IconDownload } from '@tabler/icons-react'
|
||||
|
||||
// Project import
|
||||
import { MemoizedReactMarkdown } from '@/ui-component/markdown/MemoizedReactMarkdown'
|
||||
@@ -846,6 +846,64 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
</Box>
|
||||
<div>{agent.agentName}</div>
|
||||
</Stack>
|
||||
{agent.usedTools && agent.usedTools.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'block',
|
||||
flexDirection: 'row',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
{agent.usedTools.map((tool, index) => {
|
||||
return tool !== null ? (
|
||||
<Chip
|
||||
size='small'
|
||||
key={index}
|
||||
label={tool.tool}
|
||||
component='a'
|
||||
sx={{ mr: 1, mt: 1 }}
|
||||
variant='outlined'
|
||||
clickable
|
||||
icon={<IconTool size={15} />}
|
||||
onClick={() =>
|
||||
onSourceDialogClick(
|
||||
tool,
|
||||
'Used Tools'
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : null
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{agent.state &&
|
||||
Object.keys(agent.state).length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'block',
|
||||
flexDirection: 'row',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
<Chip
|
||||
size='small'
|
||||
label={'State'}
|
||||
component='a'
|
||||
sx={{ mr: 1, mt: 1 }}
|
||||
variant='outlined'
|
||||
clickable
|
||||
icon={
|
||||
<IconDeviceSdCard size={15} />
|
||||
}
|
||||
onClick={() =>
|
||||
onSourceDialogClick(
|
||||
agent.state,
|
||||
'State'
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{agent.messages.length > 0 && (
|
||||
<MemoizedReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
@@ -893,6 +951,67 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
{agent.instructions && <p>{agent.instructions}</p>}
|
||||
{agent.messages.length === 0 &&
|
||||
!agent.instructions && <p>Finished</p>}
|
||||
{agent.sourceDocuments &&
|
||||
agent.sourceDocuments.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'block',
|
||||
flexDirection: 'row',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
{removeDuplicateURL(agent).map(
|
||||
(source, index) => {
|
||||
const URL =
|
||||
source &&
|
||||
source.metadata &&
|
||||
source.metadata.source
|
||||
? isValidURL(
|
||||
source.metadata
|
||||
.source
|
||||
)
|
||||
: undefined
|
||||
return (
|
||||
<Chip
|
||||
size='small'
|
||||
key={index}
|
||||
label={
|
||||
URL
|
||||
? URL.pathname.substring(
|
||||
0,
|
||||
15
|
||||
) === '/'
|
||||
? URL.host
|
||||
: `${URL.pathname.substring(
|
||||
0,
|
||||
15
|
||||
)}...`
|
||||
: `${source.pageContent.substring(
|
||||
0,
|
||||
15
|
||||
)}...`
|
||||
}
|
||||
component='a'
|
||||
sx={{ mr: 1, mb: 1 }}
|
||||
variant='outlined'
|
||||
clickable
|
||||
onClick={() =>
|
||||
URL
|
||||
? onURLClick(
|
||||
source
|
||||
.metadata
|
||||
.source
|
||||
)
|
||||
: onSourceDialogClick(
|
||||
source
|
||||
)
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -6,6 +6,32 @@ import { Button } from '@mui/material'
|
||||
import DeleteIcon from '@mui/icons-material/Delete'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { formatDataGridRows } from '@/utils/genericHelper'
|
||||
import { styled } from '@mui/material/styles'
|
||||
|
||||
const StyledDataGrid = styled(MUIDataGrid)(({ theme }) => ({
|
||||
border: `1px solid ${theme.palette.mode === 'light' ? '#b4b4b4' : '#303030'}`,
|
||||
|
||||
letterSpacing: 'normal',
|
||||
'& .MuiDataGrid-columnsContainer': {
|
||||
backgroundColor: theme.palette.mode === 'light' ? '#fafafa' : '#1d1d1d'
|
||||
},
|
||||
'& .MuiDataGrid-iconSeparator': {
|
||||
display: 'none'
|
||||
},
|
||||
'& .MuiDataGrid-columnHeader, .MuiDataGrid-cell': {
|
||||
borderRight: `1px solid ${theme.palette.mode === 'light' ? '#f0f0f0' : '#303030'}`
|
||||
},
|
||||
'& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell': {
|
||||
borderBottom: `1px solid ${theme.palette.mode === 'light' ? '#f0f0f0' : '#303030'}`
|
||||
},
|
||||
|
||||
'& .MuiPaginationItem-root': {
|
||||
borderRadius: 0
|
||||
},
|
||||
'& .MuiDataGrid-columnHeader:last-child, .MuiDataGrid-cell:last-child': {
|
||||
borderRight: 'none'
|
||||
}
|
||||
}))
|
||||
|
||||
export const DataGrid = ({ columns, rows, style, disabled = false, hideFooter = false, onChange }) => {
|
||||
const [rowValues, setRowValues] = useState(formatDataGridRows(rows) ?? [])
|
||||
@@ -80,7 +106,7 @@ export const DataGrid = ({ columns, rows, style, disabled = false, hideFooter =
|
||||
<>
|
||||
{rowValues && colValues && (
|
||||
<div style={{ marginTop: 10, height: 210, width: '100%', ...style }}>
|
||||
<MUIDataGrid
|
||||
<StyledDataGrid
|
||||
processRowUpdate={handleProcessRowUpdate}
|
||||
isCellEditable={() => {
|
||||
return !disabled
|
||||
|
||||
@@ -6,7 +6,17 @@ import SelectVariable from './SelectVariable'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { getAvailableNodesForVariable } from '@/utils/genericHelper'
|
||||
|
||||
export const JsonEditorInput = ({ value, onChange, inputParam, nodes, edges, nodeId, disabled = false, isDarkMode = false }) => {
|
||||
export const JsonEditorInput = ({
|
||||
value,
|
||||
onChange,
|
||||
inputParam,
|
||||
nodes,
|
||||
edges,
|
||||
nodeId,
|
||||
disabled = false,
|
||||
isDarkMode = false,
|
||||
isSequentialAgent = false
|
||||
}) => {
|
||||
const [myValue, setMyValue] = useState(value ? JSON.parse(value) : {})
|
||||
const [availableNodesForVariable, setAvailableNodesForVariable] = useState([])
|
||||
const [mouseUpKey, setMouseUpKey] = useState('')
|
||||
@@ -110,6 +120,7 @@ export const JsonEditorInput = ({ value, onChange, inputParam, nodes, edges, nod
|
||||
setNewVal(val)
|
||||
handleClosePopOver()
|
||||
}}
|
||||
isSequentialAgent={isSequentialAgent}
|
||||
/>
|
||||
</Popover>
|
||||
)}
|
||||
@@ -125,5 +136,6 @@ JsonEditorInput.propTypes = {
|
||||
inputParam: PropTypes.object,
|
||||
nodes: PropTypes.array,
|
||||
edges: PropTypes.array,
|
||||
nodeId: PropTypes.string
|
||||
nodeId: PropTypes.string,
|
||||
isSequentialAgent: PropTypes.bool
|
||||
}
|
||||
|
||||
@@ -4,9 +4,29 @@ import { Box, List, ListItemButton, ListItem, ListItemAvatar, ListItemText, Typo
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
import robotPNG from '@/assets/images/robot.png'
|
||||
import chatPNG from '@/assets/images/chathistory.png'
|
||||
import diskPNG from '@/assets/images/floppy-disc.png'
|
||||
import { baseURL } from '@/store/constant'
|
||||
|
||||
const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectAndReturnVal }) => {
|
||||
const sequentialStateMessagesSelection = [
|
||||
{
|
||||
primary: '$flow.state.messages',
|
||||
secondary: `All messages from the start of the conversation till now`
|
||||
},
|
||||
{
|
||||
primary: '$flow.state.<replace-with-key>',
|
||||
secondary: `Current value of the state variable with specified key`
|
||||
},
|
||||
{
|
||||
primary: '$flow.state.messages[0].content',
|
||||
secondary: `First message content`
|
||||
},
|
||||
{
|
||||
primary: '$flow.state.messages[-1].content',
|
||||
secondary: `Last message content`
|
||||
}
|
||||
]
|
||||
|
||||
const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectAndReturnVal, isSequentialAgent }) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
const onSelectOutputResponseClick = (node, prefix) => {
|
||||
@@ -102,9 +122,10 @@ const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectA
|
||||
{availableNodesForVariable &&
|
||||
availableNodesForVariable.length > 0 &&
|
||||
availableNodesForVariable.map((node, index) => {
|
||||
const selectedOutputAnchor = node.data.outputAnchors[0].options.find(
|
||||
(ancr) => ancr.name === node.data.outputs['output']
|
||||
)
|
||||
const selectedOutputAnchor =
|
||||
node.data.outputAnchors.length &&
|
||||
node.data.outputAnchors[0].options &&
|
||||
node.data.outputAnchors[0].options.find((ancr) => ancr.name === node.data.outputs['output'])
|
||||
return (
|
||||
<ListItemButton
|
||||
key={index}
|
||||
@@ -157,6 +178,45 @@ const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectA
|
||||
</ListItemButton>
|
||||
)
|
||||
})}
|
||||
{isSequentialAgent &&
|
||||
(sequentialStateMessagesSelection || []).map((item, index) => (
|
||||
<ListItemButton
|
||||
key={index}
|
||||
sx={{
|
||||
p: 0,
|
||||
borderRadius: `${customization.borderRadius}px`,
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
mb: 1
|
||||
}}
|
||||
disabled={disabled}
|
||||
onClick={() => onSelectAndReturnVal(item.primary)}
|
||||
>
|
||||
<ListItem alignItems='center'>
|
||||
<ListItemAvatar>
|
||||
<div
|
||||
style={{
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 10,
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt='state'
|
||||
src={diskPNG}
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText sx={{ ml: 1 }} primary={item.primary} secondary={item.secondary} />
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
</PerfectScrollbar>
|
||||
@@ -169,7 +229,8 @@ const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectA
|
||||
SelectVariable.propTypes = {
|
||||
availableNodesForVariable: PropTypes.array,
|
||||
disabled: PropTypes.bool,
|
||||
onSelectAndReturnVal: PropTypes.func
|
||||
onSelectAndReturnVal: PropTypes.func,
|
||||
isSequentialAgent: PropTypes.bool
|
||||
}
|
||||
|
||||
export default SelectVariable
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
.react-markdown table {
|
||||
border-spacing: 0 !important;
|
||||
border-collapse: collapse !important;
|
||||
border-color: inherit !important;
|
||||
display: block !important;
|
||||
width: max-content !important;
|
||||
max-width: 100% !important;
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.react-markdown tbody,
|
||||
.react-markdown td,
|
||||
.react-markdown tfoot,
|
||||
.react-markdown th,
|
||||
.react-markdown thead,
|
||||
.react-markdown tr {
|
||||
border-color: inherit !important;
|
||||
border-style: solid !important;
|
||||
border-width: 1px !important;
|
||||
padding: 10px !important;
|
||||
}
|
||||
@@ -1,4 +1,19 @@
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import './Markdown.css'
|
||||
|
||||
export const MemoizedReactMarkdown = memo(ReactMarkdown, (prevProps, nextProps) => prevProps.children === nextProps.children)
|
||||
export const MemoizedReactMarkdown = memo(
|
||||
({ children, ...props }) => (
|
||||
<div className='react-markdown'>
|
||||
<ReactMarkdown {...props}>{children}</ReactMarkdown>
|
||||
</div>
|
||||
),
|
||||
(prevProps, nextProps) => prevProps.children === nextProps.children
|
||||
)
|
||||
|
||||
MemoizedReactMarkdown.displayName = 'MemoizedReactMarkdown'
|
||||
|
||||
MemoizedReactMarkdown.propTypes = {
|
||||
children: PropTypes.any
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { styled } from '@mui/system'
|
||||
import { buttonClasses } from '@mui/base/Button'
|
||||
import { Tab as BaseTab, tabClasses } from '@mui/base/Tab'
|
||||
import { blue } from './tabColors'
|
||||
|
||||
export const Tab = styled(BaseTab)(
|
||||
({ ...props }) => `
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
line-height: 1.5;
|
||||
padding: 8px 12px;
|
||||
margin: 6px;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
background-color: ${props.sx?.backgroundColor || blue[400]};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: #fff;
|
||||
outline: 3px solid ${props.sx?.backgroundColor || blue[200]};
|
||||
}
|
||||
|
||||
&.${tabClasses.selected} {
|
||||
background-color: #fff;
|
||||
color: ${blue[600]};
|
||||
}
|
||||
|
||||
&.${buttonClasses.disabled} {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Box } from '@mui/material'
|
||||
|
||||
export const TabPanel = (props) => {
|
||||
const { children, value, index, ...other } = props
|
||||
return (
|
||||
<div role='tabpanel' hidden={value !== index} id={`tabpanel-${index}`} aria-labelledby={`tab-${index}`} {...other}>
|
||||
{value === index && <Box sx={{ p: 1 }}>{children}</Box>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
TabPanel.propTypes = {
|
||||
children: PropTypes.node,
|
||||
index: PropTypes.number.isRequired,
|
||||
value: PropTypes.number.isRequired
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { styled } from '@mui/system'
|
||||
import { TabsList as BaseTabsList } from '@mui/base/TabsList'
|
||||
import { blue } from './tabColors'
|
||||
|
||||
export const TabsList = styled(BaseTabsList)(
|
||||
({ theme, ...props }) => `
|
||||
min-width: 400px;
|
||||
background-color: ${props.sx?.backgroundColor || blue[500]};
|
||||
border-radius: 20px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-content: space-between;
|
||||
box-shadow: 0px 4px 6px ${theme.palette.mode === 'dark' ? 'rgba(0,0,0, 0.4)' : 'rgba(0,0,0, 0.2)'};
|
||||
`
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
export const blue = {
|
||||
50: '#F0F7FF',
|
||||
100: '#C2E0FF',
|
||||
200: '#80BFFF',
|
||||
300: '#66B2FF',
|
||||
400: '#3399FF',
|
||||
500: '#007FFF',
|
||||
600: '#0072E5',
|
||||
700: '#0059B2',
|
||||
800: '#004C99',
|
||||
900: '#003A75'
|
||||
}
|
||||
Reference in New Issue
Block a user