mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site/src/pages/AgentsPage): preserve chat input typed while loading (#26415)
This commit is contained in:
@@ -324,6 +324,9 @@ describe("useConversationEditingState", () => {
|
||||
);
|
||||
});
|
||||
expect(localStorage.getItem(expectedKey)).toBe("work in progress");
|
||||
// handleContentChange persists only; it must not advance the seed.
|
||||
expect(result.current.editorInitialValue).toBe("");
|
||||
expect(result.current.initialEditorState).toBeUndefined();
|
||||
|
||||
act(() => {
|
||||
// Even though the serialized state is non-empty (Lexical always
|
||||
@@ -336,6 +339,27 @@ describe("useConversationEditingState", () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("carries a draft typed during loading into the seed for the loaded editor", () => {
|
||||
const { result, unmount } = renderEditing();
|
||||
|
||||
// handleContentChange persists but does not advance the seed.
|
||||
const editorState =
|
||||
'{"root":{"children":[{"text":"typed while loading"}]}}';
|
||||
act(() => {
|
||||
result.current.handleLoadingDraftChange(
|
||||
"typed while loading",
|
||||
editorState,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
expect(localStorage.getItem(expectedKey)).toBe(editorState);
|
||||
expect(result.current.editorInitialValue).toBe("typed while loading");
|
||||
expect(result.current.initialEditorState).toBe(editorState);
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("loads edit text into the composer and restores the prior draft on cancel without refocusing", () => {
|
||||
const { result, unmount } = renderEditing();
|
||||
|
||||
|
||||
@@ -569,6 +569,21 @@ export function useConversationEditingState(deps: {
|
||||
}
|
||||
};
|
||||
|
||||
// Separate from handleContentChange, which avoids setState to prevent
|
||||
// per-keystroke re-renders. The loading editor is a different instance
|
||||
// that unmounts on load, so the seed must advance here.
|
||||
const handleLoadingDraftChange = (
|
||||
content: string,
|
||||
serializedEditorState: string,
|
||||
hasFileReferences: boolean,
|
||||
) => {
|
||||
handleContentChange(content, serializedEditorState, hasFileReferences);
|
||||
setDraftState({
|
||||
editorInitialValue: content,
|
||||
initialEditorState: serializedEditorState,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
inputValueRef,
|
||||
chatInputRef,
|
||||
@@ -584,6 +599,7 @@ export function useConversationEditingState(deps: {
|
||||
handleCancelQueueEdit,
|
||||
handleSendFromInput,
|
||||
handleContentChange,
|
||||
handleLoadingDraftChange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1548,6 +1564,11 @@ const AgentChatPage: FC = () => {
|
||||
preferencesQuery.isLoading,
|
||||
)}
|
||||
titleElement={titleElement}
|
||||
inputRef={editing.chatInputRef}
|
||||
initialValue={editing.editorInitialValue}
|
||||
initialEditorState={editing.initialEditorState}
|
||||
remountKey={editing.remountKey}
|
||||
onContentChange={editing.handleLoadingDraftChange}
|
||||
isInputDisabled={isInputDisabled}
|
||||
effectiveSelectedModel={effectiveSelectedModel}
|
||||
setSelectedModel={setSelectedModel}
|
||||
|
||||
@@ -748,6 +748,11 @@ export const Loading: Story = {
|
||||
<AgentChatPageLoadingView
|
||||
sendShortcut="enter"
|
||||
titleElement={<title>Loading — Agents</title>}
|
||||
inputRef={{ current: null }}
|
||||
initialValue=""
|
||||
initialEditorState={undefined}
|
||||
remountKey={0}
|
||||
onContentChange={fn()}
|
||||
isInputDisabled
|
||||
effectiveSelectedModel={defaultModelConfigID}
|
||||
setSelectedModel={fn()}
|
||||
@@ -767,6 +772,11 @@ export const LoadingWithModelOptions: Story = {
|
||||
<AgentChatPageLoadingView
|
||||
sendShortcut="enter"
|
||||
titleElement={<title>Loading — Agents</title>}
|
||||
inputRef={{ current: null }}
|
||||
initialValue=""
|
||||
initialEditorState={undefined}
|
||||
remountKey={0}
|
||||
onContentChange={fn()}
|
||||
isInputDisabled={false}
|
||||
effectiveSelectedModel={defaultModelConfigID}
|
||||
setSelectedModel={fn()}
|
||||
@@ -785,6 +795,11 @@ export const LoadingWithRightPanel: Story = {
|
||||
<AgentChatPageLoadingView
|
||||
sendShortcut="enter"
|
||||
titleElement={<title>Loading — Agents</title>}
|
||||
inputRef={{ current: null }}
|
||||
initialValue=""
|
||||
initialEditorState={undefined}
|
||||
remountKey={0}
|
||||
onContentChange={fn()}
|
||||
isInputDisabled
|
||||
effectiveSelectedModel={defaultModelConfigID}
|
||||
setSelectedModel={fn()}
|
||||
@@ -804,6 +819,11 @@ export const LoadingSidebarCollapsed: Story = {
|
||||
<AgentChatPageLoadingView
|
||||
sendShortcut="enter"
|
||||
titleElement={<title>Loading — Agents</title>}
|
||||
inputRef={{ current: null }}
|
||||
initialValue=""
|
||||
initialEditorState={undefined}
|
||||
remountKey={0}
|
||||
onContentChange={fn()}
|
||||
isInputDisabled
|
||||
effectiveSelectedModel={defaultModelConfigID}
|
||||
setSelectedModel={fn()}
|
||||
|
||||
@@ -1016,6 +1016,15 @@ export const AgentChatPageView: FC<AgentChatPageViewProps> = ({
|
||||
interface AgentChatPageLoadingViewProps {
|
||||
sendShortcut: AgentChatSendShortcut;
|
||||
titleElement: React.ReactNode;
|
||||
inputRef: RefObject<ChatMessageInputRef | null>;
|
||||
initialValue: string;
|
||||
initialEditorState: string | undefined;
|
||||
remountKey: number;
|
||||
onContentChange: (
|
||||
content: string,
|
||||
serializedEditorState: string,
|
||||
hasFileReferences: boolean,
|
||||
) => void;
|
||||
isInputDisabled: boolean;
|
||||
effectiveSelectedModel: string;
|
||||
setSelectedModel: (model: string) => void;
|
||||
@@ -1033,6 +1042,11 @@ interface AgentChatPageLoadingViewProps {
|
||||
export const AgentChatPageLoadingView: FC<AgentChatPageLoadingViewProps> = ({
|
||||
sendShortcut,
|
||||
titleElement,
|
||||
inputRef,
|
||||
initialValue,
|
||||
initialEditorState,
|
||||
remountKey,
|
||||
onContentChange,
|
||||
isInputDisabled,
|
||||
effectiveSelectedModel,
|
||||
setSelectedModel,
|
||||
@@ -1085,7 +1099,11 @@ export const AgentChatPageLoadingView: FC<AgentChatPageLoadingViewProps> = ({
|
||||
<AgentChatInput
|
||||
onSend={() => {}}
|
||||
sendShortcut={sendShortcut}
|
||||
initialValue=""
|
||||
inputRef={inputRef}
|
||||
initialValue={initialValue}
|
||||
initialEditorState={initialEditorState}
|
||||
remountKey={remountKey}
|
||||
onContentChange={onContentChange}
|
||||
isDisabled={isInputDisabled}
|
||||
isLoading={false}
|
||||
selectedModel={effectiveSelectedModel}
|
||||
|
||||
Reference in New Issue
Block a user