mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-26 03:01:12 +03:00
format prompt values revamp
This commit is contained in:
@@ -1,256 +0,0 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Box,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Typography,
|
||||
Stack
|
||||
} from '@mui/material'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import { DarkCodeEditor } from 'ui-component/editor/DarkCodeEditor'
|
||||
import { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'
|
||||
|
||||
import './EditPromptValuesDialog.css'
|
||||
import { baseURL } from 'store/constant'
|
||||
|
||||
const EditPromptValuesDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const languageType = 'json'
|
||||
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [inputParam, setInputParam] = useState(null)
|
||||
const [textCursorPosition, setTextCursorPosition] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.value) setInputValue(dialogProps.value)
|
||||
if (dialogProps.inputParam) setInputParam(dialogProps.inputParam)
|
||||
|
||||
return () => {
|
||||
setInputValue('')
|
||||
setInputParam(null)
|
||||
setTextCursorPosition({})
|
||||
}
|
||||
}, [dialogProps])
|
||||
|
||||
const onMouseUp = (e) => {
|
||||
if (e.target && e.target.selectionEnd && e.target.value) {
|
||||
const cursorPosition = e.target.selectionEnd
|
||||
const textBeforeCursorPosition = e.target.value.substring(0, cursorPosition)
|
||||
const textAfterCursorPosition = e.target.value.substring(cursorPosition, e.target.value.length)
|
||||
const body = {
|
||||
textBeforeCursorPosition,
|
||||
textAfterCursorPosition
|
||||
}
|
||||
setTextCursorPosition(body)
|
||||
} else {
|
||||
setTextCursorPosition({})
|
||||
}
|
||||
}
|
||||
|
||||
const onSelectOutputResponseClick = (node, isUserQuestion = false) => {
|
||||
let variablePath = isUserQuestion ? `question` : `${node.id}.data.instance`
|
||||
if (textCursorPosition) {
|
||||
let newInput = ''
|
||||
if (textCursorPosition.textBeforeCursorPosition === undefined && textCursorPosition.textAfterCursorPosition === undefined)
|
||||
newInput = `${inputValue}${`{{${variablePath}}}`}`
|
||||
else newInput = `${textCursorPosition.textBeforeCursorPosition}{{${variablePath}}}${textCursorPosition.textAfterCursorPosition}`
|
||||
setInputValue(newInput)
|
||||
}
|
||||
}
|
||||
|
||||
const component = show ? (
|
||||
<Dialog open={show} fullWidth maxWidth='md' aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description'>
|
||||
<DialogContent>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
{inputParam && inputParam.type === 'string' && (
|
||||
<div style={{ flex: 70 }}>
|
||||
<Typography sx={{ mb: 2, ml: 1 }} variant='h4'>
|
||||
{inputParam.label}
|
||||
</Typography>
|
||||
<PerfectScrollbar
|
||||
style={{
|
||||
border: '1px solid',
|
||||
borderColor: theme.palette.grey['500'],
|
||||
borderRadius: '12px',
|
||||
height: '100%',
|
||||
maxHeight: 'calc(100vh - 220px)',
|
||||
overflowX: 'hidden',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
{customization.isDarkMode ? (
|
||||
<DarkCodeEditor
|
||||
disabled={dialogProps.disabled}
|
||||
value={inputValue}
|
||||
onValueChange={(code) => setInputValue(code)}
|
||||
placeholder={inputParam.placeholder}
|
||||
type={languageType}
|
||||
onMouseUp={(e) => onMouseUp(e)}
|
||||
onBlur={(e) => onMouseUp(e)}
|
||||
style={{
|
||||
fontSize: '0.875rem',
|
||||
minHeight: 'calc(100vh - 220px)',
|
||||
width: '100%'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LightCodeEditor
|
||||
disabled={dialogProps.disabled}
|
||||
value={inputValue}
|
||||
onValueChange={(code) => setInputValue(code)}
|
||||
placeholder={inputParam.placeholder}
|
||||
type={languageType}
|
||||
onMouseUp={(e) => onMouseUp(e)}
|
||||
onBlur={(e) => onMouseUp(e)}
|
||||
style={{
|
||||
fontSize: '0.875rem',
|
||||
minHeight: 'calc(100vh - 220px)',
|
||||
width: '100%'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</PerfectScrollbar>
|
||||
</div>
|
||||
)}
|
||||
{!dialogProps.disabled && inputParam && inputParam.acceptVariable && (
|
||||
<div style={{ flex: 30 }}>
|
||||
<Stack flexDirection='row' sx={{ mb: 1, ml: 2 }}>
|
||||
<Typography variant='h4'>Select Variable</Typography>
|
||||
</Stack>
|
||||
<PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 220px)', overflowX: 'hidden' }}>
|
||||
<Box sx={{ pl: 2, pr: 2 }}>
|
||||
<List>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
p: 0,
|
||||
borderRadius: `${customization.borderRadius}px`,
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
mb: 1
|
||||
}}
|
||||
disabled={dialogProps.disabled}
|
||||
onClick={() => onSelectOutputResponseClick(null, true)}
|
||||
>
|
||||
<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='AI'
|
||||
src='https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png'
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
sx={{ ml: 1 }}
|
||||
primary='question'
|
||||
secondary={`User's question from chatbox`}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
{dialogProps.availableNodesForVariable &&
|
||||
dialogProps.availableNodesForVariable.length > 0 &&
|
||||
dialogProps.availableNodesForVariable.map((node, index) => {
|
||||
const selectedOutputAnchor = node.data.outputAnchors[0].options.find(
|
||||
(ancr) => ancr.name === node.data.outputs['output']
|
||||
)
|
||||
return (
|
||||
<ListItemButton
|
||||
key={index}
|
||||
sx={{
|
||||
p: 0,
|
||||
borderRadius: `${customization.borderRadius}px`,
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
mb: 1
|
||||
}}
|
||||
disabled={dialogProps.disabled}
|
||||
onClick={() => onSelectOutputResponseClick(node)}
|
||||
>
|
||||
<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={node.data.name}
|
||||
src={`${baseURL}/api/v1/node-icon/${node.data.name}`}
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
sx={{ ml: 1 }}
|
||||
primary={
|
||||
node.data.inputs.chainName ? node.data.inputs.chainName : node.data.id
|
||||
}
|
||||
secondary={`${selectedOutputAnchor?.label ?? 'output'} from ${
|
||||
node.data.label
|
||||
}`}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
</PerfectScrollbar>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>
|
||||
<StyledButton disabled={dialogProps.disabled} variant='contained' onClick={() => onConfirm(inputValue, inputParam.name)}>
|
||||
{dialogProps.confirmButtonName}
|
||||
</StyledButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
EditPromptValuesDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onConfirm: PropTypes.func
|
||||
}
|
||||
|
||||
export default EditPromptValuesDialog
|
||||
@@ -0,0 +1,105 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Button, Dialog, DialogActions, DialogContent, Typography } from '@mui/material'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import { DarkCodeEditor } from 'ui-component/editor/DarkCodeEditor'
|
||||
import { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'
|
||||
|
||||
import './ExpandTextDialog.css'
|
||||
|
||||
const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const languageType = 'json'
|
||||
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [inputParam, setInputParam] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.value) setInputValue(dialogProps.value)
|
||||
if (dialogProps.inputParam) setInputParam(dialogProps.inputParam)
|
||||
|
||||
return () => {
|
||||
setInputValue('')
|
||||
setInputParam(null)
|
||||
}
|
||||
}, [dialogProps])
|
||||
|
||||
const component = show ? (
|
||||
<Dialog open={show} fullWidth maxWidth='md' aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description'>
|
||||
<DialogContent>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
{inputParam && inputParam.type === 'string' && (
|
||||
<div style={{ flex: 70 }}>
|
||||
<Typography sx={{ mb: 2, ml: 1 }} variant='h4'>
|
||||
{inputParam.label}
|
||||
</Typography>
|
||||
<PerfectScrollbar
|
||||
style={{
|
||||
border: '1px solid',
|
||||
borderColor: theme.palette.grey['500'],
|
||||
borderRadius: '12px',
|
||||
height: '100%',
|
||||
maxHeight: 'calc(100vh - 220px)',
|
||||
overflowX: 'hidden',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
{customization.isDarkMode ? (
|
||||
<DarkCodeEditor
|
||||
disabled={dialogProps.disabled}
|
||||
value={inputValue}
|
||||
onValueChange={(code) => setInputValue(code)}
|
||||
placeholder={inputParam.placeholder}
|
||||
type={languageType}
|
||||
style={{
|
||||
fontSize: '0.875rem',
|
||||
minHeight: 'calc(100vh - 220px)',
|
||||
width: '100%'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LightCodeEditor
|
||||
disabled={dialogProps.disabled}
|
||||
value={inputValue}
|
||||
onValueChange={(code) => setInputValue(code)}
|
||||
placeholder={inputParam.placeholder}
|
||||
type={languageType}
|
||||
style={{
|
||||
fontSize: '0.875rem',
|
||||
minHeight: 'calc(100vh - 220px)',
|
||||
width: '100%'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</PerfectScrollbar>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>
|
||||
<StyledButton disabled={dialogProps.disabled} variant='contained' onClick={() => onConfirm(inputValue, inputParam.name)}>
|
||||
{dialogProps.confirmButtonName}
|
||||
</StyledButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
ExpandTextDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onConfirm: PropTypes.func
|
||||
}
|
||||
|
||||
export default ExpandTextDialog
|
||||
@@ -0,0 +1,56 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Dialog, DialogContent, DialogTitle } from '@mui/material'
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
import { JsonEditorInput } from 'ui-component/json/JsonEditor'
|
||||
|
||||
const FormatPromptValuesDialog = ({ show, dialogProps, onChange, onCancel }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
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'>
|
||||
Format Prompt Values
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<PerfectScrollbar
|
||||
style={{
|
||||
height: '100%',
|
||||
maxHeight: 'calc(100vh - 220px)',
|
||||
overflowX: 'hidden'
|
||||
}}
|
||||
>
|
||||
<JsonEditorInput
|
||||
onChange={(newValue) => onChange(newValue)}
|
||||
value={dialogProps.value}
|
||||
isDarkMode={customization.isDarkMode}
|
||||
inputParam={dialogProps.inputParam}
|
||||
nodes={dialogProps.nodes}
|
||||
edges={dialogProps.edges}
|
||||
nodeId={dialogProps.nodeId}
|
||||
/>
|
||||
</PerfectScrollbar>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
FormatPromptValuesDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
onCancel: PropTypes.func
|
||||
}
|
||||
|
||||
export default FormatPromptValuesDialog
|
||||
@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Dialog, DialogContent, DialogTitle } from '@mui/material'
|
||||
import ReactJson from 'react-json-view'
|
||||
import ReactJson from 'flowise-react-json-view'
|
||||
|
||||
const SourceDocDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { FormControl, OutlinedInput } from '@mui/material'
|
||||
import EditPromptValuesDialog from 'ui-component/dialog/EditPromptValuesDialog'
|
||||
import ExpandTextDialog from 'ui-component/dialog/ExpandTextDialog'
|
||||
|
||||
export const Input = ({ inputParam, value, onChange, disabled = false, showDialog, dialogProps, onDialogCancel, onDialogConfirm }) => {
|
||||
const [myValue, setMyValue] = useState(value ?? '')
|
||||
@@ -45,7 +45,7 @@ export const Input = ({ inputParam, value, onChange, disabled = false, showDialo
|
||||
/>
|
||||
</FormControl>
|
||||
{showDialog && (
|
||||
<EditPromptValuesDialog
|
||||
<ExpandTextDialog
|
||||
show={showDialog}
|
||||
dialogProps={dialogProps}
|
||||
onCancel={onDialogCancel}
|
||||
@@ -53,7 +53,7 @@ export const Input = ({ inputParam, value, onChange, disabled = false, showDialo
|
||||
setMyValue(newValue)
|
||||
onDialogConfirm(newValue, inputParamName)
|
||||
}}
|
||||
></EditPromptValuesDialog>
|
||||
></ExpandTextDialog>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { FormControl } from '@mui/material'
|
||||
import ReactJson from 'react-json-view'
|
||||
import { FormControl, Popover } from '@mui/material'
|
||||
import ReactJson from 'flowise-react-json-view'
|
||||
import SelectVariable from './SelectVariable'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { getAvailableNodesForVariable } from 'utils/genericHelper'
|
||||
|
||||
export const JsonEditorInput = ({ value, onChange, disabled = false, isDarkMode = false }) => {
|
||||
export const JsonEditorInput = ({ value, onChange, inputParam, nodes, edges, nodeId, disabled = false, isDarkMode = false }) => {
|
||||
const [myValue, setMyValue] = useState(value ? JSON.parse(value) : {})
|
||||
const [availableNodesForVariable, setAvailableNodesForVariable] = useState([])
|
||||
const [mouseUpKey, setMouseUpKey] = useState('')
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const openPopOver = Boolean(anchorEl)
|
||||
|
||||
const handleClosePopOver = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
|
||||
const setNewVal = (val) => {
|
||||
const newVal = cloneDeep(myValue)
|
||||
newVal[mouseUpKey] = val
|
||||
onChange(JSON.stringify(newVal))
|
||||
setMyValue((params) => ({
|
||||
...params,
|
||||
[mouseUpKey]: val
|
||||
}))
|
||||
}
|
||||
|
||||
const onClipboardCopy = (e) => {
|
||||
const src = e.src
|
||||
@@ -15,6 +37,13 @@ export const JsonEditorInput = ({ value, onChange, disabled = false, isDarkMode
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!disabled && nodes && edges && nodeId && inputParam) {
|
||||
const nodesForVariable = inputParam?.acceptVariable ? getAvailableNodesForVariable(nodes, edges, nodeId, inputParam.id) : []
|
||||
setAvailableNodesForVariable(nodesForVariable)
|
||||
}
|
||||
}, [disabled, inputParam, nodes, edges, nodeId])
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
@@ -30,28 +59,60 @@ export const JsonEditorInput = ({ value, onChange, disabled = false, isDarkMode
|
||||
/>
|
||||
)}
|
||||
{!disabled && (
|
||||
<ReactJson
|
||||
theme={isDarkMode ? 'ocean' : 'rjv-default'}
|
||||
style={{ padding: 10, borderRadius: 10 }}
|
||||
src={myValue}
|
||||
name={null}
|
||||
quotesOnKeys={false}
|
||||
displayDataTypes={false}
|
||||
enableClipboard={(e) => onClipboardCopy(e)}
|
||||
onEdit={(edit) => {
|
||||
setMyValue(edit.updated_src)
|
||||
onChange(JSON.stringify(edit.updated_src))
|
||||
}}
|
||||
onAdd={() => {
|
||||
//console.log(add)
|
||||
}}
|
||||
onDelete={(deleteobj) => {
|
||||
setMyValue(deleteobj.updated_src)
|
||||
onChange(JSON.stringify(deleteobj.updated_src))
|
||||
}}
|
||||
/>
|
||||
<div key={JSON.stringify(myValue)}>
|
||||
<ReactJson
|
||||
theme={isDarkMode ? 'ocean' : 'rjv-default'}
|
||||
style={{ padding: 10, borderRadius: 10 }}
|
||||
src={myValue}
|
||||
name={null}
|
||||
quotesOnKeys={false}
|
||||
displayDataTypes={false}
|
||||
enableClipboard={(e) => onClipboardCopy(e)}
|
||||
onMouseUp={(event) => {
|
||||
if (inputParam?.acceptVariable) {
|
||||
setMouseUpKey(event.name)
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
}}
|
||||
onEdit={(edit) => {
|
||||
setMyValue(edit.updated_src)
|
||||
onChange(JSON.stringify(edit.updated_src))
|
||||
}}
|
||||
onAdd={() => {
|
||||
//console.log(add)
|
||||
}}
|
||||
onDelete={(deleteobj) => {
|
||||
setMyValue(deleteobj.updated_src)
|
||||
onChange(JSON.stringify(deleteobj.updated_src))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</FormControl>
|
||||
{inputParam?.acceptVariable && (
|
||||
<Popover
|
||||
open={openPopOver}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClosePopOver}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left'
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left'
|
||||
}}
|
||||
>
|
||||
<SelectVariable
|
||||
disabled={disabled}
|
||||
availableNodesForVariable={availableNodesForVariable}
|
||||
onSelectAndReturnVal={(val) => {
|
||||
setNewVal(val)
|
||||
handleClosePopOver()
|
||||
}}
|
||||
/>
|
||||
</Popover>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -60,5 +121,9 @@ JsonEditorInput.propTypes = {
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
isDarkMode: PropTypes.bool
|
||||
isDarkMode: PropTypes.bool,
|
||||
inputParam: PropTypes.object,
|
||||
nodes: PropTypes.array,
|
||||
edges: PropTypes.array,
|
||||
nodeId: PropTypes.string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import { useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Box, List, ListItemButton, ListItem, ListItemAvatar, ListItemText, Typography, Stack } from '@mui/material'
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
|
||||
import { baseURL } from 'store/constant'
|
||||
|
||||
const SelectVariable = ({ availableNodesForVariable, disabled = false, onSelectAndReturnVal }) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
const onSelectOutputResponseClick = (node, isUserQuestion = false) => {
|
||||
let variablePath = isUserQuestion ? `question` : `${node.id}.data.instance`
|
||||
const newInput = `{{${variablePath}}}`
|
||||
onSelectAndReturnVal(newInput)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!disabled && (
|
||||
<div style={{ flex: 30 }}>
|
||||
<Stack flexDirection='row' sx={{ mb: 1, ml: 2, mt: 2 }}>
|
||||
<Typography variant='h5'>Select Variable</Typography>
|
||||
</Stack>
|
||||
<PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 220px)', overflowX: 'hidden' }}>
|
||||
<Box sx={{ pl: 2, pr: 2 }}>
|
||||
<List>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
p: 0,
|
||||
borderRadius: `${customization.borderRadius}px`,
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
mb: 1
|
||||
}}
|
||||
disabled={disabled}
|
||||
onClick={() => onSelectOutputResponseClick(null, true)}
|
||||
>
|
||||
<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='AI'
|
||||
src='https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png'
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText sx={{ ml: 1 }} primary='question' secondary={`User's question from chatbox`} />
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
{availableNodesForVariable &&
|
||||
availableNodesForVariable.length > 0 &&
|
||||
availableNodesForVariable.map((node, index) => {
|
||||
const selectedOutputAnchor = node.data.outputAnchors[0].options.find(
|
||||
(ancr) => ancr.name === node.data.outputs['output']
|
||||
)
|
||||
return (
|
||||
<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={() => onSelectOutputResponseClick(node)}
|
||||
>
|
||||
<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={node.data.name}
|
||||
src={`${baseURL}/api/v1/node-icon/${node.data.name}`}
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
sx={{ ml: 1 }}
|
||||
primary={node.data.inputs.chainName ? node.data.inputs.chainName : node.data.id}
|
||||
secondary={`${selectedOutputAnchor?.label ?? 'output'} from ${node.data.label}`}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
</PerfectScrollbar>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
SelectVariable.propTypes = {
|
||||
availableNodesForVariable: PropTypes.array,
|
||||
disabled: PropTypes.bool,
|
||||
onSelectAndReturnVal: PropTypes.func
|
||||
}
|
||||
|
||||
export default SelectVariable
|
||||
Reference in New Issue
Block a user