Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

138 lines
3.5 KiB
TypeScript

import { useCallback, useState, type KeyboardEvent } from "react";
import type { AgentMessageRowVM } from "../contracts/agentMessageRowVM";
import type { AgentUserMessageEditRetryControl } from "./AgentUserMessageEditRetry";
interface AgentUserMessageEditState {
draft: string;
originalText: string;
rowId: string;
}
export function useAgentUserMessageEditRetry(input: {
editRetry?: AgentUserMessageEditRetryControl;
isUser: boolean;
row: AgentMessageRowVM;
}) {
const rawFirstTextBlock =
input.isUser && input.row.rawFirstTextBlock !== undefined
? input.row.rawFirstTextBlock
: null;
const editableTargetMessageId =
rawFirstTextBlock !== null
? (input.row.messages.find(
(message) =>
message.contentKind === "text" || message.contentKind === undefined
)?.id ?? null)
: null;
const canEdit =
input.isUser &&
input.editRetry !== undefined &&
rawFirstTextBlock !== null &&
editableTargetMessageId !== null;
const [editState, setEditState] = useState<AgentUserMessageEditState | null>(
null
);
const [locallySubmittingRowId, setLocallySubmittingRowId] = useState<
string | null
>(null);
const isEditing =
canEdit &&
editState?.rowId === input.row.id &&
editState.originalText === rawFirstTextBlock;
const editPending =
input.editRetry?.pending === true ||
locallySubmittingRowId === input.row.id;
const handleStartEdit = useCallback(() => {
if (!canEdit || editPending || rawFirstTextBlock === null) {
return;
}
setEditState({
draft: rawFirstTextBlock,
originalText: rawFirstTextBlock,
rowId: input.row.id
});
}, [canEdit, editPending, input.row.id, rawFirstTextBlock]);
const handleCancelEdit = useCallback(() => {
if (!editPending) {
setEditState(null);
}
}, [editPending]);
const handleEditTextChange = useCallback(
(draft: string) => {
setEditState((current) =>
current?.rowId === input.row.id ? { ...current, draft } : current
);
},
[input.row.id]
);
const handleSubmitEdit = useCallback(async () => {
if (
!input.editRetry ||
!isEditing ||
editPending ||
!editState?.draft.trim()
) {
return;
}
setLocallySubmittingRowId(input.row.id);
try {
const succeeded = await input.editRetry.onSubmit({
turnId: input.row.turnId,
editedText: editState.draft
});
if (succeeded) {
setEditState(null);
}
} finally {
setLocallySubmittingRowId((current) =>
current === input.row.id ? null : current
);
}
}, [
editPending,
editState,
input.editRetry,
input.row.id,
input.row.turnId,
isEditing
]);
const handleEditorKeyDown = useCallback(
(event: KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === "Escape") {
event.preventDefault();
event.stopPropagation();
handleCancelEdit();
return;
}
if (event.key !== "Enter") {
return;
}
event.stopPropagation();
if (event.shiftKey || event.nativeEvent.isComposing) {
return;
}
event.preventDefault();
void handleSubmitEdit();
},
[handleCancelEdit, handleSubmitEdit]
);
return {
canEdit,
editableTargetMessageId,
editPending,
editState,
handleCancelEdit,
handleEditorKeyDown,
handleEditTextChange,
handleStartEdit,
handleSubmitEdit,
isEditing
};
}