diff options
Diffstat (limited to 'extension/react-app/src/components/StepContainer.tsx')
-rw-r--r-- | extension/react-app/src/components/StepContainer.tsx | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx new file mode 100644 index 00000000..03649b66 --- /dev/null +++ b/extension/react-app/src/components/StepContainer.tsx @@ -0,0 +1,208 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import styled, { keyframes } from "styled-components"; +import { + appear, + buttonColor, + defaultBorderRadius, + MainContainerWithBorder, + MainTextInput, + secondaryDark, + vscBackground, + GradientBorder, +} from "."; +import { RangeInFile, FileEdit } from "../../../src/client"; +import CodeBlock from "./CodeBlock"; +import SubContainer from "./SubContainer"; + +import { + ChevronDown, + ChevronRight, + Backward, +} from "@styled-icons/heroicons-outline"; +import { HistoryNode } from "../../../schema/HistoryNode"; +import ReactMarkdown from "react-markdown"; +import ContinueButton from "./ContinueButton"; + +interface StepContainerProps { + historyNode: HistoryNode; + onReverse: () => void; + inFuture: boolean; + onRefinement: (input: string) => void; + onUserInput: (input: string) => void; +} + +const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>` + opacity: ${(props) => (props.inFuture ? 0.3 : 1)}; + animation: ${appear} 0.3s ease-in-out; + /* padding-left: ${(props) => props.stepDepth * 20}px; */ + overflow: hidden; +`; + +const StepContainerDiv = styled.div<{ open: boolean }>` + background-color: ${(props) => (props.open ? vscBackground : secondaryDark)}; + border-radius: ${defaultBorderRadius}; + padding: 8px; +`; + +const HeaderDiv = styled.div` + display: grid; + grid-template-columns: 1fr auto; + align-items: center; +`; + +const HeaderButton = styled.button` + background-color: transparent; + border: 1px solid white; + border-radius: ${defaultBorderRadius}; + padding: 2px; + cursor: pointer; + color: white; + + &:hover { + background-color: white; + color: black; + } +`; + +const OnHoverDiv = styled.div` + text-align: center; + padding: 10px; + animation: ${appear} 0.3s ease-in-out; +`; + +const NaturalLanguageInput = styled(MainTextInput)` + width: 80%; +`; + +function StepContainer(props: StepContainerProps) { + const [open, setOpen] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const naturalLanguageInputRef = useRef<HTMLTextAreaElement>(null); + + useEffect(() => { + if (isHovered) { + naturalLanguageInputRef.current?.focus(); + } + }, [isHovered]); + + const onTextInput = useCallback(() => { + if (naturalLanguageInputRef.current) { + props.onRefinement(naturalLanguageInputRef.current.value); + naturalLanguageInputRef.current.value = ""; + } + }, [naturalLanguageInputRef]); + + return ( + <MainDiv + stepDepth={(props.historyNode.depth as any) || 0} + inFuture={props.inFuture} + onMouseEnter={() => { + setIsHovered(true); + }} + onMouseLeave={() => { + setIsHovered(false); + }} + hidden={props.historyNode.step.hide as any} + > + <GradientBorder + className="m-2 overflow-hidden cursor-pointer" + onClick={() => setOpen((prev) => !prev)} + > + <StepContainerDiv open={open}> + <HeaderDiv> + <h4 className="m-2 cursor-pointer"> + {open ? ( + <ChevronDown size="1.4em" /> + ) : ( + <ChevronRight size="1.4em" /> + )} + {props.historyNode.step.name as any}: + </h4> + <HeaderButton + onClick={(e) => { + e.stopPropagation(); + props.onReverse(); + }} + > + <Backward size="1.6em" onClick={props.onReverse}></Backward> + </HeaderButton> + </HeaderDiv> + + <ReactMarkdown key={1} className="overflow-scroll"> + {props.historyNode.step.description as any} + </ReactMarkdown> + + {props.historyNode.step.name === "Waiting for user input" && ( + <input + className="m-auto p-2 rounded-md border-1 border-solid text-white w-3/4 border-gray-200 bg-vsc-background" + onKeyDown={(e) => { + if (e.key === "Enter") { + props.onUserInput(e.currentTarget.value); + } + }} + type="text" + onSubmit={(ev) => { + props.onUserInput(ev.currentTarget.value); + }} + /> + )} + {props.historyNode.step.name === "Waiting for user confirmation" && ( + <> + <input + type="button" + value="Cancel" + className="m-4 p-2 rounded-md border border-solid text-white border-gray-200 bg-vsc-background cursor-pointer hover:bg-white hover:text-black" + ></input> + <input + className="m-4 p-2 rounded-md border border-solid text-white border-gray-200 bg-vsc-background cursor-pointer hover:bg-white hover:text-black" + onClick={(e) => { + props.onUserInput("ok"); + e.preventDefault(); + e.stopPropagation(); + }} + type="button" + value="Confirm" + /> + </> + )} + + {open && ( + <> + {/* {props.historyNode.observation && ( + <SubContainer title="Error"> + <CodeBlock>Error Here</CodeBlock> + </SubContainer> + )} */} + {/* {props.iterationContext.suggestedChanges.map((sc) => { + return ( + <SubContainer title="Suggested Change"> + {sc.filepath} + <CodeBlock>{sc.replacement}</CodeBlock> + </SubContainer> + ); + })} */} + </> + )} + </StepContainerDiv> + </GradientBorder> + + <OnHoverDiv hidden={!open}> + <NaturalLanguageInput + onKeyDown={(e) => { + if (e.key === "Enter") { + onTextInput(); + } + }} + ref={naturalLanguageInputRef} + onClick={(e) => { + e.stopPropagation(); + e.preventDefault(); + }} + ></NaturalLanguageInput> + <ContinueButton onClick={onTextInput}></ContinueButton> + </OnHoverDiv> + </MainDiv> + ); +} + +export default StepContainer; |