import React, {useState, useEffect, useLayoutEffect} from 'react'; import { IconButton, Dialog, Modal, Tooltip, DialogTitle, DialogContent, Typography, Paper, Menu, MenuItem, Button, } from '@material-ui/core'; import Checkbox from '@mui/material/Checkbox'; import { orange } from '@mui/material/colors'; import { isMobile } from "react-device-detect" import { GetParsedPaths, FindJsonPath } from "../views/Apps.jsx"; import NestedMenuItem from "material-ui-nested-menu-item"; import { FullscreenExit as FullscreenExitIcon, Extension as ExtensionIcon, Apps as AppsIcon, FavoriteBorder as FavoriteBorderIcon, Schedule as ScheduleIcon, FormatListNumbered as FormatListNumberedIcon, SquareFoot as SquareFootIcon, Circle as CircleIcon, Add as AddIcon, } from '@mui/icons-material'; import { AutoFixHigh as AutoFixHighIcon, CompressOutlined, QrCodeScannerOutlined, } from '@mui/icons-material'; import { useTheme } from '@material-ui/core/styles'; import { validateJson } from "../views/Workflows.jsx"; import ReactJson from "react-json-view"; import PaperComponent from "../components/PaperComponent.jsx"; import CodeMirror from '@uiw/react-codemirror'; import 'codemirror/keymap/sublime'; import 'codemirror/addon/selection/mark-selection.js' import 'codemirror/theme/gruvbox-dark.css'; import 'codemirror/theme/duotone-light.css'; import { padding, textAlign } from '@mui/system'; import data from '../frameworkStyle.jsx'; import { useNavigate, Link, useParams } from "react-router-dom"; const liquidFilters = [ {"name": "Size", "value": "size", "example": ""}, {"name": "Date", "value": `date: "%Y%m%d"`, "example": `{{ "now" | date: "%s" }}`}, {"name": "Escape String", "value": `{{ \"\"\"'string with weird'" quotes\"\"\" | escape_string }}`, "example": ``}, {"name": "Flatten", "value": `flatten`, "example": `{{ [1, [1, 2], [2, 3, 4]] | flatten }}`}, ] const mathFilters = [ {"name": "Plus", "value": "plus: 1", "example": `{{ "1" | plus: 1 }}`}, {"name": "Minus", "value": "minus: 1", "example": `{{ "1" | minus: 1 }}`}, ] const pythonFilters = [ {"name": "Hello World", "value": `{% python %}\nprint("hello world")\n{% endpython %}`, "example": ``}, {"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``}, ] const CodeEditor = (props) => { const { fieldCount, setFieldCount, actionlist, changeActionParameterCodeMirror, expansionModalOpen, setExpansionModalOpen, codedata, setcodedata, isFileEditor, runUpdateText } = props const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata); // const {codelang, setcodelang} = props const theme = useTheme(); 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 [editorPopupOpen, setEditorPopupOpen] = React.useState(false); const [currentCharacter, setCurrentCharacter] = React.useState(-1); const [currentLine, setCurrentLine] = React.useState(-1); const [variableOccurences, setVariableOccurences] = React.useState([]); const [currentLocation, setCurrentLocation] = React.useState([]); const [currentVariable, setCurrentVariable] = React.useState(""); const [anchorEl, setAnchorEl] = React.useState(null); const [anchorEl2, setAnchorEl2] = React.useState(null); const [anchorEl3, setAnchorEl3] = React.useState(null); const [mainVariables, setMainVariables] = React.useState([]); const [availableVariables, setAvailableVariables] = React.useState([]); const [menuPosition, setMenuPosition] = useState(null); const [showAutocomplete, setShowAutocomplete] = React.useState(false); const liquidOpen = Boolean(anchorEl); const mathOpen = Boolean(anchorEl2); const pythonOpen = Boolean(anchorEl3); const handleMenuClose = () => { setShowAutocomplete(false); setMenuPosition(null); } let navigate = useNavigate(); // console.log("is it file editor? - ", isFileEditor); useEffect(() => { var allVariables = [] var tmpVariables = [] if (actionlist === undefined || actionlist === null) { return } for(var i=0; i < actionlist.length; i++){ allVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) tmpVariables.push('$'+actionlist[i].autocomplete.toLowerCase()) var parsedPaths = [] if (typeof actionlist[i].example === "object") { parsedPaths = GetParsedPaths(actionlist[i].example, ""); } for (var key in parsedPaths) { const fullpath = "$"+actionlist[i].autocomplete.toLowerCase()+parsedPaths[key].autocomplete if (!allVariables.includes(fullpath)) { allVariables.push(fullpath) } } } setAvailableVariables(allVariables) setMainVariables(tmpVariables) }, []) const autoFormat = (input) => { if (validation !== true) { return } try { input = JSON.stringify(JSON.parse(input), null, 4) } catch (e) { console.log("Failed magic JSON stringification: ", e) } if (input !== localcodedata) { setlocalcodedata(input) } } const findIndex = (line, loc) => { // var temp_arr = [] // for(var i=0; i { // value.markText({line:0, ch:2}, {line:0, ch:8}, {"css": "background-color: #f85a3e; border-radius: 4px; color: white"}) // value.markText({line:0, ch:13}, {line:0, ch:15}, {"css": "background-color: #f85a3e; border-radius: 4px; color: white"}) // value.markText({line:0, ch:19}, {line:0, ch:26}, {"css": "background-color: #f85a3e; border-radius: 4px; color: white"}) // value.markText({line:0, ch:31}, {line:0, ch:35}, {"css": "background-color: #f85a3e; border-radius: 4px; color: white"}) // value.markText({line:0, ch:69}, {line:0, ch:73}, {"css": "background-color: #f85a3e; border-radius: 4px; color: white"}) // var code_variables = value.getValue().match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) // console.log(code_variables) // var regex = /[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g // var code_variables_loc = regex.exec(value.getValue()) // console.log(code_variables_loc) // while ((code_variables_loc = regex.exec(value.getValue())) != null) { // console.log(code_variables_loc); // } // var code_variables_loc = regex.exec(value.getValue()) // console.log(code_variables_loc) var code_lines = localcodedata.split('\n') for (var i = 0; i < code_lines.length; i++){ var current_code_line = code_lines[i] // console.log(current_code_line) var variable_occurence = current_code_line.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) // console.log(variable_occurence) // console.log() // for (var j = 0; j < actionlist.length; j++) { // if(found[i].slice(1,).toLowerCase() === actionlist[j].autocomplete.toLowerCase()){ // input = input.replace(found[i], JSON.stringify(actionlist[j].example)); // console.log(input) // console.log(actionlist[j].example) // } // console.log(actionlist[j].autocomplete); // } var dollar_occurence = [] for(var ch=0; ch action.autocomplete.toLowerCase() === variable_occurence[occ].slice(1,).toLowerCase()) var correctVariable = availableVariables.includes(variable_occurence[occ].toLowerCase()) // console.log(actionlist) if(!correctVariable) { value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: rgb(248, 106, 62, 0.9); padding-top: 2px; padding-bottom: 2px; color: white"}) } else{ value.markText({line:i, ch:dollar_occurence[occ]}, {line:i, ch:dollar_occurence_len[occ]+dollar_occurence[occ]}, {"css": "background-color: #8b8e26; padding-top: 2px; padding-bottom: 2px; color: white"}) } // console.log(correctVariables) } } catch (e) {} } } // function findlocation(arr) { // for(var i=0; i { // var updatedCode = localcodedata.slice(0,index) + "$" + str + localcodedata.slice(index+currentVariable.length+1,) // setlocalcodedata(updatedCode) // setEditorPopupOpen(false) // setCurrentLocation(0) // console.log(index) // console.log(currentLocation) var code_lines = localcodedata.split('\n') var parsedVariable = currentVariable if (currentVariable === undefined || currentVariable === null) { console.log("Location: ", currentLocation) parsedVariable= "$" } code_lines[currentLine] = code_lines[currentLine].slice(0,currentLocation[1]) + "$" + swapVariable + code_lines[currentLine].slice(currentLocation[1]+parsedVariable.length,) // console.log(code_lines) var updatedCode = code_lines.join('\n') // console.log(updatedCode) setlocalcodedata(updatedCode) } const expectedOutput = (input) => { const found = input.match(/[$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,}/g) //console.log(found) // Whelp this is inefficient af. Single loop pls // When the found array is empty. try { for (var i = 0; i < found.length; i++) { try { found[i] = found[i].toLowerCase() var valuefound = false for (var j = 0; j < actionlist.length; j++) { if(found[i].slice(1,).toLowerCase() === actionlist[j].autocomplete.toLowerCase()){ valuefound = true try { input = input.replace(found[i], JSON.stringify(actionlist[j].example)); } catch (e) { input = input.replace(found[i], actionlist[j].example) } } else { } } if (!valuefound && availableVariables.includes(found[i])) { var shouldbreak = false for (var k=0; k < actionlist.length; k++){ var parsedPaths = [] if (typeof actionlist[k].example === "object") { parsedPaths = GetParsedPaths(actionlist[k].example, ""); } for (var key in parsedPaths) { const fullpath = "$"+actionlist[k].autocomplete.toLowerCase()+parsedPaths[key].autocomplete if (fullpath === found[i]) { //if (actionlist[k].example === undefined) { // actionlist[k].example = "TMP" //} try { var new_input = FindJsonPath(fullpath, actionlist[k].example) } catch (e) { console.log("ERR IN INPUT: ", e) } if (typeof new_input === "object") { new_input = JSON.stringify(new_input) } else { if (typeof new_input === "string") { new_input = new_input } else { new_input = "" } } input = input.replace(found[i], new_input) //} catch (e) { // input = input.replace(found[i], actionlist[k].example) //} shouldbreak = true break } } if (shouldbreak) { break } } } } catch (e) { console.log("Replace error: ", e) } } } catch (e) { } const tmpValidation = validateJson(input.valueOf()) //setValidation(true) if (tmpValidation.valid === true) { setValidation(true) setExpOutput(tmpValidation.result) } else { setExpOutput(input.valueOf()) setValidation(false) } } const handleItemClick = (values) => { if ( values === undefined || values === null || values.length === 0 ) { return; } var toComplete = localcodedata.trim().endsWith("$") ? values[0].autocomplete : "$" + values[0].autocomplete; toComplete = toComplete.toLowerCase().replaceAll(" ", "_"); for (var key in values) { if (key == 0 || values[key].autocomplete.length === 0) { continue; } toComplete += values[key].autocomplete; } setlocalcodedata(localcodedata+toComplete) setMenuPosition(null) } const handleClick = (item) => { if (item === undefined || item.value === undefined || item.value === null) { return } if (!item.value.includes("{%") && !item.value.includes("{{")) { setlocalcodedata(localcodedata+" | "+item.value+" }}") } else { setlocalcodedata(localcodedata+item.value) } setAnchorEl(null) setAnchorEl2(null) setAnchorEl3(null) } return ( { console.log("In closer") changeActionParameterCodeMirror({target: {value: ""}}, fieldCount, localcodedata) //setExpansionModalOpen(false) }} PaperComponent={PaperComponent} PaperProps={{ style: { backgroundColor: theme.palette.surfaceColor, color: "white", minWidth: isMobile ? "100%" : 600, padding: isMobile ? "25px 10px 25px 10px" : 25, border: theme.palette.defaultBorder, zIndex: 10012, }, }} > { isFileEditor ?
File Editor
:
Code Editor { }} > { autoFormat(localcodedata) }} >
} { isFileEditor ? null :
{ setAnchorEl(null); }} MenuListProps={{ 'aria-labelledby': 'basic-button', }} > {liquidFilters.map((item, index) => { return ( { handleClick(item) }}>{item.name} ) })} { setAnchorEl2(null); }} MenuListProps={{ 'aria-labelledby': 'basic-button', }} > {mathFilters.map((item, index) => { return ( { handleClick(item) }}>{item.name} ) })} { setAnchorEl3(null); }} MenuListProps={{ 'aria-labelledby': 'basic-button', }} > {pythonFilters.map((item, index) => { return ( { handleClick(item) }}>{item.name} ) })} { handleMenuClose(); }} open={!!menuPosition} style={{ color: "white", marginTop: 2, maxHeight: 650, }} > {actionlist.map((innerdata) => { const icon = innerdata.type === "action" ? ( ) : innerdata.type === "workflow_variable" || innerdata.type === "execution_variable" ? ( ) : ( ); const handleExecArgumentHover = (inside) => { var exec_text_field = document.getElementById( "execution_argument_input_field" ); if (exec_text_field !== null) { if (inside) { exec_text_field.style.border = "2px solid #f85a3e"; } else { exec_text_field.style.border = ""; } } }; const handleActionHover = (inside, actionId) => { }; const handleMouseover = () => { if (innerdata.type === "Execution Argument") { handleExecArgumentHover(true); } else if (innerdata.type === "action") { handleActionHover(true, innerdata.id); } }; const handleMouseOut = () => { if (innerdata.type === "Execution Argument") { handleExecArgumentHover(false); } else if (innerdata.type === "action") { handleActionHover(false, innerdata.id); } }; var parsedPaths = []; if (typeof innerdata.example === "object") { parsedPaths = GetParsedPaths(innerdata.example, ""); } const coverColor = "#82ccc3" //menuPosition.left -= 50 //menuPosition.top -= 250 //console.log("POS: ", menuPosition1) var menuPosition1 = menuPosition if (menuPosition1 === null) { menuPosition1 = { "left": 0, "top": 0, } } else if (menuPosition1.top === null || menuPosition1.top === undefined) { menuPosition1.top = 0 } else if (menuPosition1.left === null || menuPosition1.left === undefined) { menuPosition1.left = 0 } //console.log("POS1: ", menuPosition1) return parsedPaths.length > 0 ? ( {icon} {innerdata.name}
} parentMenuOpen={!!menuPosition} style={{ color: "white", minWidth: 250, maxWidth: 250, maxHeight: 50, overflow: "hidden", }} onClick={() => { console.log("CLICKED: ", innerdata); console.log(innerdata.example) handleItemClick([innerdata]); }} > { //console.log("HOVER: ", pathdata); }} onClick={() => { handleItemClick([innerdata]); }} > {innerdata.name} {parsedPaths.map((pathdata, index) => { // FIXME: Should be recursive in here // const icon = pathdata.type === "value" ? ( ) : pathdata.type === "list" ? ( ) : ( ); // const indentation_count = (pathdata.name.match(/\./g) || []).length+1 //const boxPadding = pathdata.type === "object" ? "10px 0px 0px 0px" : 0 const boxPadding = 0 const namesplit = pathdata.name.split(".") const newname = namesplit[namesplit.length-1] return ( { //console.log("HOVER: ", pathdata); }} onClick={() => { handleItemClick([innerdata, pathdata]); }} >
{Array(indentation_count).fill().map((subdata, subindex) => { return (
) })} {icon} {newname} {pathdata.type === "list" ? { e.preventDefault() e.stopPropagation() console.log("INNER: ", innerdata, pathdata) // Removing .list from autocomplete var newname = pathdata.name if (newname.length > 5) { newname = newname.slice(0, newname.length-5) } //selectedActionParameters[count].value += `{{ $${innerdata.name}.${newname} | size }}` //selectedAction.parameters[count].value = selectedActionParameters[count].value; //setSelectedAction(selectedAction); //setShowDropdown(false); setMenuPosition(null); // innerdata.name // pathdata.name //handleItemClick([innerdata, newpathdata]) //console.log("CLICK LENGTH!") }} /> : null}
); })} ) : ( handleMouseover()} onMouseOut={() => { handleMouseOut(); }} onClick={() => { handleItemClick([innerdata]); }} >
{icon} {innerdata.name}
); })}
} { // console.log(value.getCursor()) setCurrentCharacter(value.getCursor().ch) setCurrentLine(value.getCursor().line) // console.log(value.getCursor().ch, value.getCursor().line) findIndex(value.getCursor().line, value.getCursor().ch) highlight_variables(value) }} onChange={(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) }} options={{ styleSelectedText: true, theme: codeTheme, keyMap: 'sublime', mode: 'python', lineWrapping: linewrap, // mode: {codelang}, }} /> {/*editorPopupOpen ? {mainVariables.map((data, index) => { // console.log(data) return (
) })}
: null*/}
{/* Line Wrap { if (linewrap) { setlinewrap(false) } if (!linewrap){ setlinewrap(true) } }} defaultChecked size="small" sx={{ color: orange[600], '&.Mui-checked': { color: orange[800], }, }} /> Dark Theme { if (codeTheme === "gruvbox-dark") { setcodeTheme("duotone-light") } if (codeTheme === "duotone-light"){ setcodeTheme("gruvbox-dark") } }} defaultChecked size="small" sx={{ color: orange[600], '&.Mui-checked': { color: orange[800], }, }} /> */}
{isFileEditor ? null :
{isMobile ? null : Output } {isMobile ? null : validation === true ? { //handleReactJsonClipboard(copy); }} displayDataTypes={false} onSelect={(select) => { //HandleJsonCopy(validate.result, select, "exec"); }} name={"JSON autocompletion"} /> :

{expOutput}

}

JSON Validation: {validation ? "Correct" : "Incorrect"}

}
) } export default CodeEditor;