fix(asset-ai): refine mobile viewport and operating query scope
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -92,18 +92,47 @@ export default function AssetAssistant({
|
||||
const page = useRef<HTMLElement>(null);
|
||||
useLayoutEffect(() => {
|
||||
const viewport = window.visualViewport;
|
||||
const mobile = window.matchMedia('(max-width: 767px)');
|
||||
let frame = 0;
|
||||
let unlock: (() => void) | undefined;
|
||||
const updateViewport = () => {
|
||||
frame = 0;
|
||||
if (!mobile.matches || (viewport && viewport.scale !== 1)) return;
|
||||
page.current?.style.setProperty('--aq-viewport-height', `${viewport?.height ?? window.innerHeight}px`);
|
||||
page.current?.style.setProperty('--aq-viewport-top', `${viewport?.offsetTop ?? 0}px`);
|
||||
};
|
||||
updateViewport();
|
||||
viewport?.addEventListener('resize', updateViewport);
|
||||
viewport?.addEventListener('scroll', updateViewport);
|
||||
window.addEventListener('resize', updateViewport);
|
||||
const scheduleViewport = () => {
|
||||
if (!frame) frame = requestAnimationFrame(updateViewport);
|
||||
};
|
||||
const syncMobile = () => {
|
||||
unlock?.();
|
||||
unlock = undefined;
|
||||
if (mobile.matches) {
|
||||
const scrollY = window.scrollY;
|
||||
const body = document.body;
|
||||
const previous = body.style.top;
|
||||
body.style.top = `${-scrollY}px`;
|
||||
body.classList.add('aq-mobile-open');
|
||||
unlock = () => {
|
||||
body.classList.remove('aq-mobile-open');
|
||||
body.style.top = previous;
|
||||
window.scrollTo({ top: scrollY, behavior: 'instant' });
|
||||
};
|
||||
}
|
||||
updateViewport();
|
||||
};
|
||||
syncMobile();
|
||||
mobile.addEventListener('change', syncMobile);
|
||||
viewport?.addEventListener('resize', scheduleViewport);
|
||||
viewport?.addEventListener('scroll', scheduleViewport);
|
||||
window.addEventListener('resize', scheduleViewport);
|
||||
return () => {
|
||||
viewport?.removeEventListener('resize', updateViewport);
|
||||
viewport?.removeEventListener('scroll', updateViewport);
|
||||
window.removeEventListener('resize', updateViewport);
|
||||
cancelAnimationFrame(frame);
|
||||
mobile.removeEventListener('change', syncMobile);
|
||||
viewport?.removeEventListener('resize', scheduleViewport);
|
||||
viewport?.removeEventListener('scroll', scheduleViewport);
|
||||
window.removeEventListener('resize', scheduleViewport);
|
||||
unlock?.();
|
||||
};
|
||||
}, []);
|
||||
const [theme, setTheme] = useState(appearance);
|
||||
@@ -214,7 +243,7 @@ export default function AssetAssistant({
|
||||
setActiveResult(undefined);
|
||||
sessionRef.current = undefined;
|
||||
setIndependent(true);
|
||||
composer.current?.focus();
|
||||
composer.current?.focus({ preventScroll: true });
|
||||
};
|
||||
useEffect(
|
||||
() => () => {
|
||||
@@ -360,7 +389,7 @@ export default function AssetAssistant({
|
||||
setTurns([]);
|
||||
setExpandedTurns(new Set());
|
||||
setDraft("");
|
||||
composer.current?.focus();
|
||||
composer.current?.focus({ preventScroll: true });
|
||||
};
|
||||
useEffect(() => {
|
||||
reset();
|
||||
@@ -390,7 +419,7 @@ export default function AssetAssistant({
|
||||
className="aq-nav-active"
|
||||
onClick={() => {
|
||||
scroller.current?.scrollTo({ top: 0 });
|
||||
composer.current?.focus();
|
||||
composer.current?.focus({ preventScroll: true });
|
||||
}}
|
||||
>
|
||||
<MessageSquare size={16} />
|
||||
|
||||
@@ -799,9 +799,7 @@ function ResultViewContent({
|
||||
"分组",
|
||||
"车辆数",
|
||||
"占比",
|
||||
"运营率",
|
||||
"异动",
|
||||
"待交车",
|
||||
...(result.plan.category === "operating" ? [] : ["运营率", "异动", "待交车"]),
|
||||
"下钻",
|
||||
].map((h) => (
|
||||
<th key={h}>{h}</th>
|
||||
@@ -814,9 +812,11 @@ function ResultViewContent({
|
||||
<td>{g.name}</td>
|
||||
<td>{g.count}</td>
|
||||
<td>{g.share}%</td>
|
||||
<td>{g.rate}%</td>
|
||||
<td>{g.abnormal}</td>
|
||||
<td>{g.pending}</td>
|
||||
{result.plan.category !== "operating" && <>
|
||||
<td>{g.rate}%</td>
|
||||
<td>{g.abnormal}</td>
|
||||
<td>{g.pending}</td>
|
||||
</>}
|
||||
<td>
|
||||
<button
|
||||
disabled={
|
||||
|
||||
@@ -2098,3 +2098,23 @@
|
||||
.aq-page .aq-shimmer { background: none; color: var(--aq-muted); }
|
||||
.aq-page .aq-trace-collapse { transition: none; }
|
||||
}
|
||||
|
||||
/* Keep the document stationary while the keyboard resizes the conversation. */
|
||||
@media (max-width: 767px) {
|
||||
body.aq-mobile-open { position: fixed; width: 100%; overflow: hidden; overscroll-behavior: none; }
|
||||
.aq-page .aq-workspace { min-height: 0; }
|
||||
.aq-page .aq-header { min-height: 48px; height: 48px; padding: 2px 10px; }
|
||||
.aq-page .aq-scope { padding: 6px 12px; }
|
||||
.aq-page .aq-conversation { min-height: 0; overscroll-behavior-y: contain; }
|
||||
.aq-page .aq-composer-area { padding: 6px 12px max(6px, env(safe-area-inset-bottom)); }
|
||||
.aq-page .aq-composer { position: relative; border-radius: 14px; }
|
||||
.aq-page .aq-composer textarea { min-height: 44px; padding: 10px 56px 8px 12px; line-height: 1.5; }
|
||||
.aq-page .aq-composer-bottom { padding: 0 10px 6px; min-height: 22px; }
|
||||
.aq-page .aq-composer-bottom > div:last-child { position: absolute; right: 4px; bottom: 4px; }
|
||||
.aq-page .aq-composer .aq-send { width: 44px; height: 44px; }
|
||||
.aq-page .aq-composer-foot { padding-top: 4px; }
|
||||
.aq-page .aq-context { flex-wrap: nowrap; overflow-x: auto; margin-bottom: 5px; padding-bottom: 2px; }
|
||||
.aq-page .aq-context > * { flex-shrink: 0; }
|
||||
.aq-page .aq-latest-answer { min-height: 32px; margin-bottom: 2px; }
|
||||
.aq-page:has(.aq-composer textarea:focus) .aq-composer-foot { display: none; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user