fix: stabilize mobile logo carry across page re-entry

This commit is contained in:
xmanrui
2026-03-03 21:14:07 +08:00
parent 08e83b12fe
commit 597e0a5183
3 changed files with 62 additions and 11 deletions
+21 -8
View File
@@ -47,21 +47,34 @@ export function GlobalBugsOverlay() {
const onStorage = () => applyConfig();
const onConfigChanged = () => applyConfig();
const getVisibleLogoAnchorCenter = (): { x: number; y: number } | null => {
const anchors = Array.from(document.querySelectorAll<HTMLElement>("[data-openclaw-logo-anchor='true']"));
for (const anchor of anchors) {
const rect = anchor.getBoundingClientRect();
if (rect.width <= 0 || rect.height <= 0) continue;
return {
x: (rect.left + rect.width / 2) / BUGS_ZOOM,
y: (rect.top + rect.height / 2) / BUGS_ZOOM,
};
}
return null;
};
const onLogoDragStart = () => {
if (!systemRef.current) return;
// Detect mobile viewport
const isMobile = window.matchMedia("(max-width: 767px)").matches;
// Calculate logo position based on viewport
let startX: number;
let startY: number;
if (isMobile) {
// Mobile: logo is in top header, centered horizontally
startX = (window.innerWidth / 2) / BUGS_ZOOM;
startY = 28 / BUGS_ZOOM; // Half of header height (h-14 = 56px)
const anchorCenter = getVisibleLogoAnchorCenter();
if (anchorCenter) {
startX = anchorCenter.x;
startY = anchorCenter.y;
} else {
startX = (window.innerWidth / 2) / BUGS_ZOOM;
startY = 28 / BUGS_ZOOM;
}
} else {
// Desktop: logo is in left sidebar
// Keep desktop behavior unchanged.
startX = 58 / BUGS_ZOOM;
startY = 42 / BUGS_ZOOM;
}
+21 -2
View File
@@ -188,6 +188,7 @@ export function Sidebar() {
};
const logoTransform = `translate(${manualLogoOffset.dx + logoCarry.dx}px, ${manualLogoOffset.dy + logoCarry.dy}px) rotate(${logoCarry.angle + manualLogoAngle}rad)`;
const mobileLogoTransform = `translate(${logoCarry.dx}px, ${logoCarry.dy}px) rotate(${logoCarry.angle}rad)`;
const logoCursor = !bugsEnabled ? (isLogoDragging ? "grabbing" : "grab") : "default";
const mobileCurrent = NAV_ITEMS.flatMap((g) => g.items).find((item) =>
item.href === "/" ? pathname === "/" : pathname.startsWith(item.href)
@@ -243,8 +244,26 @@ export function Sidebar() {
>
</button>
<Link href="/" className="flex items-center gap-2 min-w-0">
<span className="text-2xl leading-none">🦞</span>
<Link
href="/"
className="flex items-center gap-2 min-w-0"
onClickCapture={handleLogoClickCapture}
onDragStart={handleLogoNativeDragStart}
draggable={false}
>
<span
className="relative inline-block text-2xl leading-none transition-opacity duration-300"
data-openclaw-logo-anchor="true"
onDragStart={handleLogoNativeDragStart}
draggable={false}
style={{
transform: mobileLogoTransform,
transformOrigin: "50% 50%",
opacity: logoCarry.hidden ? 0 : 1,
}}
>
🦞
</span>
<div className="min-w-0">
<div className="text-xs font-bold tracking-wide truncate">OPENCLAW</div>
<div className="text-[10px] text-[var(--text-muted)] truncate">
+20 -1
View File
@@ -297,10 +297,25 @@ export class BugSystem {
this.logoCarry.carrierIds.clear()
this.logoCarry.laggers.clear()
this.logoCarry.regripUntil.clear()
this.logoCarry.logoX = this.logoCarry.startX
this.logoCarry.logoY = this.logoCarry.startY
this.logoCarry.logoVx = 0
this.logoCarry.logoVy = 0
this.logoCarry.logoAngle = 0
this.logoCarry.logoAngularV = 0
this.logoCarry.displayX = this.logoCarry.startX
this.logoCarry.displayY = this.logoCarry.startY
this.logoCarry.displayVx = 0
this.logoCarry.displayVy = 0
this.logoCarry.displayAngle = 0
this.logoCarry.displayAngularV = 0
for (const b of this.bugs) b.isCarrier = false
}
getLogoCarryVisual(): { active: boolean; dx: number; dy: number; angle: number; hidden: boolean } {
if (!this.logoCarry.active) {
return { active: false, dx: 0, dy: 0, angle: 0, hidden: false }
}
return {
active: this.logoCarry.active,
dx: this.logoCarry.displayX - this.logoCarry.startX,
@@ -660,11 +675,15 @@ export class BugSystem {
this.logoCarry.logoAngle = clamp(this.logoCarry.logoAngle, -0.75, 0.75)
this.logoCarry.logoX += this.logoCarry.logoVx * dt
this.logoCarry.logoY += this.logoCarry.logoVy * dt
} else {
} else if (n > 0) {
this.logoCarry.logoVx *= 0.85
this.logoCarry.logoVy *= 0.85
this.logoCarry.logoAngularV *= 0.82
this.logoCarry.logoAngle += this.logoCarry.logoAngularV * dt
} else {
this.logoCarry.logoVx = 0
this.logoCarry.logoVy = 0
this.logoCarry.logoAngularV = 0
}
// Keep visual center fully aligned with physical payload center.