fix(web): agent FAB slides away while scrolling down (QA U6)
Page-level scroll areas only; it returns on scroll up, near the top or at the bottom, and when focused by keyboard. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
312e70d0bc
commit
9b017906cf
1 file changed
+31
-6
@@ -101,6 +101,29 @@ function PulseDot({ className }: { className?: string }) {
|
||||
</span>
|
||||
);
|
||||
}
|
||||
/**
|
||||
* QA U6:页面向下滚动时 FAB 滑出右侧,向上滚动、停在页底或页面很短时回来,
|
||||
* 滚动途中不再盖住右对齐的开关与状态栏。只看页面级滚动区域(高度 ≥ 半屏),列表内小滚动区不算。
|
||||
*/
|
||||
function useHideOnScroll(): boolean {
|
||||
const [hidden, setHidden] = useState(false);
|
||||
useEffect(() => {
|
||||
const last = new WeakMap<object, number>();
|
||||
const onScroll = (e: Event) => {
|
||||
const t = e.target === document ? document.scrollingElement : e.target;
|
||||
if (!(t instanceof HTMLElement) || t.clientHeight < window.innerHeight / 2) return;
|
||||
const top = t.scrollTop;
|
||||
const prev = last.get(t) ?? top;
|
||||
last.set(t, top);
|
||||
const atBottom = top + t.clientHeight >= t.scrollHeight - 4;
|
||||
if (atBottom || top < 80 || top - prev < -8) setHidden(false);
|
||||
else if (top - prev > 8) setHidden(true);
|
||||
};
|
||||
document.addEventListener("scroll", onScroll, { capture: true, passive: true });
|
||||
return () => document.removeEventListener("scroll", onScroll, { capture: true });
|
||||
}, []);
|
||||
return hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* 右下角悬浮按钮 + 全局快捷键。挂在 console layout 里一次。
|
||||
@@ -113,6 +136,7 @@ export function AgentLauncher() {
|
||||
const { open, minimized, runId } = useAgentDrawer();
|
||||
const { count, summary } = useActiveAgentRuns();
|
||||
const lift = useFloatingBarLift();
|
||||
const scrolledAway = useHideOnScroll();
|
||||
const shortcut = agentShortcutLabel();
|
||||
// 挂到 body:外壳是 isolate 的层叠上下文,留在里面的话抽屉打开后"收起"会被遮罩盖住
|
||||
const [host, setHost] = useState<HTMLElement | null>(null);
|
||||
@@ -121,7 +145,7 @@ export function AgentLauncher() {
|
||||
const onAgentPage = /^\/console\/agent(\/|$)/.test(pathname);
|
||||
if (!host || (onAgentPage && !open) || (minimized && runId && !open)) return null;
|
||||
|
||||
const label = open ? "收起 Agent" : count ? `打开 Agent(${count} 个会话进行中)` : "打开 Agent";
|
||||
const label = open ? "收起 Agent" : count ? `打开 Agent(${count} 个会话进行中)` : "打开 Agent";
|
||||
return createPortal(
|
||||
<button
|
||||
type="button"
|
||||
@@ -138,6 +162,7 @@ export function AgentLauncher() {
|
||||
"animate-in fade-in-0 zoom-in-95 duration-base motion-reduce:animate-none",
|
||||
"md:px-2 md:pr-5",
|
||||
open && "z-[60] hidden md:right-[calc(36rem+1rem)] md:flex",
|
||||
!open && scrolledAway && "translate-x-[calc(100%+2rem)] focus-visible:translate-x-0",
|
||||
)}
|
||||
>
|
||||
<span className="relative grid size-10 shrink-0 place-items-center rounded-full pill-selected">
|
||||
@@ -154,7 +179,7 @@ export function AgentLauncher() {
|
||||
</kbd>
|
||||
</span>
|
||||
<span className="max-w-56 truncate text-meta leading-4 text-muted-foreground">
|
||||
{open ? "Esc 关闭" : (summary ?? "交代目标,边做边汇报")}
|
||||
{open ? "Esc 关闭" : (summary ?? "交代目标,边做边汇报")}
|
||||
</span>
|
||||
</span>
|
||||
</button>,
|
||||
@@ -180,7 +205,7 @@ export function AgentNavEntry({ collapsed = false, onNavigate }: { collapsed?: b
|
||||
{...{ [AGENT_LAUNCHER_ATTR]: "" }}
|
||||
onClick={activate}
|
||||
title={title}
|
||||
aria-label={count ? `打开 Agent(${count} 个会话进行中)` : "打开 Agent"}
|
||||
aria-label={count ? `打开 Agent(${count} 个会话进行中)` : "打开 Agent"}
|
||||
aria-keyshortcuts="Control+J Meta+J"
|
||||
className="prism-ring relative mb-2 grid h-10 w-10 shrink-0 cursor-pointer place-items-center rounded-xl text-foreground outline-none transition-transform duration-micro active:scale-[0.95] focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
@@ -207,7 +232,7 @@ export function AgentNavEntry({ collapsed = false, onNavigate }: { collapsed?: b
|
||||
{count ? <PulseDot /> : null}
|
||||
</span>
|
||||
<span className="truncate text-meta text-muted-foreground">
|
||||
{count ? `${count} 个会话进行中` : "交代目标,边做边汇报"}
|
||||
{count ? `${count} 个会话进行中` : "交代目标,边做边汇报"}
|
||||
</span>
|
||||
</span>
|
||||
{count ? (
|
||||
@@ -225,9 +250,9 @@ export function AgentNavEntry({ collapsed = false, onNavigate }: { collapsed?: b
|
||||
export function AgentShortcutsHelp() {
|
||||
const shortcut = agentShortcutLabel();
|
||||
const rows: [string, string][] = [
|
||||
[shortcut, "打开 / 收起 Agent,附带当前页面"],
|
||||
[shortcut, "打开 / 收起 Agent,附带当前页面"],
|
||||
["Esc", "关闭 Agent"],
|
||||
["Enter", "发送目标;Shift+Enter 换行"],
|
||||
["Enter", "发送目标;Shift+Enter 换行"],
|
||||
];
|
||||
return (
|
||||
<section aria-labelledby="agent-shortcuts-title" className="space-y-2">
|
||||
|
||||
Reference in new issue
Block a user