diff --git a/src/components/SasquatchGuide.tsx b/src/components/SasquatchGuide.tsx index 5b7885c..0c59b19 100644 --- a/src/components/SasquatchGuide.tsx +++ b/src/components/SasquatchGuide.tsx @@ -635,11 +635,26 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) { const bubbleStyle = useMemo(() => { if (dragPos) { - return { position: 'fixed' as const, left: dragPos.x, top: dragPos.y, bottom: 'auto', right: 'auto', zIndex: 50 }; + return { position: 'fixed' as const, left: dragPos.x, top: dragPos.y, bottom: 'auto', right: 'auto', zIndex: 60 }; } return undefined; }, [dragPos]); + // Safety: a bubble position saved on one device/viewport (e.g. a phone) can + // land off-screen or hidden on another (e.g. desktop). On mount, if a saved + // drag position is outside the current viewport, clear it so the bubble + // returns to its default, always-visible corner. + useEffect(() => { + if (!dragPos) return; + const maxX = window.innerWidth - 56; + const maxY = window.innerHeight - 56; + if (dragPos.x < 0 || dragPos.y < 0 || dragPos.x > maxX || dragPos.y > maxY) { + setDragPos(null); + } + // Only run on mount — we don't want to fight the user mid-drag. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // Only auto-open the panel when the context message explicitly opts in via // `autoShow`. On the map (`/`) and The Lab (`/lab`) the guide stays minimized // until the user taps the bubble — the message content is still used to @@ -1181,8 +1196,11 @@ Instructions:
- {activeMessage.message} -
+ {/* Greeting — only shown before the user starts chatting, so the + same wall of welcome text doesn't reappear on every open once + a conversation is underway. */} + {chatMessages.length === 0 && ( ++ {activeMessage.message} +
+ )} - {/* Action button */} - {activeMessage.action && ( + {/* Action button — also only before chatting */} + {chatMessages.length === 0 && activeMessage.action && (