Spaces:
Sleeping
Sleeping
Commit
Β·
8e42f9d
1
Parent(s):
1e144a5
add
Browse files
frontend/src/components/shared/FloatingActionWidget.tsx
CHANGED
|
@@ -34,10 +34,6 @@ import { useNavigation } from "@/context/NavigationContext";
|
|
| 34 |
import { useTaskPolling } from "@/hooks/useTaskPolling";
|
| 35 |
import { api } from "@/lib/api";
|
| 36 |
|
| 37 |
-
// Disable jittery animations/motion for the floating widget in HF build
|
| 38 |
-
const DISABLE_FLOATING_WIDGET_JITTER = true;
|
| 39 |
-
const FLOATING_WIDGET_MINIMAL = true; // fixed bottom-right, no drag/tooltip; click opens splitter
|
| 40 |
-
|
| 41 |
// Add custom glow animation styles
|
| 42 |
const glowStyle = `
|
| 43 |
@keyframes magical-glow {
|
|
@@ -143,7 +139,7 @@ export function FloatingActionWidget() {
|
|
| 143 |
|
| 144 |
const [isExpanded, setIsExpanded] = useState(false);
|
| 145 |
const [isDragging, setIsDragging] = useState(false);
|
| 146 |
-
const [showTooltip, setShowTooltip] = useState(
|
| 147 |
const [hasMoved, setHasMoved] = useState(false);
|
| 148 |
const [currentMood, setCurrentMood] = useState<PetMood>("curious");
|
| 149 |
const [currentMessage, setCurrentMessage] = useState<PetMessage>({
|
|
@@ -328,10 +324,10 @@ export function FloatingActionWidget() {
|
|
| 328 |
// Hide tooltip after 6 seconds or when expanded
|
| 329 |
useEffect(() => {
|
| 330 |
const timer = setTimeout(() => {
|
| 331 |
-
|
| 332 |
}, 6000);
|
| 333 |
|
| 334 |
-
if (isExpanded
|
| 335 |
setShowTooltip(false);
|
| 336 |
}
|
| 337 |
|
|
@@ -443,22 +439,62 @@ export function FloatingActionWidget() {
|
|
| 443 |
};
|
| 444 |
}, [lastInteraction]);
|
| 445 |
|
| 446 |
-
// Random movement behavior (
|
| 447 |
useEffect(() => {
|
| 448 |
-
if (DISABLE_FLOATING_WIDGET_JITTER) return;
|
| 449 |
if (isDragging || isExpanded) return;
|
| 450 |
|
| 451 |
const randomMove = () => {
|
| 452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 453 |
};
|
| 454 |
|
| 455 |
-
|
|
|
|
| 456 |
return () => clearInterval(moveInterval);
|
| 457 |
}, [isDragging, isExpanded]);
|
| 458 |
|
| 459 |
// Handle mouse down for dragging
|
| 460 |
const handleMouseDown = (e: React.MouseEvent) => {
|
| 461 |
-
if (FLOATING_WIDGET_MINIMAL) return; // disable drag in minimal mode
|
| 462 |
if (!widgetRef.current) return;
|
| 463 |
|
| 464 |
e.preventDefault();
|
|
@@ -541,12 +577,6 @@ export function FloatingActionWidget() {
|
|
| 541 |
// Prevent toggle during dragging or if widget was just moved
|
| 542 |
if (isDragging || hasMoved) return;
|
| 543 |
|
| 544 |
-
if (FLOATING_WIDGET_MINIMAL) {
|
| 545 |
-
setIsSplitterModalOpen(true);
|
| 546 |
-
setLastInteraction(Date.now());
|
| 547 |
-
return;
|
| 548 |
-
}
|
| 549 |
-
|
| 550 |
setIsExpanded(!isExpanded);
|
| 551 |
setLastInteraction(Date.now()); // Update interaction time
|
| 552 |
};
|
|
@@ -830,11 +860,12 @@ export function FloatingActionWidget() {
|
|
| 830 |
{/* Collapsed Widget */}
|
| 831 |
<div
|
| 832 |
ref={widgetRef}
|
| 833 |
-
className={`fixed ${
|
|
|
|
|
|
|
| 834 |
style={{
|
| 835 |
-
|
| 836 |
-
|
| 837 |
-
: { left: `${position.x}px`, top: `${position.y}px` }),
|
| 838 |
zIndex: 9999,
|
| 839 |
pointerEvents: "auto",
|
| 840 |
transition: isDragging ? "none" : "all 0.1s ease",
|
|
@@ -843,24 +874,23 @@ export function FloatingActionWidget() {
|
|
| 843 |
{/* Pet Widget - Always visible */}
|
| 844 |
<div className="relative">
|
| 845 |
<div
|
| 846 |
-
className={`w-14 h-14 bg-primary hover:bg-primary/90 rounded-full shadow-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-200 ${
|
| 847 |
-
|
| 848 |
-
}
|
| 849 |
-
|
| 850 |
-
} ${
|
| 851 |
-
|
| 852 |
-
} ${
|
| 853 |
-
isAtChartCenter && !FLOATING_WIDGET_MINIMAL
|
| 854 |
? "ring-4 ring-blue-400/50 ring-offset-4 shadow-xl shadow-blue-500/30 scale-110"
|
| 855 |
: ""
|
| 856 |
}`}
|
| 857 |
onMouseDown={handleMouseDown}
|
| 858 |
-
onMouseEnter={
|
| 859 |
-
onMouseLeave={
|
| 860 |
onClick={toggleExpanded}
|
| 861 |
style={{
|
| 862 |
-
|
| 863 |
-
|
|
|
|
| 864 |
transition: isDragging ? "none" : "all 0.2s ease",
|
| 865 |
transform: `scale(${petScale})`,
|
| 866 |
}}
|
|
@@ -873,8 +903,8 @@ export function FloatingActionWidget() {
|
|
| 873 |
</div>
|
| 874 |
|
| 875 |
{/* Pet tooltip with mood-based messages - only show when collapsed */}
|
| 876 |
-
{showTooltip && !isExpanded &&
|
| 877 |
-
<div className="absolute -top-16 -right-4 w-48 bg-black/90 text-white text-xs px-3 py-2 rounded-lg shadow-lg z-10">
|
| 878 |
<div className="text-center">{currentMessage.text}</div>
|
| 879 |
<div className="absolute top-full right-6 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-black/90"></div>
|
| 880 |
</div>
|
|
@@ -883,7 +913,7 @@ export function FloatingActionWidget() {
|
|
| 883 |
</div>
|
| 884 |
|
| 885 |
{/* Expanded Panel - Positioned Separately */}
|
| 886 |
-
{isExpanded &&
|
| 887 |
<div
|
| 888 |
className="fixed"
|
| 889 |
style={{
|
|
|
|
| 34 |
import { useTaskPolling } from "@/hooks/useTaskPolling";
|
| 35 |
import { api } from "@/lib/api";
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
// Add custom glow animation styles
|
| 38 |
const glowStyle = `
|
| 39 |
@keyframes magical-glow {
|
|
|
|
| 139 |
|
| 140 |
const [isExpanded, setIsExpanded] = useState(false);
|
| 141 |
const [isDragging, setIsDragging] = useState(false);
|
| 142 |
+
const [showTooltip, setShowTooltip] = useState(true);
|
| 143 |
const [hasMoved, setHasMoved] = useState(false);
|
| 144 |
const [currentMood, setCurrentMood] = useState<PetMood>("curious");
|
| 145 |
const [currentMessage, setCurrentMessage] = useState<PetMessage>({
|
|
|
|
| 324 |
// Hide tooltip after 6 seconds or when expanded
|
| 325 |
useEffect(() => {
|
| 326 |
const timer = setTimeout(() => {
|
| 327 |
+
setShowTooltip(false);
|
| 328 |
}, 6000);
|
| 329 |
|
| 330 |
+
if (isExpanded) {
|
| 331 |
setShowTooltip(false);
|
| 332 |
}
|
| 333 |
|
|
|
|
| 439 |
};
|
| 440 |
}, [lastInteraction]);
|
| 441 |
|
| 442 |
+
// Random movement behavior (when not being dragged)
|
| 443 |
useEffect(() => {
|
|
|
|
| 444 |
if (isDragging || isExpanded) return;
|
| 445 |
|
| 446 |
const randomMove = () => {
|
| 447 |
+
const moveChance = Math.random();
|
| 448 |
+
if (moveChance < 0.2) {
|
| 449 |
+
// 20% chance to move
|
| 450 |
+
const margin = 100;
|
| 451 |
+
const newX =
|
| 452 |
+
margin + Math.random() * (window.innerWidth - 300 - margin);
|
| 453 |
+
const newY =
|
| 454 |
+
margin + Math.random() * (window.innerHeight - 200 - margin);
|
| 455 |
+
|
| 456 |
+
setPosition({ x: newX, y: newY });
|
| 457 |
+
setIsAnimating(true);
|
| 458 |
+
|
| 459 |
+
// Show a playful message during movement
|
| 460 |
+
const playfulMessages = [
|
| 461 |
+
{
|
| 462 |
+
text: "πββοΈ Just stretching my legs!",
|
| 463 |
+
mood: "excited" as PetMood,
|
| 464 |
+
emoji: "πββοΈ",
|
| 465 |
+
},
|
| 466 |
+
{
|
| 467 |
+
text: "π Change of scenery!",
|
| 468 |
+
mood: "happy" as PetMood,
|
| 469 |
+
emoji: "π",
|
| 470 |
+
},
|
| 471 |
+
{
|
| 472 |
+
text: "π Exploring your workspace!",
|
| 473 |
+
mood: "curious" as PetMood,
|
| 474 |
+
emoji: "π",
|
| 475 |
+
},
|
| 476 |
+
];
|
| 477 |
+
const randomMsg =
|
| 478 |
+
playfulMessages[Math.floor(Math.random() * playfulMessages.length)];
|
| 479 |
+
if (!randomMsg) return;
|
| 480 |
+
setCurrentMessage(randomMsg);
|
| 481 |
+
setCurrentMood(randomMsg.mood);
|
| 482 |
+
setShowTooltip(true);
|
| 483 |
+
|
| 484 |
+
setTimeout(() => {
|
| 485 |
+
setIsAnimating(false);
|
| 486 |
+
setShowTooltip(false);
|
| 487 |
+
}, 2000);
|
| 488 |
+
}
|
| 489 |
};
|
| 490 |
|
| 491 |
+
// Check for random movement every 30-60 seconds
|
| 492 |
+
const moveInterval = setInterval(randomMove, 45000);
|
| 493 |
return () => clearInterval(moveInterval);
|
| 494 |
}, [isDragging, isExpanded]);
|
| 495 |
|
| 496 |
// Handle mouse down for dragging
|
| 497 |
const handleMouseDown = (e: React.MouseEvent) => {
|
|
|
|
| 498 |
if (!widgetRef.current) return;
|
| 499 |
|
| 500 |
e.preventDefault();
|
|
|
|
| 577 |
// Prevent toggle during dragging or if widget was just moved
|
| 578 |
if (isDragging || hasMoved) return;
|
| 579 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 580 |
setIsExpanded(!isExpanded);
|
| 581 |
setLastInteraction(Date.now()); // Update interaction time
|
| 582 |
};
|
|
|
|
| 860 |
{/* Collapsed Widget */}
|
| 861 |
<div
|
| 862 |
ref={widgetRef}
|
| 863 |
+
className={`fixed ${
|
| 864 |
+
isDragging ? "cursor-grabbing select-none" : "cursor-grab"
|
| 865 |
+
}`}
|
| 866 |
style={{
|
| 867 |
+
left: `${position.x}px`,
|
| 868 |
+
top: `${position.y}px`,
|
|
|
|
| 869 |
zIndex: 9999,
|
| 870 |
pointerEvents: "auto",
|
| 871 |
transition: isDragging ? "none" : "all 0.1s ease",
|
|
|
|
| 874 |
{/* Pet Widget - Always visible */}
|
| 875 |
<div className="relative">
|
| 876 |
<div
|
| 877 |
+
className={`w-14 h-14 bg-primary hover:bg-primary/90 rounded-full shadow-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-200 hover:scale-110 relative ${
|
| 878 |
+
showTooltip ? "shadow-primary/50 shadow-2xl" : ""
|
| 879 |
+
} ${isDragging ? "scale-110 shadow-2xl opacity-90" : ""} ${
|
| 880 |
+
isAnimating ? "animate-pulse" : ""
|
| 881 |
+
} ${isExpanded ? "ring-2 ring-primary/50 ring-offset-2" : ""} ${
|
| 882 |
+
isAtChartCenter
|
|
|
|
|
|
|
| 883 |
? "ring-4 ring-blue-400/50 ring-offset-4 shadow-xl shadow-blue-500/30 scale-110"
|
| 884 |
: ""
|
| 885 |
}`}
|
| 886 |
onMouseDown={handleMouseDown}
|
| 887 |
+
onMouseEnter={handleMouseEnter}
|
| 888 |
+
onMouseLeave={handleMouseLeave}
|
| 889 |
onClick={toggleExpanded}
|
| 890 |
style={{
|
| 891 |
+
animation: showTooltip
|
| 892 |
+
? "magical-glow 2s ease-in-out infinite"
|
| 893 |
+
: undefined,
|
| 894 |
transition: isDragging ? "none" : "all 0.2s ease",
|
| 895 |
transform: `scale(${petScale})`,
|
| 896 |
}}
|
|
|
|
| 903 |
</div>
|
| 904 |
|
| 905 |
{/* Pet tooltip with mood-based messages - only show when collapsed */}
|
| 906 |
+
{showTooltip && !isExpanded && (
|
| 907 |
+
<div className="absolute -top-16 -right-4 w-48 bg-black/90 text-white text-xs px-3 py-2 rounded-lg shadow-lg animate-bounce z-10">
|
| 908 |
<div className="text-center">{currentMessage.text}</div>
|
| 909 |
<div className="absolute top-full right-6 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-black/90"></div>
|
| 910 |
</div>
|
|
|
|
| 913 |
</div>
|
| 914 |
|
| 915 |
{/* Expanded Panel - Positioned Separately */}
|
| 916 |
+
{isExpanded && (
|
| 917 |
<div
|
| 918 |
className="fixed"
|
| 919 |
style={{
|