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