feat(FN-2996): add research agent tools with extension wiring, remote setti
This merge brings multiple substantial features: new research extension tools wired through the AI engine (with full test coverage and documentation), legacy routines agentId backward compatibility with migration paths, migration of experimental remote settings to the global scope, Nerd Font glyph a Fusion-Task-Id: FN-2996
This commit is contained in:
@@ -637,18 +637,21 @@
|
||||
/* === Quick Chat Mobile (FN: full-screen) =================================== */
|
||||
@media (max-width: 768px) {
|
||||
.quick-chat-panel {
|
||||
/* Full-screen sheet that ignores drag position on mobile. The
|
||||
JSX-level style only emits right/bottom on desktop now, but we
|
||||
still pin every edge with !important here so any stray inline
|
||||
value (or a future regression) cannot pull the panel off-screen.
|
||||
`top: 0` is intentional — when the iOS keyboard opens we just
|
||||
shrink height (via --vv-height), the panel does not translate
|
||||
so the header stays in place. */
|
||||
/* Full-screen sheet that ignores drag position on mobile. `top`
|
||||
follows visualViewport.offsetTop because on the *second* input
|
||||
focus iOS Safari shifts the visual viewport (offsetTop > 0)
|
||||
instead of repositioning the keyboard, so a fixed panel pinned
|
||||
at layout top:0 ends up rendering at visual -offsetTop and slides
|
||||
off-screen. Translating the panel by offsetTop puts the header
|
||||
back at visual y=0 — i.e. the user still sees the top of the
|
||||
chat box. The CSS variable is written directly to the DOM via
|
||||
a layout effect, so it tracks the keyboard 1:1 with no React
|
||||
state lag. */
|
||||
position: fixed !important;
|
||||
inset: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
top: 0 !important;
|
||||
top: var(--vv-offset-top, 0px) !important;
|
||||
bottom: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
|
||||
@@ -901,43 +901,50 @@ export function QuickChatFAB({
|
||||
// iOS keeps the keyboard up across that transfer.
|
||||
const stealthInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// Lock body scroll while the panel is open on mobile. Otherwise iOS
|
||||
// can leave the document scrolled (e.g. after the keyboard was opened
|
||||
// and dismissed once), and on the next open the position:fixed panel
|
||||
// anchors to layout top:0 which is *above* the visible viewport — only
|
||||
// the bottom of the panel (the input bar) pokes into view at the top of
|
||||
// the screen. Locking the body keeps layout-top and visual-top aligned.
|
||||
// Pin the document at the top while the panel is open on mobile.
|
||||
// Otherwise iOS can leave window.scrollY > 0 (e.g. after the keyboard
|
||||
// was opened and dismissed once), and on the next open the
|
||||
// position:fixed panel anchors to layout top:0 which is *above* the
|
||||
// visible viewport — only the bottom of the panel (the input bar)
|
||||
// pokes into view at the top of the screen.
|
||||
//
|
||||
// We deliberately do NOT use `body { position: fixed }` to lock scroll:
|
||||
// that would make the body the containing block for the panel's
|
||||
// position:fixed and reintroduce the same translation bug. Instead we
|
||||
// scroll to 0 and lock overflow on <html> and <body>; the panel's
|
||||
// viewport anchor stays correct.
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
if (typeof window === "undefined" || typeof document === "undefined") return;
|
||||
if (window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return;
|
||||
|
||||
const scrollY = window.scrollY;
|
||||
const html = document.documentElement;
|
||||
const body = document.body;
|
||||
const prev = {
|
||||
position: body.style.position,
|
||||
top: body.style.top,
|
||||
width: body.style.width,
|
||||
overflow: body.style.overflow,
|
||||
htmlOverflow: html.style.overflow,
|
||||
bodyOverflow: body.style.overflow,
|
||||
};
|
||||
|
||||
body.style.position = "fixed";
|
||||
body.style.top = `-${scrollY}px`;
|
||||
body.style.width = "100%";
|
||||
window.scrollTo(0, 0);
|
||||
html.style.overflow = "hidden";
|
||||
body.style.overflow = "hidden";
|
||||
|
||||
return () => {
|
||||
body.style.position = prev.position;
|
||||
body.style.top = prev.top;
|
||||
body.style.width = prev.width;
|
||||
body.style.overflow = prev.overflow;
|
||||
html.style.overflow = prev.htmlOverflow;
|
||||
body.style.overflow = prev.bodyOverflow;
|
||||
window.scrollTo(0, scrollY);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Mirror visualViewport.height onto the panel as --vv-height directly,
|
||||
// bypassing React state. The panel just shrinks when the iOS keyboard
|
||||
// opens — top stays at 0 so the header remains visible.
|
||||
// Mirror visualViewport metrics onto the panel as CSS variables
|
||||
// directly, bypassing React state. --vv-height shrinks the panel to
|
||||
// the visible area; --vv-offset-top translates it back into view when
|
||||
// iOS shifts the visual viewport on input focus (which would otherwise
|
||||
// slide the position:fixed panel off-screen, especially on the second
|
||||
// focus after the keyboard has been dismissed once). Going through
|
||||
// setState here introduced per-event React reconciliation lag that
|
||||
// showed up as visible jank during the keyboard animation.
|
||||
useLayoutEffect(() => {
|
||||
if (!isOpen) return;
|
||||
if (typeof window === "undefined" || !window.visualViewport) return;
|
||||
|
||||
Reference in New Issue
Block a user