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
138 lines
3.5 KiB
TypeScript
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
|
|
};
|
|
}
|