feat(FN-3022): merge fusion/fn-3022
Commits merged: - feat(FN-3022): complete Step 4 — document mobile quick chat autofocus behavior - test(FN-3022): complete Step 2 — cover mobile autofocus and delayed enable path - feat(FN-3022): complete Step 1 — reliable mobile autofocus when composer becomes ready Files changed: docs/dashboard-guide.md | 1 + packages/dashboard/app/components/QuickChatFAB.tsx | 42 +++++++++++++++--- .../app/components/__tests__/QuickChatFAB.test.tsx | 51 ++++++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-3022
This commit is contained in:
@@ -806,6 +806,7 @@ export function QuickChatFAB({
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const pendingAttachmentsRef = useRef<PendingAttachment[]>([]);
|
||||
const shouldAutoFocusComposerRef = useRef(false);
|
||||
|
||||
const resolvedModelSelection = selectedModel || configuredDefaultModelSelection;
|
||||
|
||||
@@ -834,6 +835,7 @@ export function QuickChatFAB({
|
||||
}, [chatMode, parsedModelSelection, selectedAgentId]);
|
||||
|
||||
const hasChatTarget = chatMode === "agent" ? Boolean(selectedAgentId) : Boolean(parsedModelSelection);
|
||||
const inputDisabled = !hasChatTarget || !activeSession;
|
||||
|
||||
useEffect(() => {
|
||||
if (agents.length === 0) {
|
||||
@@ -968,13 +970,45 @@ export function QuickChatFAB({
|
||||
}, [pendingAttachments]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
const frame = requestAnimationFrame(() => {
|
||||
inputRef.current?.focus();
|
||||
});
|
||||
return () => cancelAnimationFrame(frame);
|
||||
if (!isOpen) {
|
||||
shouldAutoFocusComposerRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
shouldAutoFocusComposerRef.current = window.innerWidth <= QUICK_CHAT_DESKTOP_BREAKPOINT;
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || inputDisabled || !shouldAutoFocusComposerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const input = inputRef.current;
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeElement = document.activeElement;
|
||||
const panelContainsFocus = activeElement ? panelRef.current?.contains(activeElement) : false;
|
||||
const isBodyFocused = activeElement === document.body;
|
||||
|
||||
if (!panelContainsFocus && !isBodyFocused) {
|
||||
shouldAutoFocusComposerRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const frame = requestAnimationFrame(() => {
|
||||
input.focus();
|
||||
shouldAutoFocusComposerRef.current = false;
|
||||
});
|
||||
|
||||
return () => cancelAnimationFrame(frame);
|
||||
}, [isOpen, inputDisabled]);
|
||||
|
||||
// Attachment object URLs must be revoked when the composer unmounts.
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -1086,8 +1120,6 @@ export function QuickChatFAB({
|
||||
return "Select a model to start chatting";
|
||||
}, [chatMode, selectedAgent, selectedModelTag]);
|
||||
|
||||
const inputDisabled = !hasChatTarget || !activeSession;
|
||||
|
||||
const pendingPreview = pendingMessage.length > 50
|
||||
? `${pendingMessage.slice(0, 50)}…`
|
||||
: pendingMessage;
|
||||
|
||||
Reference in New Issue
Block a user