History

Stabilize composer editor callbacks

Cursor Agent committed 6 months ago

Commit 14ff38c1 parent 159f13c1 file · +43 −31Browse files

Unified · Split

apps/web/src/components/chat/ChatComposer.tsx+43−31
@@ -1635,43 +1635,49 @@ export const ChatComposer = memo( · 1634 unmodified lines
16351635 // ------------------------------------------------------------------
16361636 // Callbacks: command key
16371637 // ------------------------------------------------------------------
1638− const onComposerCommandKey = (
1639− key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab",
1640− event: KeyboardEvent,
1641− ) => {
1642− if (key === "Tab" && event.shiftKey) {
1643− toggleInteractionMode();
1644− return true;
1645− }
1646− const { trigger } = resolveActiveComposerTrigger();
1647− const menuIsActive = composerMenuOpenRef.current || trigger !== null;
1648− if (menuIsActive) {
1649− const currentItems = composerMenuItemsRef.current;
1650− const selectedItem = activeComposerMenuItemRef.current ?? currentItems[0];
1651− if (key === "ArrowDown" && currentItems.length > 0) {
1652− nudgeComposerMenuHighlight("ArrowDown");
1638+ const onComposerCommandKey = useCallback(
1639+ (key: "ArrowDown" | "ArrowUp" | "Enter" | "Tab", event: KeyboardEvent) => {
1640+ if (key === "Tab" && event.shiftKey) {
1641+ toggleInteractionMode();
16531642 return true;
16541643 }
1655− if (key === "ArrowUp" && currentItems.length > 0) {
1656− nudgeComposerMenuHighlight("ArrowUp");
1657− return true;
1644+ const { trigger } = resolveActiveComposerTrigger();
1645+ const menuIsActive = composerMenuOpenRef.current || trigger !== null;
1646+ if (menuIsActive) {
1647+ const currentItems = composerMenuItemsRef.current;
1648+ const selectedItem = activeComposerMenuItemRef.current ?? currentItems[0];
1649+ if (key === "ArrowDown" && currentItems.length > 0) {
1650+ nudgeComposerMenuHighlight("ArrowDown");
1651+ return true;
1652+ }
1653+ if (key === "ArrowUp" && currentItems.length > 0) {
1654+ nudgeComposerMenuHighlight("ArrowUp");
1655+ return true;
1656+ }
1657+ if ((key === "Enter" || key === "Tab") && selectedItem) {
1658+ onSelectComposerItem(selectedItem);
1659+ return true;
1660+ }
16581661 }
1659− if ((key === "Enter" || key === "Tab") && selectedItem) {
1660− onSelectComposerItem(selectedItem);
1662+ if (key === "Enter" && !event.shiftKey) {
1663+ submitComposer();
16611664 return true;
16621665 }
1663− }
1664− if (key === "Enter" && !event.shiftKey) {
1665− submitComposer();
1666− return true;
1667− }
1668− return false;
1669− };
1666+ return false;
1667+ },
1668+ [
1669+ nudgeComposerMenuHighlight,
1670+ onSelectComposerItem,
1671+ resolveActiveComposerTrigger,
1672+ submitComposer,
1673+ toggleInteractionMode,
1674+ ],
1675+ );
16701676
16711677 // ------------------------------------------------------------------
16721678 // Callbacks: images
16731679 // ------------------------------------------------------------------
1674− const addComposerImages = (files: File[]) => {
1680+ const addComposerImages = useCallback((files: File[]) => {
16751681 if (!activeThreadId || files.length === 0) return;
16761682 if (pendingUserInputs.length > 0) {
16771683 toastManager.add({
@@ -1714,7 +1720,13 @@ export const ChatComposer = memo( · 36 unmodified lines
17141720 addComposerImagesToDraft(nextImages);
17151721 }
17161722 setThreadError(activeThreadId, error);
1717− };
1723+ }, [
1724+ activeThreadId,
1725+ addComposerImage,
1726+ addComposerImagesToDraft,
1727+ pendingUserInputs.length,
1728+ setThreadError,
1729+ ]);
17181730
17191731 const removeComposerImage = (imageId: string) => {
17201732 removeComposerImageFromDraft(imageId);
@@ -1723,14 +1735,14 @@ export const ChatComposer = memo( · 2 unmodified lines
17231735 // ------------------------------------------------------------------
17241736 // Callbacks: paste / drag
17251737 // ------------------------------------------------------------------
1726− const onComposerPaste = (event: React.ClipboardEvent<HTMLElement>) => {
1738+ const onComposerPaste = useCallback((event: React.ClipboardEvent<HTMLElement>) => {
17271739 const files = Array.from(event.clipboardData.files);
17281740 if (files.length === 0) return;
17291741 const imageFiles = files.filter((file) => file.type.startsWith("image/"));
17301742 if (imageFiles.length === 0) return;
17311743 event.preventDefault();
17321744 addComposerImages(imageFiles);
1733− };
1745+ }, [addComposerImages]);
17341746
17351747 const onComposerDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
17361748 if (!event.dataTransfer.types.includes("Files")) return;