From 8b6bcab05cda4c47bf95a1050b654b17ba931cc3 Mon Sep 17 00:00:00 2001 From: chenhang Date: Thu, 16 Jul 2026 14:36:33 +0800 Subject: [PATCH] refactor(chat): remove keyboard compatibility layer --- .../chat/components/chat-input-bar.module.css | 5 +- src/app/chat/components/chat-input-bar.tsx | 7 +- .../__tests__/chat-keyboard-geometry.test.ts | 90 ------- .../use-chat-keyboard-avoidance.test.tsx | 178 ------------- .../use-chat-keyboard-diagnostics.test.tsx | 10 +- src/app/chat/hooks/chat-keyboard-geometry.ts | 95 ------- .../chat/hooks/use-chat-keyboard-avoidance.ts | 246 ------------------ .../hooks/use-chat-keyboard-diagnostics.ts | 19 +- src/app/chat/layout.tsx | 5 - 9 files changed, 26 insertions(+), 629 deletions(-) delete mode 100644 src/app/chat/hooks/__tests__/chat-keyboard-geometry.test.ts delete mode 100644 src/app/chat/hooks/__tests__/use-chat-keyboard-avoidance.test.tsx delete mode 100644 src/app/chat/hooks/chat-keyboard-geometry.ts delete mode 100644 src/app/chat/hooks/use-chat-keyboard-avoidance.ts diff --git a/src/app/chat/components/chat-input-bar.module.css b/src/app/chat/components/chat-input-bar.module.css index f33a9f44..ac57c7cc 100644 --- a/src/app/chat/components/chat-input-bar.module.css +++ b/src/app/chat/components/chat-input-bar.module.css @@ -1,14 +1,11 @@ /* ChatInputBar 输入栏容器样式(与 Dart chat_input_bar.dart 对齐) */ -/* 键盘适配:--chat-input-lift 仅写入当前聊天输入栏。 */ - .bar { flex: 0 0 auto; padding: 0 calc(var(--chat-inline-padding, 16px) + var(--app-safe-right, 0px)) calc( - var(--spacing-lg, 16px) + var(--chat-input-lift, 0px) + - var(--app-safe-bottom, 0px) + var(--spacing-lg, 16px) + var(--app-safe-bottom, 0px) ) calc(var(--chat-inline-padding, 16px) + var(--app-safe-left, 0px)); background: transparent; diff --git a/src/app/chat/components/chat-input-bar.tsx b/src/app/chat/components/chat-input-bar.tsx index 05021634..909e2b60 100644 --- a/src/app/chat/components/chat-input-bar.tsx +++ b/src/app/chat/components/chat-input-bar.tsx @@ -4,7 +4,7 @@ import { useRef, useState } from "react"; import { useChatDispatch } from "@/stores/chat/chat-context"; import { Logger } from "@/utils/logger"; -import { useChatKeyboardAvoidance } from "../hooks/use-chat-keyboard-avoidance"; +import { useChatKeyboardDiagnostics } from "../hooks/use-chat-keyboard-diagnostics"; import { ChatInputTextField } from "./chat-input-text-field"; import { ChatSendButton } from "./chat-send-button"; import styles from "./chat-input-bar.module.css"; @@ -26,10 +26,7 @@ export function ChatInputBar({ disabled = false }: ChatInputBarProps) { const hasContent = input.trim().length > 0; - useChatKeyboardAvoidance({ - containerRef: barRef, - active: isFocused, - }); + useChatKeyboardDiagnostics({ containerRef: barRef }); const handleInputChange = (value: string) => { setInput(value); diff --git a/src/app/chat/hooks/__tests__/chat-keyboard-geometry.test.ts b/src/app/chat/hooks/__tests__/chat-keyboard-geometry.test.ts deleted file mode 100644 index 330688e1..00000000 --- a/src/app/chat/hooks/__tests__/chat-keyboard-geometry.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { - resolveChatKeyboardFallbackLift, - resolveChatKeyboardLift, - type ChatKeyboardViewportBaseline, - type ChatKeyboardViewportSnapshot, -} from "../chat-keyboard-geometry"; - -const baseline: ChatKeyboardViewportBaseline = { - layoutHeight: 800, - visualHeight: 760, -}; - -function makeSnapshot( - overrides: Partial = {}, -): ChatKeyboardViewportSnapshot { - return { - layoutHeight: 800, - visualHeight: 760, - visualScale: 1, - virtualKeyboardHeight: 0, - ...overrides, - }; -} - -describe("resolveChatKeyboardLift", () => { - it("does not add lift when the content viewport already resized", () => { - expect( - resolveChatKeyboardLift( - baseline, - makeSnapshot({ layoutHeight: 500, visualHeight: 500 }), - ), - ).toEqual({ - keyboardVisible: true, - lift: 0, - source: "content-resize", - }); - }); - - it("uses the visual viewport delta for an overlay keyboard", () => { - expect( - resolveChatKeyboardLift( - baseline, - makeSnapshot({ visualHeight: 460 }), - ), - ).toEqual({ - keyboardVisible: true, - lift: 314, - source: "visual-viewport", - }); - }); - - it("prefers Virtual Keyboard geometry when available", () => { - expect( - resolveChatKeyboardLift( - baseline, - makeSnapshot({ - visualHeight: 520, - virtualKeyboardHeight: 320, - }), - ), - ).toEqual({ - keyboardVisible: true, - lift: 334, - source: "virtual-keyboard", - }); - }); - - it("ignores visual viewport changes caused by page zoom", () => { - expect( - resolveChatKeyboardLift( - baseline, - makeSnapshot({ visualHeight: 460, visualScale: 1.2 }), - ), - ).toEqual({ - keyboardVisible: false, - lift: 0, - source: "none", - }); - }); -}); - -describe("resolveChatKeyboardFallbackLift", () => { - it("scales with viewport height and remains bounded", () => { - expect(resolveChatKeyboardFallbackLift(500)).toBe(260); - expect(resolveChatKeyboardFallbackLift(800)).toBe(336); - expect(resolveChatKeyboardFallbackLift(1_200)).toBe(380); - }); -}); diff --git a/src/app/chat/hooks/__tests__/use-chat-keyboard-avoidance.test.tsx b/src/app/chat/hooks/__tests__/use-chat-keyboard-avoidance.test.tsx deleted file mode 100644 index 4eccba69..00000000 --- a/src/app/chat/hooks/__tests__/use-chat-keyboard-avoidance.test.tsx +++ /dev/null @@ -1,178 +0,0 @@ -/* @vitest-environment jsdom */ - -import { act, useRef } from "react"; -import { createRoot, type Root } from "react-dom/client"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; - -import { BrowserDetector } from "@/utils/browser-detect"; - -import { useChatKeyboardAvoidance } from "../use-chat-keyboard-avoidance"; - -vi.mock("@/utils/browser-detect", () => ({ - BrowserDetector: { - isInAppBrowser: vi.fn(() => true), - }, -})); - -vi.mock("@/utils/platform-detect", () => ({ - PlatformDetector: { - isAndroid: vi.fn(() => true), - }, -})); - -class MockVisualViewport extends EventTarget { - height = 760; - offsetTop = 0; - scale = 1; - width = 390; -} - -function Harness({ active }: { active: boolean }) { - const containerRef = useRef(null); - useChatKeyboardAvoidance({ active, containerRef }); - return
; -} - -describe("useChatKeyboardAvoidance", () => { - let container: HTMLDivElement; - let root: Root; - let visualViewport: MockVisualViewport; - let innerHeight: number; - - beforeEach(() => { - vi.useFakeTimers(); - (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }) - .IS_REACT_ACT_ENVIRONMENT = true; - vi.mocked(BrowserDetector.isInAppBrowser).mockReturnValue(true); - innerHeight = 800; - visualViewport = new MockVisualViewport(); - Object.defineProperty(window, "innerHeight", { - configurable: true, - get: () => innerHeight, - }); - Object.defineProperty(window, "visualViewport", { - configurable: true, - value: visualViewport, - }); - vi.stubGlobal( - "requestAnimationFrame", - vi.fn((callback: FrameRequestCallback) => - window.setTimeout(() => callback(0), 0), - ), - ); - vi.stubGlobal( - "cancelAnimationFrame", - vi.fn((id: number) => window.clearTimeout(id)), - ); - container = document.createElement("div"); - document.body.append(container); - root = createRoot(container); - }); - - afterEach(() => { - act(() => root.unmount()); - container.remove(); - vi.useRealTimers(); - vi.restoreAllMocks(); - vi.unstubAllGlobals(); - }); - - it("uses a one-shot adaptive fallback without polling", () => { - const intervalSpy = vi.spyOn(window, "setInterval"); - renderHarness(root, false); - renderHarness(root, true); - - act(() => vi.advanceTimersByTime(350)); - - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "336px", - ); - expect(intervalSpy).not.toHaveBeenCalled(); - }); - - it("does not use the fallback in a regular Android browser", () => { - vi.mocked(BrowserDetector.isInAppBrowser).mockReturnValue(false); - renderHarness(root, false); - renderHarness(root, true); - - act(() => vi.advanceTimersByTime(350)); - - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "", - ); - }); - - it("uses Visual Viewport geometry and clears after keyboard dismissal", () => { - renderHarness(root, false); - renderHarness(root, true); - - visualViewport.height = 460; - act(() => { - visualViewport.dispatchEvent(new Event("resize")); - vi.runOnlyPendingTimers(); - }); - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "314px", - ); - - visualViewport.height = 760; - act(() => { - visualViewport.dispatchEvent(new Event("resize")); - vi.runOnlyPendingTimers(); - }); - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "0px", - ); - - act(() => vi.advanceTimersByTime(1_000)); - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "0px", - ); - }); - - it("does not add fallback lift when the layout viewport resized", () => { - renderHarness(root, false); - renderHarness(root, true); - - innerHeight = 500; - visualViewport.height = 500; - act(() => { - window.dispatchEvent(new Event("resize")); - vi.advanceTimersByTime(350); - }); - - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "0px", - ); - }); - - it("removes the local lift when focus leaves or the hook unmounts", () => { - renderHarness(root, false); - renderHarness(root, true); - act(() => vi.advanceTimersByTime(350)); - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "336px", - ); - - renderHarness(root, false); - expect(getInput(container).style.getPropertyValue("--chat-input-lift")).toBe( - "", - ); - - act(() => root.unmount()); - expect(container.querySelector('[data-testid="chat-input"]')).toBeNull(); - root = createRoot(container); - }); -}); - -function renderHarness(root: Root, active: boolean): void { - act(() => root.render()); -} - -function getInput(container: HTMLElement): HTMLDivElement { - const input = container.querySelector( - '[data-testid="chat-input"]', - ); - if (!input) throw new Error("Chat input test node was not rendered"); - return input; -} diff --git a/src/app/chat/hooks/__tests__/use-chat-keyboard-diagnostics.test.tsx b/src/app/chat/hooks/__tests__/use-chat-keyboard-diagnostics.test.tsx index 388ca2fe..c6d5375a 100644 --- a/src/app/chat/hooks/__tests__/use-chat-keyboard-diagnostics.test.tsx +++ b/src/app/chat/hooks/__tests__/use-chat-keyboard-diagnostics.test.tsx @@ -68,6 +68,8 @@ describe("chat keyboard diagnostics", () => { afterEach(() => { window.__keyboardDebugCleanup?.(); + document.documentElement.style.removeProperty("--app-viewport-height"); + document.documentElement.style.removeProperty("--app-visible-height"); vi.restoreAllMocks(); }); @@ -79,7 +81,8 @@ describe("chat keyboard diagnostics", () => { it("records viewport events and exposes manual snapshot helpers", () => { const input = document.createElement("textarea"); - input.style.setProperty("--chat-input-lift", "314px"); + document.documentElement.style.setProperty("--app-viewport-height", "800px"); + document.documentElement.style.setProperty("--app-visible-height", "460px"); document.body.append(input); input.focus(); const inputRef = { current: input } as RefObject; @@ -96,7 +99,10 @@ describe("chat keyboard diagnostics", () => { virtualKeyboardRect: { height: 300 }, activeElement: "TEXTAREA", activeFocused: true, - inputLift: "314px", + inputBarTop: 0, + inputBarBottom: 0, + appViewportHeight: "800px", + appVisibleHeight: "460px", platform: { name: "android", deviceModel: "PLB110" }, browser: { inAppBrowserName: "facebook" }, }); diff --git a/src/app/chat/hooks/chat-keyboard-geometry.ts b/src/app/chat/hooks/chat-keyboard-geometry.ts deleted file mode 100644 index 970cbe82..00000000 --- a/src/app/chat/hooks/chat-keyboard-geometry.ts +++ /dev/null @@ -1,95 +0,0 @@ -export const CHAT_KEYBOARD_FOCUS_GAP_PX = 14; -export const CHAT_KEYBOARD_VISIBLE_THRESHOLD_PX = 80; -export const CHAT_KEYBOARD_ZOOM_THRESHOLD = 1.05; -export const CHAT_KEYBOARD_FALLBACK_RATIO = 0.42; -export const CHAT_KEYBOARD_FALLBACK_MIN_PX = 260; -export const CHAT_KEYBOARD_FALLBACK_MAX_PX = 380; - -const CHAT_KEYBOARD_MAX_LIFT_RATIO = 0.55; - -export interface ChatKeyboardViewportBaseline { - layoutHeight: number; - visualHeight: number; -} - -export interface ChatKeyboardViewportSnapshot { - layoutHeight: number; - visualHeight: number; - visualScale: number; - virtualKeyboardHeight: number; -} - -export type ChatKeyboardLiftSource = - | "content-resize" - | "visual-viewport" - | "virtual-keyboard" - | "none"; - -export interface ChatKeyboardLiftResolution { - keyboardVisible: boolean; - lift: number; - source: ChatKeyboardLiftSource; -} - -export function resolveChatKeyboardLift( - baseline: ChatKeyboardViewportBaseline, - snapshot: ChatKeyboardViewportSnapshot, -): ChatKeyboardLiftResolution { - const contentResize = baseline.layoutHeight - snapshot.layoutHeight; - if (contentResize > CHAT_KEYBOARD_VISIBLE_THRESHOLD_PX) { - return { - keyboardVisible: true, - lift: 0, - source: "content-resize", - }; - } - - const virtualKeyboardHeight = Math.max( - 0, - snapshot.virtualKeyboardHeight, - ); - const visualViewportHeight = - snapshot.visualScale <= CHAT_KEYBOARD_ZOOM_THRESHOLD - ? Math.max(0, baseline.visualHeight - snapshot.visualHeight) - : 0; - const occludedHeight = Math.max( - virtualKeyboardHeight, - visualViewportHeight, - ); - - if (occludedHeight <= CHAT_KEYBOARD_VISIBLE_THRESHOLD_PX) { - return { - keyboardVisible: false, - lift: 0, - source: "none", - }; - } - - const maxLift = Math.round( - baseline.layoutHeight * CHAT_KEYBOARD_MAX_LIFT_RATIO, - ); - - return { - keyboardVisible: true, - lift: Math.min( - maxLift, - Math.round(occludedHeight + CHAT_KEYBOARD_FOCUS_GAP_PX), - ), - source: - virtualKeyboardHeight >= visualViewportHeight - ? "virtual-keyboard" - : "visual-viewport", - }; -} - -export function resolveChatKeyboardFallbackLift( - layoutHeight: number, -): number { - return Math.min( - CHAT_KEYBOARD_FALLBACK_MAX_PX, - Math.max( - CHAT_KEYBOARD_FALLBACK_MIN_PX, - Math.round(layoutHeight * CHAT_KEYBOARD_FALLBACK_RATIO), - ), - ); -} diff --git a/src/app/chat/hooks/use-chat-keyboard-avoidance.ts b/src/app/chat/hooks/use-chat-keyboard-avoidance.ts deleted file mode 100644 index 466f96cf..00000000 --- a/src/app/chat/hooks/use-chat-keyboard-avoidance.ts +++ /dev/null @@ -1,246 +0,0 @@ -"use client"; - -import { useEffect, useRef, type RefObject } from "react"; - -import { BrowserDetector } from "@/utils/browser-detect"; -import { Logger } from "@/utils/logger"; -import { PlatformDetector } from "@/utils/platform-detect"; - -import { - resolveChatKeyboardFallbackLift, - resolveChatKeyboardLift, - type ChatKeyboardLiftResolution, - type ChatKeyboardViewportBaseline, - type ChatKeyboardViewportSnapshot, -} from "./chat-keyboard-geometry"; -import { useChatKeyboardDiagnostics } from "./use-chat-keyboard-diagnostics"; - -const CHAT_INPUT_LIFT_VAR = "--chat-input-lift"; -const CHAT_KEYBOARD_FALLBACK_DELAY_MS = 350; - -const log = new Logger("UseChatKeyboardAvoidance"); - -interface VirtualKeyboardLike extends EventTarget { - readonly boundingRect?: Pick; -} - -interface NavigatorWithVirtualKeyboard extends Navigator { - readonly virtualKeyboard?: VirtualKeyboardLike; -} - -export interface UseChatKeyboardAvoidanceOptions { - active: boolean; - containerRef: RefObject; -} - -export function useChatKeyboardAvoidance({ - active, - containerRef, -}: UseChatKeyboardAvoidanceOptions): void { - const activeRef = useRef(active); - const baselineRef = useRef(null); - - useChatKeyboardDiagnostics({ containerRef }); - - useEffect(() => { - activeRef.current = active; - }, [active]); - - useEffect(() => { - if (typeof window === "undefined" || !PlatformDetector.isAndroid()) { - return; - } - - const updateBaseline = () => { - if (activeRef.current) return; - baselineRef.current = readViewportBaseline(); - }; - const visualViewport = window.visualViewport; - - updateBaseline(); - window.addEventListener("resize", updateBaseline); - window.addEventListener("orientationchange", updateBaseline); - visualViewport?.addEventListener("resize", updateBaseline); - visualViewport?.addEventListener("scroll", updateBaseline); - - return () => { - window.removeEventListener("resize", updateBaseline); - window.removeEventListener("orientationchange", updateBaseline); - visualViewport?.removeEventListener("resize", updateBaseline); - visualViewport?.removeEventListener("scroll", updateBaseline); - }; - }, []); - - useEffect(() => { - const container = containerRef.current; - if (typeof window === "undefined" || !container) return; - - if (!active || !PlatformDetector.isAndroid()) { - clearInputLift(container); - if (!active) baselineRef.current = readViewportBaseline(); - return; - } - - const baseline = baselineRef.current ?? readViewportBaseline(); - const visualViewport = window.visualViewport; - const virtualKeyboard = getVirtualKeyboard(); - const fallbackAllowed = BrowserDetector.isInAppBrowser(); - let frameId: number | null = null; - let fallbackTimerId: number | null = null; - let lastLift = -1; - let pendingCanDismiss = false; - let pendingReason = "initial"; - let keyboardObserved = false; - let fallbackApplied = false; - let sessionDismissed = false; - - const applyLift = ( - lift: number, - source: ChatKeyboardLiftResolution["source"] | "fallback" | "reset", - ) => { - const nextLift = Math.max(0, Math.round(lift)); - if (nextLift === lastLift) return; - lastLift = nextLift; - container.style.setProperty(CHAT_INPUT_LIFT_VAR, `${nextLift}px`); - log.debug("[chat-keyboard] apply", { - source, - lift: nextLift, - }); - }; - - const clearFallbackTimer = () => { - if (fallbackTimerId == null) return; - window.clearTimeout(fallbackTimerId); - fallbackTimerId = null; - }; - - const measure = (reason: string, canDismiss: boolean) => { - if (sessionDismissed) return; - - const resolution = resolveChatKeyboardLift( - baseline, - readViewportSnapshot(), - ); - - if (resolution.keyboardVisible) { - keyboardObserved = true; - applyLift(resolution.lift, resolution.source); - return; - } - - if ((keyboardObserved || fallbackApplied) && canDismiss) { - sessionDismissed = true; - clearFallbackTimer(); - applyLift(0, "reset"); - log.debug("[chat-keyboard] dismissed", { reason }); - } - }; - - const scheduleMeasure = (reason: string, canDismiss: boolean) => { - pendingReason = reason; - pendingCanDismiss ||= canDismiss; - if (frameId != null) return; - - frameId = window.requestAnimationFrame(() => { - frameId = null; - const nextReason = pendingReason; - const nextCanDismiss = pendingCanDismiss; - pendingCanDismiss = false; - measure(nextReason, nextCanDismiss); - }); - }; - - const handleVisualResize = () => scheduleMeasure("visual-resize", true); - const handleVisualScroll = () => scheduleMeasure("visual-scroll", false); - const handleWindowResize = () => scheduleMeasure("window-resize", true); - const handleVirtualKeyboardChange = () => - scheduleMeasure("virtual-keyboard", true); - const resetSession = (reason: string) => { - sessionDismissed = true; - clearFallbackTimer(); - applyLift(0, "reset"); - log.debug("[chat-keyboard] reset", { reason }); - }; - const handleOrientationChange = () => resetSession("orientationchange"); - const handleVisibilityChange = () => { - if (document.visibilityState === "hidden") { - resetSession("visibilitychange"); - } - }; - - visualViewport?.addEventListener("resize", handleVisualResize); - visualViewport?.addEventListener("scroll", handleVisualScroll); - virtualKeyboard?.addEventListener( - "geometrychange", - handleVirtualKeyboardChange, - ); - window.addEventListener("resize", handleWindowResize); - window.addEventListener("orientationchange", handleOrientationChange); - document.addEventListener("visibilitychange", handleVisibilityChange); - - scheduleMeasure("focus", false); - fallbackTimerId = window.setTimeout(() => { - fallbackTimerId = null; - if (!activeRef.current || sessionDismissed) return; - - const resolution = resolveChatKeyboardLift( - baseline, - readViewportSnapshot(), - ); - if (resolution.keyboardVisible) { - keyboardObserved = true; - applyLift(resolution.lift, resolution.source); - return; - } - if (!fallbackAllowed) return; - - fallbackApplied = true; - applyLift( - resolveChatKeyboardFallbackLift(baseline.layoutHeight), - "fallback", - ); - }, CHAT_KEYBOARD_FALLBACK_DELAY_MS); - - return () => { - if (frameId != null) window.cancelAnimationFrame(frameId); - clearFallbackTimer(); - visualViewport?.removeEventListener("resize", handleVisualResize); - visualViewport?.removeEventListener("scroll", handleVisualScroll); - virtualKeyboard?.removeEventListener( - "geometrychange", - handleVirtualKeyboardChange, - ); - window.removeEventListener("resize", handleWindowResize); - window.removeEventListener("orientationchange", handleOrientationChange); - document.removeEventListener("visibilitychange", handleVisibilityChange); - clearInputLift(container); - }; - }, [active, containerRef]); -} - -function readViewportBaseline(): ChatKeyboardViewportBaseline { - return { - layoutHeight: window.innerHeight, - visualHeight: window.visualViewport?.height ?? window.innerHeight, - }; -} - -function readViewportSnapshot(): ChatKeyboardViewportSnapshot { - const visualViewport = window.visualViewport; - return { - layoutHeight: window.innerHeight, - visualHeight: visualViewport?.height ?? window.innerHeight, - visualScale: visualViewport?.scale ?? 1, - virtualKeyboardHeight: getVirtualKeyboard()?.boundingRect?.height ?? 0, - }; -} - -function getVirtualKeyboard(): VirtualKeyboardLike | null { - return ( - (navigator as NavigatorWithVirtualKeyboard).virtualKeyboard ?? null - ); -} - -function clearInputLift(container: HTMLElement): void { - container.style.removeProperty(CHAT_INPUT_LIFT_VAR); -} diff --git a/src/app/chat/hooks/use-chat-keyboard-diagnostics.ts b/src/app/chat/hooks/use-chat-keyboard-diagnostics.ts index 09b0bb73..778f7962 100644 --- a/src/app/chat/hooks/use-chat-keyboard-diagnostics.ts +++ b/src/app/chat/hooks/use-chat-keyboard-diagnostics.ts @@ -6,7 +6,6 @@ import { BrowserDetector } from "@/utils/browser-detect"; import { PlatformDetector } from "@/utils/platform-detect"; const CHAT_KEYBOARD_DEBUG_PARAM = "keyboard_debug"; -const CHAT_INPUT_LIFT_VAR = "--chat-input-lift"; interface VirtualKeyboardLike extends EventTarget { readonly boundingRect?: Pick< @@ -41,7 +40,11 @@ export interface ChatKeyboardDebugSnapshot { activeElement: string | null; activeBottom: number | null; activeFocused: boolean; - inputLift: string | null; + inputBarTop: number | null; + inputBarBottom: number | null; + appViewportHeight: string | null; + appVisibleHeight: string | null; + appVisibleOffsetTop: string | null; viewportMeta: string | null; platform: { name: string; @@ -151,6 +154,8 @@ function createChatKeyboardDebugSnapshot( : null; const platform = PlatformDetector.getPlatformInfo(); const browser = BrowserDetector.getBrowserInfo(); + const rootStyle = document.documentElement.style; + const inputBarRect = containerRef.current?.getBoundingClientRect() ?? null; return { event, @@ -176,8 +181,14 @@ function createChatKeyboardDebugSnapshot( activeElement: activeElement?.tagName ?? null, activeBottom: activeRect?.bottom ?? null, activeFocused: activeElement !== document.body, - inputLift: - containerRef.current?.style.getPropertyValue(CHAT_INPUT_LIFT_VAR) || null, + inputBarTop: inputBarRect?.top ?? null, + inputBarBottom: inputBarRect?.bottom ?? null, + appViewportHeight: + rootStyle.getPropertyValue("--app-viewport-height") || null, + appVisibleHeight: + rootStyle.getPropertyValue("--app-visible-height") || null, + appVisibleOffsetTop: + rootStyle.getPropertyValue("--app-visible-offset-top") || null, viewportMeta: document.querySelector('meta[name="viewport"]') ?.content ?? null, diff --git a/src/app/chat/layout.tsx b/src/app/chat/layout.tsx index 048d0789..275a186c 100644 --- a/src/app/chat/layout.tsx +++ b/src/app/chat/layout.tsx @@ -1,12 +1,7 @@ -import type { Viewport } from "next"; import type { ReactNode } from "react"; import { ChatRouteProviders } from "@/providers/chat-route-providers"; -export const viewport: Viewport = { - interactiveWidget: "resizes-content", -}; - export default function ChatLayout({ children }: { children: ReactNode }) { return {children}; }