From b6a930eff045757db9c6cf12d46adbc8d46c2e7f Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 23 Jul 2026 18:03:21 -0700 Subject: [PATCH] fix(dashboard): commit planning comments on pointerdown before keyboard blur On phone/tablet the first tap of Add/Submit blurred the composer, closed the keyboard, and shifted the fixed panel so the click was lost. Commit the action on pointerdown (same pattern as Refine Apply) so one tap is enough. --- .../app/components/PlanningModeModal.tsx | 57 +++++++++++++++++-- ...PlanningModeModal.ui-interactions.test.tsx | 3 + 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/packages/dashboard/app/components/PlanningModeModal.tsx b/packages/dashboard/app/components/PlanningModeModal.tsx index 46e6fd3c6a..c5777a33f4 100644 --- a/packages/dashboard/app/components/PlanningModeModal.tsx +++ b/packages/dashboard/app/components/PlanningModeModal.tsx @@ -2876,6 +2876,20 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat window.getSelection()?.removeAllRanges(); }, [commentDraft, selectedPlanQuote, setCommentEditorOpen]); + const handleAddContextualCommentPointerDown = useCallback((event: ReactPointerEvent) => { + /* + FNXC:PlanningComments 2026-07-24-06:15: + Same keyboard-dismiss race as Refine Apply: on phone/tablet the first tap blurs the + suggestion field, the soft keyboard closes, the fixed composer shifts, and the trailing + click never lands — operators had to tap Add/Submit a second time. Commit on pointerdown + before blur; the empty-draft guard makes a compatibility click a no-op. + */ + if (viewportMode === "desktop" || event.pointerType === "mouse") return; + if (!selectedPlanQuote || !commentDraft.trim()) return; + event.preventDefault(); + handleAddContextualComment(); + }, [commentDraft, handleAddContextualComment, selectedPlanQuote, viewportMode]); + const handleSubmitContextualComments = useCallback(async () => { const sessionId = currentSessionIdRef.current; const summary = runningSummaryRef.current; @@ -2931,22 +2945,32 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat }, [projectId, t, workflowId, workspaceQuestion]); const handleMobileKeyboardActionPointerDown = useCallback((event: ReactPointerEvent) => { - if (viewportMode !== "mobile" || event.pointerType === "mouse") return; + if (viewportMode === "desktop" || event.pointerType === "mouse") return; event.preventDefault(); }, [viewportMode]); const handleApplyRefinementPointerDown = useCallback((event: ReactPointerEvent) => { - if (viewportMode !== "mobile" || event.pointerType === "mouse") return; + if (viewportMode === "desktop" || event.pointerType === "mouse") return; /* FNXC:PlanningModeMobile 2026-07-21-00:50: Preventing the touch pointer default suppresses the browser's compatibility click. Apply before the keyboard resize can move/remove the popup button; the single-flight guard above makes a browser that still emits click harmless. + + FNXC:PlanningComments 2026-07-24-06:15: Same race applies on tablet while the comment/refine + composers are keyboard-backed, so this is no longer phone-only. */ event.preventDefault(); void handleRefineFromPlan(); }, [handleRefineFromPlan, viewportMode]); + const handleSubmitContextualCommentsPointerDown = useCallback((event: ReactPointerEvent) => { + if (viewportMode === "desktop" || event.pointerType === "mouse") return; + if (contextualComments.length === 0 || contextualCommentInFlightRef.current) return; + event.preventDefault(); + void handleSubmitContextualComments(); + }, [contextualComments.length, handleSubmitContextualComments, viewportMode]); + const handleRetryCreateTask = useCallback(async () => { if (view.type !== "create_retry" || validateCreateInFlightRef.current) return; validateCreateInFlightRef.current = true; @@ -3184,8 +3208,23 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat