fix apply state actions

This commit is contained in:
Dallin Romney
2025-05-12 19:12:59 -07:00
parent e0b38ea746
commit 764356a4be
8 changed files with 52 additions and 63 deletions

View File

@@ -58,14 +58,6 @@ export function ApplyActions(props: ApplyActionsProps) {
);
switch (props.applyState ? props.applyState.status : null) {
case "not-started":
return (
<div className="flex select-none items-center rounded bg-zinc-700 pl-2 pr-1">
<span className="text-lightgray inline-flex w-min items-center gap-2 text-center text-xs">
Pending
</span>
</div>
);
case "streaming":
return (
<div className="flex select-none items-center rounded bg-zinc-700 pl-2 pr-1">

View File

@@ -1,26 +0,0 @@
import Spinner from "../../gui/Spinner";
export interface GeneratingCodeLoaderProps {
showLineCount: boolean;
codeBlockContent: string;
isPending: boolean;
}
export function GeneratingCodeLoader({
showLineCount,
codeBlockContent,
isPending,
}: GeneratingCodeLoaderProps) {
const numLinesCodeBlock = codeBlockContent.split("\n").length;
const linesGeneratedText =
numLinesCodeBlock === 1
? `1 line generated`
: `${numLinesCodeBlock} lines ${isPending ? "pending" : "generated"}`;
return (
<span className="text-lightgray inline-flex items-center gap-2">
{showLineCount ? linesGeneratedText : "Generating"}
{!isPending && <Spinner />}
</span>
);
}

View File

@@ -11,14 +11,15 @@ import { IdeMessengerContext } from "../../../context/IdeMessenger";
import { useIdeMessengerRequest } from "../../../hooks";
import { useWebviewListener } from "../../../hooks/useWebviewListener";
import { useAppSelector } from "../../../redux/hooks";
import { selectCurrentToolCallApplyState } from "../../../redux/selectors/selectCurrentToolCall";
import { selectApplyStateByStreamId } from "../../../redux/slices/sessionSlice";
import { getFontSize } from "../../../util";
import Spinner from "../../gui/Spinner";
import { isTerminalCodeBlock } from "../utils";
import { ApplyActions } from "./ApplyActions";
import { CopyButton } from "./CopyButton";
import { CreateFileButton } from "./CreateFileButton";
import { FileInfo } from "./FileInfo";
import { GeneratingCodeLoader } from "./GeneratingCodeLoader";
import { InsertButton } from "./InsertButton";
import { RunInTerminalButton } from "./RunInTerminalButton";
@@ -49,8 +50,9 @@ export interface StepContainerPreToolbarProps {
codeBlockContent: string;
language: string | null;
relativeFilepath?: string;
isFinalCodeblock: boolean;
itemIndex?: number;
codeBlockIndex: number; // To track which codeblock we are applying
isLastCodeblock: boolean;
codeBlockStreamId: string;
range?: string;
children: any;
@@ -62,8 +64,9 @@ export function StepContainerPreToolbar({
codeBlockContent,
language,
relativeFilepath,
isFinalCodeblock,
itemIndex,
codeBlockIndex,
isLastCodeblock,
codeBlockStreamId,
range,
children,
@@ -71,6 +74,7 @@ export function StepContainerPreToolbar({
disableManualApply,
}: StepContainerPreToolbarProps) {
const ideMessenger = useContext(IdeMessengerContext);
const history = useAppSelector((state) => state.session.history);
const [isExpanded, setIsExpanded] = useState(expanded ?? true);
const [relativeFilepathUri, setRelativeFilepathUri] = useState<string | null>(
@@ -95,6 +99,9 @@ export function StepContainerPreToolbar({
const applyState = useAppSelector((state) =>
selectApplyStateByStreamId(state, codeBlockStreamId),
);
const currentToolCallApplyState = useAppSelector(
selectCurrentToolCallApplyState,
);
/**
* In the case where `relativeFilepath` is defined, this will just be `relativeFilepathUri`.
@@ -110,7 +117,12 @@ export function StepContainerPreToolbar({
relativeFilepath && /\.[0-9a-z]+$/i.test(relativeFilepath);
const isStreaming = useAppSelector((store) => store.session.isStreaming);
const isGeneratingCodeBlock = isFinalCodeblock && isStreaming;
const isLastItem = useMemo(() => {
return itemIndex === history.length - 1;
}, [history.length, itemIndex]);
const isGeneratingCodeBlock = isLastItem && isLastCodeblock && isStreaming;
// If we are creating a file, we already render that in the button
// so we don't want to dispaly it twice here
@@ -221,6 +233,32 @@ export function StepContainerPreToolbar({
}
const renderActionButtons = () => {
const isPendingToolCall =
currentToolCallApplyState &&
currentToolCallApplyState.streamId === applyState?.streamId &&
currentToolCallApplyState.status === "not-started";
if (isGeneratingCodeBlock || isPendingToolCall) {
const numLines = codeBlockContent.split("\n").length;
const plural = numLines === 1 ? "" : "s";
if (isGeneratingCodeBlock) {
return (
<span className="text-lightgray inline-flex w-min items-center gap-2">
{!isExpanded ? `${numLines} line${plural} generated` : "Generating"}{" "}
<div>
<Spinner />
</div>
</span>
);
} else {
return (
<span className="text-lightgray inline-flex w-min items-center gap-2">
{`${numLines} line${plural} pending`}
</span>
);
}
}
if (isTerminalCodeBlock(language, codeBlockContent)) {
return <RunInTerminalButton command={codeBlockContent} />;
}
@@ -281,17 +319,7 @@ export function StepContainerPreToolbar({
</div>
)}
{isGeneratingCodeBlock || applyState?.status === "not-started" ? (
<GeneratingCodeLoader
showLineCount={!isExpanded}
codeBlockContent={codeBlockContent}
isPending={
applyState?.status === "not-started" && !isGeneratingCodeBlock
}
/>
) : (
renderActionButtons()
)}
{renderActionButtons()}
</div>
</ToolbarDiv>

View File

@@ -193,11 +193,7 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview(
};
}, [props.itemIndex, history, allSymbols]);
const pastFileInfoRef = useUpdatingRef(pastFileInfo);
const isLastItem = useMemo(() => {
return props.itemIndex === history.length - 1;
}, [history.length, props.itemIndex]);
const isLastItemRef = useUpdatingRef(isLastItem);
const itemIndexRef = useUpdatingRef(props.itemIndex);
const codeblockStreamIds = useRef<string[]>([]);
useEffect(() => {
@@ -296,8 +292,7 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview(
const language = getLanguageFromClassName(className);
const isFinalCodeblock =
preChildProps["data-islastcodeblock"] && isLastItemRef.current;
const isLastCodeblock = preChildProps["data-islastcodeblock"];
if (codeblockStreamIds.current[codeBlockIndex] === undefined) {
codeblockStreamIds.current[codeBlockIndex] =
@@ -307,10 +302,11 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview(
return (
<StepContainerPreToolbar
codeBlockContent={codeBlockContent}
itemIndex={itemIndexRef.current}
codeBlockIndex={codeBlockIndex}
language={language}
relativeFilepath={relativeFilePath}
isFinalCodeblock={isFinalCodeblock}
isLastCodeblock={isLastCodeblock}
range={range}
codeBlockStreamId={codeblockStreamIds.current[codeBlockIndex]}
expanded={props.expandCodeblocks}

View File

@@ -57,11 +57,11 @@ export function LumpToolbar() {
if (metaKey && event.key === "Enter") {
event.preventDefault();
event.stopPropagation();
dispatch(callCurrentTool());
void dispatch(callCurrentTool());
} else if ((jetbrains ? altKey : metaKey) && event.key === "Backspace") {
event.preventDefault();
event.stopPropagation();
dispatch(cancelCurrentToolCall());
void dispatch(cancelCurrentToolCall());
}
}
};

View File

@@ -19,7 +19,7 @@ export function EditFile(props: EditToolCallProps) {
const src = `\`\`\`${getMarkdownLanguageTagForFile(props.relativeFilePath ?? "test.txt")} ${props.relativeFilePath}\n${props.changes ?? ""}\n\`\`\``;
const dispatch = useAppDispatch();
const isStreaming = useAppSelector((state) => state.session.isStreaming);
const applyState = useAppSelector((state) =>
selectApplyStateByToolCallId(state, props.toolCallId),
);

View File

@@ -27,8 +27,8 @@ function FunctionSpecificToolCallDiv({
case BuiltInToolNames.EditExistingFile:
return (
<EditFile
relativeFilePath={args.filepath}
changes={args.changes}
relativeFilePath={args.filepath ?? ""}
changes={args.changes ?? ""}
toolCallId={toolCall.id}
historyIndex={historyIndex}
/>

View File

@@ -63,7 +63,6 @@ export const streamNormalInput = createAsyncThunk<
dispatch(abortStream());
break;
}
dispatch(streamUpdate(next.value));
next = await gen.next();
}