| @@ -30,6 +30,7 @@ import { · 29 unmodified lines |
| 30 | 30 | useRef, |
| 31 | 31 | useState, |
| 32 | 32 | } from "react"; |
| 33 | +import { flushSync } from "react-dom"; |
| 33 | 34 | import { useQuery } from "@tanstack/react-query"; |
| 34 | 35 | import { useDebouncedValue } from "@tanstack/react-pacer"; |
| 35 | 36 | import { projectSearchEntriesQueryOptions } from "~/lib/projectReactQuery"; |
| @@ -71,6 +72,7 @@ import { ComposerPendingUserInputPanel } from "./ComposerPendingUserInputPanel"; · 35 unmodified lines |
| 71 | 72 | import { ComposerPlanFollowUpBanner } from "./ComposerPlanFollowUpBanner"; |
| 72 | 73 | import { resolveComposerMenuActiveItemId } from "./composerMenuHighlight"; |
| 73 | 74 | import { searchSlashCommandItems } from "./composerSlashCommandSearch"; |
| 75 | +import { expandMobileComposerForKeyboard } from "./mobileComposerFocus"; |
| 74 | 76 | import { |
| 75 | 77 | getComposerProviderState, |
| 76 | 78 | renderProviderTraitsMenuContent, |
| @@ -1627,25 +1629,42 @@ export const ChatComposer = memo( · 1550 unmodified lines |
| 1627 | 1629 | [blurMobileComposerAfterSend, onSend, shouldBlurMobileComposerOnSubmit], |
| 1628 | 1630 | ); |
| 1629 | 1631 | const expandMobileComposer = useCallback(() => { |
| 1630 | | − if (composerBlurFrameRef.current !== null) { |
| 1631 | | − window.cancelAnimationFrame(composerBlurFrameRef.current); |
| 1632 | | − composerBlurFrameRef.current = null; |
| 1633 | | − } |
| 1634 | | − if (mobileComposerExpandFrameRef.current !== null) { |
| 1635 | | − window.cancelAnimationFrame(mobileComposerExpandFrameRef.current); |
| 1636 | | − } |
| 1637 | | − if (mobileComposerExpandReleaseFrameRef.current !== null) { |
| 1638 | | − window.cancelAnimationFrame(mobileComposerExpandReleaseFrameRef.current); |
| 1639 | | − } |
| 1640 | | − mobileComposerExpandInFlightRef.current = true; |
| 1641 | | − setIsComposerFocused(true); |
| 1642 | | − mobileComposerExpandFrameRef.current = window.requestAnimationFrame(() => { |
| 1643 | | − mobileComposerExpandFrameRef.current = null; |
| 1644 | | − composerEditorRef.current?.focusAtEnd(); |
| 1645 | | − mobileComposerExpandReleaseFrameRef.current = window.requestAnimationFrame(() => { |
| 1646 | | − mobileComposerExpandReleaseFrameRef.current = null; |
| 1647 | | − mobileComposerExpandInFlightRef.current = false; |
| 1648 | | − }); |
| 1632 | + expandMobileComposerForKeyboard({ |
| 1633 | + cancelPendingBlur: () => { |
| 1634 | + if (composerBlurFrameRef.current !== null) { |
| 1635 | + window.cancelAnimationFrame(composerBlurFrameRef.current); |
| 1636 | + composerBlurFrameRef.current = null; |
| 1637 | + } |
| 1638 | + }, |
| 1639 | + cancelPendingExpandFocus: () => { |
| 1640 | + if (mobileComposerExpandFrameRef.current !== null) { |
| 1641 | + window.cancelAnimationFrame(mobileComposerExpandFrameRef.current); |
| 1642 | + mobileComposerExpandFrameRef.current = null; |
| 1643 | + } |
| 1644 | + }, |
| 1645 | + cancelPendingRelease: () => { |
| 1646 | + if (mobileComposerExpandReleaseFrameRef.current !== null) { |
| 1647 | + window.cancelAnimationFrame(mobileComposerExpandReleaseFrameRef.current); |
| 1648 | + mobileComposerExpandReleaseFrameRef.current = null; |
| 1649 | + } |
| 1650 | + }, |
| 1651 | + setExpandInFlight: (inFlight) => { |
| 1652 | + mobileComposerExpandInFlightRef.current = inFlight; |
| 1653 | + }, |
| 1654 | + commitExpandedState: () => { |
| 1655 | + flushSync(() => { |
| 1656 | + setIsComposerFocused(true); |
| 1657 | + }); |
| 1658 | + }, |
| 1659 | + focusEditorAtEnd: () => { |
| 1660 | + composerEditorRef.current?.focusAtEnd(); |
| 1661 | + }, |
| 1662 | + scheduleRelease: () => { |
| 1663 | + mobileComposerExpandReleaseFrameRef.current = window.requestAnimationFrame(() => { |
| 1664 | + mobileComposerExpandReleaseFrameRef.current = null; |
| 1665 | + mobileComposerExpandInFlightRef.current = false; |
| 1666 | + }); |
| 1667 | + }, |
| 1649 | 1668 | }); |
| 1650 | 1669 | }, []); |
| 1651 | 1670 | |
| @@ -2063,7 +2082,10 @@ export const ChatComposer = memo( · 411 unmodified lines |
| 2063 | 2082 | : "text-muted-foreground/60", |
| 2064 | 2083 | !activePendingProgress?.activeQuestion?.multiSelect && "px-3 py-2", |
| 2065 | 2084 | )} |
| 2066 | | − onPointerDown={(event) => event.preventDefault()} |
| 2085 | + onPointerDown={(event) => { |
| 2086 | + event.preventDefault(); |
| 2087 | + expandMobileComposer(); |
| 2088 | + }} |
| 2067 | 2089 | onClick={expandMobileComposer} |
| 2068 | 2090 | aria-label="Write custom answer" |
| 2069 | 2091 | > |
| @@ -2102,7 +2124,10 @@ export const ChatComposer = memo( · 32 unmodified lines |
| 2102 | 2124 | ? "text-foreground" |
| 2103 | 2125 | : "text-muted-foreground/35", |
| 2104 | 2126 | )} |
| 2105 | | − onPointerDown={(event) => event.preventDefault()} |
| 2127 | + onPointerDown={(event) => { |
| 2128 | + event.preventDefault(); |
| 2129 | + expandMobileComposer(); |
| 2130 | + }} |
| 2106 | 2131 | onClick={expandMobileComposer} |
| 2107 | 2132 | aria-label="Expand composer" |
| 2108 | 2133 | > |