mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 13:47:19 +08:00
1. Add support for rejection action on frontend 2. Show users the reason for rejection 3. Get rid of weird empty box after delegation 4. On web GUI, show customer when a delegation starts and ends
145 lines
4.1 KiB
TypeScript
145 lines
4.1 KiB
TypeScript
import { Tooltip } from "@nextui-org/react";
|
|
import React, { useEffect } from "react";
|
|
import { useSelector } from "react-redux";
|
|
import ArrowIcon from "#/assets/arrow";
|
|
import PauseIcon from "#/assets/pause";
|
|
import PlayIcon from "#/assets/play";
|
|
import { changeAgentState } from "#/services/agentStateService";
|
|
import store, { RootState } from "#/store";
|
|
import AgentState from "#/types/AgentState";
|
|
import { clearMessages } from "#/state/chatSlice";
|
|
|
|
const IgnoreTaskStateMap: { [k: string]: AgentState[] } = {
|
|
[AgentState.PAUSED]: [
|
|
AgentState.INIT,
|
|
AgentState.PAUSED,
|
|
AgentState.STOPPED,
|
|
AgentState.FINISHED,
|
|
AgentState.REJECTED,
|
|
AgentState.AWAITING_USER_INPUT,
|
|
],
|
|
[AgentState.RUNNING]: [
|
|
AgentState.INIT,
|
|
AgentState.RUNNING,
|
|
AgentState.STOPPED,
|
|
AgentState.FINISHED,
|
|
AgentState.REJECTED,
|
|
AgentState.AWAITING_USER_INPUT,
|
|
],
|
|
[AgentState.STOPPED]: [AgentState.INIT, AgentState.STOPPED],
|
|
};
|
|
|
|
interface ButtonProps {
|
|
isDisabled: boolean;
|
|
content: string;
|
|
action: AgentState;
|
|
handleAction: (action: AgentState) => void;
|
|
large?: boolean;
|
|
}
|
|
|
|
function ActionButton({
|
|
isDisabled = false,
|
|
content,
|
|
action,
|
|
handleAction,
|
|
children,
|
|
large = false,
|
|
}: React.PropsWithChildren<ButtonProps>): React.ReactNode {
|
|
return (
|
|
<Tooltip content={content} closeDelay={100}>
|
|
<button
|
|
onClick={() => handleAction(action)}
|
|
disabled={isDisabled}
|
|
className={`
|
|
relative overflow-visible cursor-default hover:cursor-pointer group
|
|
disabled:cursor-not-allowed disabled:opacity-60
|
|
${large ? "rounded-full bg-neutral-800 p-3" : ""}
|
|
transition-all duration-300 ease-in-out
|
|
`}
|
|
type="button"
|
|
>
|
|
<span className="relative z-10 group-hover:filter group-hover:drop-shadow-[0_0_5px_rgba(255,64,0,0.4)]">
|
|
{children}
|
|
</span>
|
|
<span className="absolute -inset-[5px] border-2 border-red-400/40 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300 ease-in-out" />
|
|
</button>
|
|
</Tooltip>
|
|
);
|
|
}
|
|
|
|
function AgentControlBar() {
|
|
const { curAgentState } = useSelector((state: RootState) => state.agent);
|
|
const [desiredState, setDesiredState] = React.useState(AgentState.INIT);
|
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
|
|
const handleAction = (action: AgentState) => {
|
|
if (IgnoreTaskStateMap[action].includes(curAgentState)) {
|
|
return;
|
|
}
|
|
|
|
if (action === AgentState.STOPPED) {
|
|
store.dispatch(clearMessages());
|
|
} else {
|
|
setIsLoading(true);
|
|
}
|
|
|
|
setDesiredState(action);
|
|
changeAgentState(action);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (curAgentState === desiredState) {
|
|
if (curAgentState === AgentState.STOPPED) {
|
|
store.dispatch(clearMessages());
|
|
}
|
|
setIsLoading(false);
|
|
} else if (curAgentState === AgentState.RUNNING) {
|
|
setDesiredState(AgentState.RUNNING);
|
|
}
|
|
// We only want to run this effect when curAgentState changes
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [curAgentState]);
|
|
|
|
return (
|
|
<div className="flex items-center gap-3">
|
|
{curAgentState === AgentState.PAUSED ? (
|
|
<ActionButton
|
|
isDisabled={
|
|
isLoading ||
|
|
IgnoreTaskStateMap[AgentState.RUNNING].includes(curAgentState)
|
|
}
|
|
content="Resume the agent task"
|
|
action={AgentState.RUNNING}
|
|
handleAction={handleAction}
|
|
large
|
|
>
|
|
<PlayIcon />
|
|
</ActionButton>
|
|
) : (
|
|
<ActionButton
|
|
isDisabled={
|
|
isLoading ||
|
|
IgnoreTaskStateMap[AgentState.PAUSED].includes(curAgentState)
|
|
}
|
|
content="Pause the current task"
|
|
action={AgentState.PAUSED}
|
|
handleAction={handleAction}
|
|
large
|
|
>
|
|
<PauseIcon />
|
|
</ActionButton>
|
|
)}
|
|
<ActionButton
|
|
isDisabled={isLoading}
|
|
content="Start a new task"
|
|
action={AgentState.STOPPED}
|
|
handleAction={handleAction}
|
|
>
|
|
<ArrowIcon />
|
|
</ActionButton>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default AgentControlBar;
|