Unified · Split
| @@ -1635,43 +1635,49 @@ export const ChatComposer = memo( · 1634 unmodified lines | ||
| 1635 | 1635 | // ------------------------------------------------------------------ |
| 1636 | 1636 | // Callbacks: command key |
| 1637 | 1637 | // ------------------------------------------------------------------ |
| 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(); | |
| 1653 | 1642 | return true; |
| 1654 | 1643 | } |
| 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 | + } | |
| 1658 | 1661 | } |
| 1659 | − if ((key === "Enter" || key === "Tab") && selectedItem) { | |
| 1660 | − onSelectComposerItem(selectedItem); | |
| 1662 | + if (key === "Enter" && !event.shiftKey) { | |
| 1663 | + submitComposer(); | |
| 1661 | 1664 | return true; |
| 1662 | 1665 | } |
| 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 | + ); | |
| 1670 | 1676 | |
| 1671 | 1677 | // ------------------------------------------------------------------ |
| 1672 | 1678 | // Callbacks: images |
| 1673 | 1679 | // ------------------------------------------------------------------ |
| 1674 | − const addComposerImages = (files: File[]) => { | |
| 1680 | + const addComposerImages = useCallback((files: File[]) => { | |
| 1675 | 1681 | if (!activeThreadId || files.length === 0) return; |
| 1676 | 1682 | if (pendingUserInputs.length > 0) { |
| 1677 | 1683 | toastManager.add({ |
| @@ -1714,7 +1720,13 @@ export const ChatComposer = memo( · 36 unmodified lines | ||
| 1714 | 1720 | addComposerImagesToDraft(nextImages); |
| 1715 | 1721 | } |
| 1716 | 1722 | setThreadError(activeThreadId, error); |
| 1717 | − }; | |
| 1723 | + }, [ | |
| 1724 | + activeThreadId, | |
| 1725 | + addComposerImage, | |
| 1726 | + addComposerImagesToDraft, | |
| 1727 | + pendingUserInputs.length, | |
| 1728 | + setThreadError, | |
| 1729 | + ]); | |
| 1718 | 1730 | |
| 1719 | 1731 | const removeComposerImage = (imageId: string) => { |
| 1720 | 1732 | removeComposerImageFromDraft(imageId); |
| @@ -1723,14 +1735,14 @@ export const ChatComposer = memo( · 2 unmodified lines | ||
| 1723 | 1735 | // ------------------------------------------------------------------ |
| 1724 | 1736 | // Callbacks: paste / drag |
| 1725 | 1737 | // ------------------------------------------------------------------ |
| 1726 | − const onComposerPaste = (event: React.ClipboardEvent<HTMLElement>) => { | |
| 1738 | + const onComposerPaste = useCallback((event: React.ClipboardEvent<HTMLElement>) => { | |
| 1727 | 1739 | const files = Array.from(event.clipboardData.files); |
| 1728 | 1740 | if (files.length === 0) return; |
| 1729 | 1741 | const imageFiles = files.filter((file) => file.type.startsWith("image/")); |
| 1730 | 1742 | if (imageFiles.length === 0) return; |
| 1731 | 1743 | event.preventDefault(); |
| 1732 | 1744 | addComposerImages(imageFiles); |
| 1733 | − }; | |
| 1745 | + }, [addComposerImages]); | |
| 1734 | 1746 | |
| 1735 | 1747 | const onComposerDragEnter = (event: React.DragEvent<HTMLDivElement>) => { |
| 1736 | 1748 | if (!event.dataTransfer.types.includes("Files")) return; |