mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-07 01:48:53 +00:00
fix(webui): preserve session drag contracts
This commit is contained in:
parent
f44a766f98
commit
9af6bb91c7
@ -221,7 +221,11 @@ export const ChatList = memo(function ChatList({
|
|||||||
const targetIndex = reordered.indexOf(targetKey);
|
const targetIndex = reordered.indexOf(targetKey);
|
||||||
if (targetIndex < 0) return;
|
if (targetIndex < 0) return;
|
||||||
reordered.splice(targetIndex + (edge === "after" ? 1 : 0), 0, draggedSessionKey);
|
reordered.splice(targetIndex + (edge === "after" ? 1 : 0), 0, draggedSessionKey);
|
||||||
onReorderSessions(reordered);
|
const groupedKeys = new Set(keys);
|
||||||
|
onReorderSessions([
|
||||||
|
...reordered,
|
||||||
|
...sessionOrder.filter((key) => !groupedKeys.has(key)),
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1730,9 +1730,8 @@ export function ThreadComposer({
|
|||||||
|
|
||||||
const previewSessionDrop = useCallback((event: React.DragEvent) => {
|
const previewSessionDrop = useCallback((event: React.DragEvent) => {
|
||||||
if (!hasDraggedSession(event.dataTransfer)) return false;
|
if (!hasDraggedSession(event.dataTransfer)) return false;
|
||||||
event.preventDefault();
|
|
||||||
event.dataTransfer.dropEffect = "copy";
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
|
event.dataTransfer.dropEffect = "none";
|
||||||
setSessionDragPreview(null);
|
setSessionDragPreview(null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1744,9 +1743,12 @@ export function ThreadComposer({
|
|||||||
(candidate) => candidate.session_key === mention.session_key,
|
(candidate) => candidate.session_key === mention.session_key,
|
||||||
);
|
);
|
||||||
if (!mention || (!alreadySelected && activeSessionMentions.length >= SESSION_MENTIONS_LIMIT)) {
|
if (!mention || (!alreadySelected && activeSessionMentions.length >= SESSION_MENTIONS_LIMIT)) {
|
||||||
|
event.dataTransfer.dropEffect = "none";
|
||||||
setSessionDragPreview(null);
|
setSessionDragPreview(null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
event.dataTransfer.dropEffect = "copy";
|
||||||
const start = textareaRef.current?.selectionStart ?? value.length;
|
const start = textareaRef.current?.selectionStart ?? value.length;
|
||||||
const end = textareaRef.current?.selectionEnd ?? start;
|
const end = textareaRef.current?.selectionEnd ?? start;
|
||||||
setSessionDragPreview((current) => (
|
setSessionDragPreview((current) => (
|
||||||
|
|||||||
@ -88,6 +88,8 @@ describe("ChatList", () => {
|
|||||||
session({ chatId: "alpha", title: "Alpha" }),
|
session({ chatId: "alpha", title: "Alpha" }),
|
||||||
session({ chatId: "bravo", title: "Bravo" }),
|
session({ chatId: "bravo", title: "Bravo" }),
|
||||||
session({ chatId: "charlie", title: "Charlie" }),
|
session({ chatId: "charlie", title: "Charlie" }),
|
||||||
|
session({ chatId: "old-a", title: "Old A" }),
|
||||||
|
session({ chatId: "old-b", title: "Old B" }),
|
||||||
];
|
];
|
||||||
const { rerender } = render(
|
const { rerender } = render(
|
||||||
<ChatList
|
<ChatList
|
||||||
@ -99,6 +101,8 @@ describe("ChatList", () => {
|
|||||||
onRequestRename={vi.fn()}
|
onRequestRename={vi.fn()}
|
||||||
onToggleArchive={vi.fn()}
|
onToggleArchive={vi.fn()}
|
||||||
onReorderSessions={onReorderSessions}
|
onReorderSessions={onReorderSessions}
|
||||||
|
archivedKeys={["websocket:old-a", "websocket:old-b"]}
|
||||||
|
sessionOrder={sessions.map((item) => item.key)}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
const dataTransfer = {
|
const dataTransfer = {
|
||||||
@ -117,6 +121,8 @@ describe("ChatList", () => {
|
|||||||
"websocket:bravo",
|
"websocket:bravo",
|
||||||
"websocket:charlie",
|
"websocket:charlie",
|
||||||
"websocket:alpha",
|
"websocket:alpha",
|
||||||
|
"websocket:old-a",
|
||||||
|
"websocket:old-b",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
rerender(
|
rerender(
|
||||||
@ -129,10 +135,13 @@ describe("ChatList", () => {
|
|||||||
onRequestRename={vi.fn()}
|
onRequestRename={vi.fn()}
|
||||||
onToggleArchive={vi.fn()}
|
onToggleArchive={vi.fn()}
|
||||||
onReorderSessions={onReorderSessions}
|
onReorderSessions={onReorderSessions}
|
||||||
|
archivedKeys={["websocket:old-a", "websocket:old-b"]}
|
||||||
sessionOrder={[
|
sessionOrder={[
|
||||||
"websocket:bravo",
|
"websocket:bravo",
|
||||||
"websocket:charlie",
|
"websocket:charlie",
|
||||||
"websocket:alpha",
|
"websocket:alpha",
|
||||||
|
"websocket:old-a",
|
||||||
|
"websocket:old-b",
|
||||||
]}
|
]}
|
||||||
sort="manual"
|
sort="manual"
|
||||||
/>,
|
/>,
|
||||||
|
|||||||
@ -1647,6 +1647,28 @@ describe("ThreadComposer", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("rejects session drops that are unavailable to the composer", () => {
|
||||||
|
render(
|
||||||
|
<ThreadComposer
|
||||||
|
onSend={vi.fn()}
|
||||||
|
placeholder="Type your message..."
|
||||||
|
sessions={[]}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
const input = screen.getByLabelText("Message input");
|
||||||
|
const dataTransfer = {
|
||||||
|
types: [SESSION_DRAG_TYPE],
|
||||||
|
effectAllowed: "copyMove",
|
||||||
|
dropEffect: "copy",
|
||||||
|
files: [],
|
||||||
|
getData: () => "websocket:current",
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(fireEvent.dragEnter(input, { dataTransfer })).toBe(true);
|
||||||
|
expect(fireEvent.dragOver(input, { dataTransfer })).toBe(true);
|
||||||
|
expect(screen.queryByTestId("composer-session-drag-preview")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("disambiguates duplicate and capability-colliding session names", () => {
|
it("disambiguates duplicate and capability-colliding session names", () => {
|
||||||
render(
|
render(
|
||||||
<ThreadComposer
|
<ThreadComposer
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user