diff --git a/frontend/package.json b/frontend/package.json index eb281a80..c7ba31ca 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,8 +14,8 @@ "@mui/styles": "^5.14.0", "@mui/x-data-grid": "^5.17.11", "@mui/x-date-pickers": "^6.11.1", - "@uiw/codemirror-themes": "^4.21.7", - "@uiw/react-codemirror": "^4.21.7", + "@uiw/codemirror-themes": "^4.21.9", + "@uiw/react-codemirror": "^4.21.9", "@use-it/interval": "^1.0.0", "algoliasearch": "^4.13.1", "calculate-size": "^1.1.1", diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 4843bfc7..9c0c5c64 100755 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -92,7 +92,7 @@ import { //import CodeMirror from "@uiw/react-codemirror"; //import "codemirror/keymap/sublime"; //import "codemirror/theme/gruvbox-dark.css"; -import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; +//import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; const useStyles = makeStyles({ notchedOutline: { @@ -158,28 +158,30 @@ const ParsedAction = (props) => { authenticationType, appAuthentication, getAppAuthentication, - actionDelayChange, - getParents, - isCloud, - lastSaved, - setLastSaved, - setShowVideo, - toolsAppId, - aiSubmit, - //expansionModalOpen, - //setExpansionModalOpen, + actionDelayChange, + getParents, + isCloud, + lastSaved, + setLastSaved, + setShowVideo, + toolsAppId, + aiSubmit, + + expansionModalOpen, + setExpansionModalOpen, + + setEditorData, + setcodedata, } = props; const classes = useStyles(); const alert = useAlert() - - const [expansionModalOpen, setExpansionModalOpen] = React.useState(false); + const [hideBody, setHideBody] = React.useState(true); const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false); - const [codedata, setcodedata] = React.useState(""); - const [fieldCount, setFieldCount] = React.useState(0); - const [hiddenDescription, setHiddenDescription] = React.useState(true); + const [fieldCount, setFieldCount] = React.useState(0); + const [hiddenDescription, setHiddenDescription] = React.useState(true); useEffect(() => { @@ -1518,29 +1520,6 @@ const ParsedAction = (props) => { const clickedFieldId = "rightside_field_" + count; - const shufflecode = fieldCount !== count ? null : - ( - - ) - // { disableUnderline: true, endAdornment: hideExtraTypes ? null : ( - - - { - event.preventDefault() - setFieldCount(count) - setcodedata(data.value) - setExpansionModalOpen(true) - }} - /> - - - { - event.preventDefault() + + + { + event.preventDefault() + setFieldCount(count) + setExpansionModalOpen(true) - // Get cursor position - // This makes it so we can put it in the right location? - setMenuPosition({ - top: event.pageY + 10, - left: event.pageX + 10, - }); - setShowDropdownNumber(count); - setShowDropdown(true); - setShowAutocomplete(true); - }} - /> - - - + //setcodedata(data.value) + + setEditorData({ + "name": data.name, + "value": data.value, + "field_number": count, + "actionlist": actionlist, + "field_id": clickedFieldId, + }) + }} + /> + + + { + event.preventDefault() + + // Get cursor position + // This makes it so we can put it in the right location? + setMenuPosition({ + top: event.pageY + 10, + left: event.pageX + 10, + }); + setShowDropdownNumber(count); + setShowDropdown(true); + setShowAutocomplete(true); + }} + /> + + + ), }} multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline} @@ -1651,11 +1639,11 @@ const ParsedAction = (props) => { */ //console.log("Clicked field: ", clickedFieldId) - if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { - scrollConfig.selected = clickedFieldId - setScrollConfig(scrollConfig) - //console.log("Change field id!") - } + if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { + scrollConfig.selected = clickedFieldId + setScrollConfig(scrollConfig) + //console.log("Change field id!") + } }} id={clickedFieldId} rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows} @@ -2567,7 +2555,7 @@ const ParsedAction = (props) => { */} {datafield} - {shufflecode} + {/*shufflecode*/} {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && @@ -3542,8 +3530,6 @@ const ParsedAction = (props) => { return null; } - console.log("OPTION: ", option) - const newname = ( option.name.charAt(0).toUpperCase() + option.name.substring(1) ).replaceAll("_", " "); diff --git a/frontend/src/components/ShuffleCodeEditor.jsx b/frontend/src/components/ShuffleCodeEditor.jsx index 0a0e48fc..21293d7c 100644 --- a/frontend/src/components/ShuffleCodeEditor.jsx +++ b/frontend/src/components/ShuffleCodeEditor.jsx @@ -54,9 +54,10 @@ import {indentWithTab} from "@codemirror/commands" import { padding, textAlign } from '@mui/system'; import data from '../frameworkStyle.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; +import { tags as t } from '@lezer/highlight'; import { createTheme } from '@uiw/codemirror-themes'; -import { tags } from '@lezer/highlight'; + const liquidFilters = [ {"name": "Size", "value": "size", "example": ""}, @@ -81,44 +82,39 @@ const pythonFilters = [ {"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``}, ] -//const shuffleTheme = createTheme({ -// theme: 'dark', -// settings: { -// background: '#282828', -// foreground: '#282828', -// caret: '#5d00ff', -// selection: '#036dd626', -// selectionMatch: '#036dd626', -// lineHighlight: '#8a91991a', -// gutterBackground: '#282828', -// gutterForeground: '#8a919966', -// fontSize: 18, -// borderRadius: theme.palette.borderRadius, -// border: `2px solid ${theme.palette.inputColor}`, -// }, -// styles: [ -// { tag: tags.comment, color: '#787b8099' }, -// { tag: tags.variableName, color: '#0080ff' }, -// { tag: [tags.string, tags.special(tags.brace)], color: '#5c6166' }, -// { tag: tags.number, color: '#5c6166' }, -// { tag: tags.bool, color: '#5c6166' }, -// { tag: tags.null, color: '#5c6166' }, -// { tag: tags.keyword, color: '#5c6166' }, -// { tag: tags.operator, color: '#5c6166' }, -// { tag: tags.className, color: '#5c6166' }, -// { tag: tags.definition(tags.typeName), color: '#5c6166' }, -// { tag: tags.typeName, color: '#5c6166' }, -// { tag: tags.angleBracket, color: '#5c6166' }, -// { tag: tags.tagName, color: '#5c6166' }, -// { tag: tags.attributeName, color: '#5c6166' }, -// ], -//}); +const shuffleTheme = createTheme({ + theme: 'dark', + settings: { + background: "rgba(40,40,40, 1)", + foreground: '#75baff', + caret: '#5d00ff', + selection: '#036dd626', + selectionMatch: '#036dd626', + lineHighlight: '#8a91991a', + gutterForeground: '#8a919966', + }, + styles: [ + { tag: t.comment, color: '#787b8099' }, + { tag: t.variableName, color: '#0080ff' }, + { tag: [t.string, t.special(t.brace)], color: '#5c6166' }, + { tag: t.number, color: '#5c6166' }, + { tag: t.bool, color: '#5c6166' }, + { tag: t.null, color: '#5c6166' }, + { tag: t.keyword, color: '#5c6166' }, + { tag: t.operator, color: '#5c6166' }, + { tag: t.className, color: '#5c6166' }, + { tag: t.definition(t.typeName), color: '#5c6166' }, + { tag: t.typeName, color: '#5c6166' }, + { tag: t.angleBracket, color: '#5c6166' }, + { tag: t.tagName, color: '#5c6166' }, + { tag: t.attributeName, color: '#5c6166' }, + ], +}); const CodeEditor = (props) => { const { globalUrl, fieldCount, - setFieldCount, actionlist, changeActionParameterCodeMirror, expansionModalOpen, @@ -132,6 +128,8 @@ const CodeEditor = (props) => { selectedAction , workflowExecutions, getParents, + + fieldname, } = props const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); @@ -140,7 +138,7 @@ const CodeEditor = (props) => { const [validation, setValidation] = React.useState(false); const [expOutput, setExpOutput] = React.useState(" "); const [linewrap, setlinewrap] = React.useState(true); - const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark"); + //const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark"); const [editorPopupOpen, setEditorPopupOpen] = React.useState(false); const [currentCharacter, setCurrentCharacter] = React.useState(-1); @@ -155,8 +153,8 @@ const CodeEditor = (props) => { const [mainVariables, setMainVariables] = React.useState([]); const [availableVariables, setAvailableVariables] = React.useState([]); - const [menuPosition, setMenuPosition] = useState(null); - const [showAutocomplete, setShowAutocomplete] = React.useState(false); + const [menuPosition, setMenuPosition] = useState(null); + const [showAutocomplete, setShowAutocomplete] = React.useState(false); const [isAiLoading, setIsAiLoading] = React.useState(false); @@ -919,7 +917,6 @@ const CodeEditor = (props) => { maxHeight: isMobile ? "100%" : 720, border: theme.palette.defaultBorder, padding: isMobile ? "25px 10px 25px 10px" : 25, - backgroundColor: theme.palette.surfaceColor, }, }} > @@ -1020,6 +1017,7 @@ const CodeEditor = (props) => { aria-controls={liquidOpen ? 'basic-menu' : undefined} aria-expanded={liquidOpen ? 'true' : undefined} variant="outlined" + color="secondary" style={{ textTransform: "none", width: 100, @@ -1055,6 +1053,7 @@ const CodeEditor = (props) => { aria-controls={mathOpen ? 'basic-menu' : undefined} aria-expanded={mathOpen ? 'true' : undefined} variant="outlined" + color="secondary" style={{ textTransform: "none", width: 100, @@ -1090,6 +1089,7 @@ const CodeEditor = (props) => { aria-controls={pythonOpen ? 'basic-menu' : undefined} aria-expanded={pythonOpen ? 'true' : undefined} variant="outlined" + color="secondary" style={{ textTransform: "none", width: 100, @@ -1125,6 +1125,7 @@ const CodeEditor = (props) => { aria-controls={!!menuPosition ? 'basic-menu' : undefined} aria-expanded={!!menuPosition ? 'true' : undefined} variant="outlined" + color="secondary" style={{ textTransform: "none", width: 130, @@ -1381,7 +1382,7 @@ const CodeEditor = (props) => { position: "relative", }}> { wordBreak: "break-word", marginTop: 0, paddingTop: 0, + backgroundColor: "rgba(40,40,40,1)", + minHeight: 470, }} onCursorActivity = {(value) => { // console.log(value.getCursor()) @@ -1398,27 +1401,22 @@ const CodeEditor = (props) => { findIndex(value.getCursor().line, value.getCursor().ch) highlight_variables(value) }} - onChange={(value) => { - //console.log("Value: '", value.getValue(), "'") + onChange={(value, viewUpdate) => { + console.log("Value: ", value, viewUpdate) + setlocalcodedata(value) + expectedOutput(value) - setlocalcodedata(value.getValue()) - expectedOutput(value.getValue()) - - if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){ - setEditorPopupOpen(true) - } - - // console.log(findIndex(value.getValue())) - // highlight_variables(value) + //if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){ + // setEditorPopupOpen(true) + //} }} - extensions={[indentWithTab]} + extensions={[]}//indentWithTab]} + theme={shuffleTheme} options={{ styleSelectedText: true, - theme: codeTheme, keyMap: 'sublime', mode: validation === true ? "json" : "python", lineWrapping: linewrap, - }} /> @@ -1664,34 +1662,29 @@ const CodeEditor = (props) => {
- - +
) } diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index ef91194b..0ecece8d 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -4,6 +4,7 @@ import { createTheme, adaptV4Theme } from "@mui/material/styles"; //const theme = createTheme({ const theme = createTheme(adaptV4Theme({ palette: { + theme: "dark", main: "#F86743", primary: { main: "#F86743", diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index aa4eda05..1294165b 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -16,6 +16,7 @@ import { useAlert } from "react-alert"; import { isMobile } from "react-device-detect" import aa from 'search-insights' import Drift from "react-driftjs"; +import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx"; import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom'; import algoliasearch from 'algoliasearch/lite'; @@ -516,7 +517,7 @@ const AngularWorkflow = (defaultprops) => { const [selectionOpen, setSelectionOpen] = React.useState(false); // eslint-disable-next-line no-unused-vars - const [_, setUpdate] = useState(""); // Used for rendring, don't remove + const [_, setUpdate] = useState(""); // Used to force rendring, don't remove const [workflowExecutions, setWorkflowExecutions] = React.useState([]); const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0); @@ -532,7 +533,16 @@ const AngularWorkflow = (defaultprops) => { "attachedTo": "", }); - + // For code editor + const [codeEditorModalOpen, setCodeEditorModalOpen] = React.useState(false); + const [codedata, setcodedata] = React.useState(""); + const [editorData, setEditorData] = React.useState({ + "name": "", + "value": "", + "field_number": -1, + "actionlist": [], + "field_id": "", + }) // This should all be set once, not on every iteration // Use states and don't update lol @@ -12798,8 +12808,11 @@ const AngularWorkflow = (defaultprops) => { requiresAuthentication={requiresAuthentication} setLastSaved={setLastSaved} lastSaved={lastSaved} - aiSubmit={aiSubmit} + + expansionModalOpen={codeEditorModalOpen} + setExpansionModalOpen={setCodeEditorModalOpen} + setEditorData={setEditorData} /> } else if (Object.getOwnPropertyNames(selectedComment).length > 0) { @@ -14993,8 +15006,8 @@ const AngularWorkflow = (defaultprops) => { {/* Looks for triggers" */} {/* Only fixed the ones that require scrolling on a small screen */} - {/* Most prominent: Actions. But these are a lot more complex */} - {rightSideBarOpen ? + {/* Most important: Actions. But these are a lot more complex */} + {rightSideBarOpen && (selectedTrigger.trigger_type === "SCHEDULE" || selectedTrigger.trigger_type === "WEBHOOK") ?
{Object.getOwnPropertyNames(selectedTrigger).length > 0 ? selectedTrigger.trigger_type === "SCHEDULE" ? @@ -16599,6 +16612,91 @@ const AngularWorkflow = (defaultprops) => {
+ const changeActionParameterCodeMirror = (event, count, data, actionlist) => { + if (data.startsWith("${") && data.endsWith("}")) { + // PARAM FIX - Gonna use the ID field, even though it's a hack + const paramcheck = selectedAction.parameters.find(param => param.name === "body") + if (paramcheck !== undefined) { + // Escapes all double quotes + const toReplace = event.target.value.trim().replaceAll("\\\"", "\"").replaceAll("\"", "\\\""); + console.log("REPLACE WITH: ", toReplace) + if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) { + paramcheck["value_replace"] = [{ + "key": data.name, + "value": toReplace, + }] + + console.log("IN IF: ", paramcheck) + + } else { + const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name) + if (subparamindex === -1) { + paramcheck["value_replace"].push({ + "key": data.name, + "value": toReplace, + }) + } else { + paramcheck["value_replace"][subparamindex]["value"] = toReplace + } + + console.log("IN ELSE: ", paramcheck) + } + + if (paramcheck["value_replace"] === undefined) { + selectedAction.parameters[count]["value_replace"] = paramcheck + } else { + //selectedActionParameters[count]["value_replace"] = paramcheck["value_replace"] + selectedAction.parameters[count]["value_replace"] = paramcheck["value_replace"] + } + setSelectedAction(selectedAction) + //setUpdate(Math.random()) + return + } + } + + if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) { + console.log("GET THE LAST ARGUMENT FOR NODE!") + + var curstring = "" + var record = false + for (let [key,keyval] in Object.entries(selectedAction.parameters[count].value)) { + const item = selectedAction.parameters[count].value[key] + if (record) { + curstring += item + } + + if (item === "$") { + record = true + curstring = "" + } + } + + //console.log("CURSTRING: ", curstring) + if (curstring.length > 0 && actionlist !== null) { + // Search back in the action list + curstring = curstring.split(" ").join("_").toLowerCase() + var actionItem = actionlist.find(data => data.autocomplete.split(" ").join("_").toLowerCase() === curstring) + if (actionItem !== undefined) { + console.log("Found item: ", actionItem) + + var jsonvalid = true + try { + const tmp = String(JSON.parse(actionItem.example)) + if (!actionItem.example.includes("{") && !actionItem.example.includes("[")) { + jsonvalid = false + } + } catch (e) { + jsonvalid = false + } + } + } + } + + selectedAction.parameters[count].autocompleted = false + selectedAction.parameters[count].value = data + setSelectedAction(selectedAction) + //setUpdate(Math.random()) + } const loadedCheck = isLoaded && workflowDone ? ( @@ -16614,6 +16712,31 @@ const AngularWorkflow = (defaultprops) => { {workflowRevisions} + {codeEditorModalOpen ? + + : null} + {editWorkflowModalOpen === true ? { */} {loadedCheck} + ); };