From 0d98d4090e9a84812d22cd6f7ff8ea18fbd95c2b Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 9 Jan 2026 12:09:13 -0500 Subject: [PATCH 1/5] feat(ui): Add support for dragging keyless prompt --- .../devPrompts/KeylessPrompt/index.tsx | 60 +++- .../KeylessPrompt/use-drag-to-corner.ts | 280 ++++++++++++++++++ .../ui/src/components/devPrompts/shared.tsx | 44 +-- 3 files changed, 360 insertions(+), 24 deletions(-) create mode 100644 packages/ui/src/components/devPrompts/KeylessPrompt/use-drag-to-corner.ts diff --git a/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx b/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx index d6e5070cdd4..ea63538650f 100644 --- a/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx +++ b/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx @@ -16,6 +16,7 @@ import { PromptSuccessIcon, } from '../shared'; import { KeySlashIcon } from './KeySlashIcon'; +import { useDragToCorner } from './use-drag-to-corner'; import { useRevalidateEnvironment } from './use-revalidate-environment'; type KeylessPromptProps = { @@ -42,6 +43,7 @@ function withLastActiveFallback(cb: () => string): string { const KeylessPromptInternal = (_props: KeylessPromptProps) => { const { isSignedIn } = useUser(); const [isExpanded, setIsExpanded] = useState(false); + const { corner, isDragging, style: positionStyle, containerRef, onPointerDown, preventClick } = useDragToCorner(); useEffect(() => { if (isSignedIn) { @@ -114,16 +116,27 @@ const KeylessPromptInternal = (_props: KeylessPromptProps) => { return ( ({ position: 'fixed', - bottom: '1.25rem', - right: '1.25rem', height: `${t.sizes.$10}`, minWidth: '13.4rem', paddingLeft: `${t.space.$3}`, borderRadius: '1.25rem', - transition: 'all 195ms cubic-bezier(0.2, 0.61, 0.1, 1)', + touchAction: 'none', // Prevent scroll interference on mobile + cursor: isDragging ? 'grabbing' : 'grab', + + '&:hover [data-drag-handle]': { + opacity: 0.4, + }, + + '&[data-dragging="true"] [data-drag-handle]': { + opacity: 0.6, + }, '&[data-expanded="false"]:hover': { background: 'linear-gradient(180deg, rgba(255, 255, 255, 0.20) 0%, rgba(255, 255, 255, 0) 100%), #1f1f1f', @@ -140,7 +153,6 @@ const KeylessPromptInternal = (_props: KeylessPromptProps) => { gap: `${t.space.$1x5}`, padding: `${t.space.$2x5} ${t.space.$3} ${t.space.$3} ${t.space.$3}`, borderRadius: `${t.radii.$xl}`, - transition: 'all 230ms cubic-bezier(0.28, 1, 0.32, 1)', }, })} > @@ -149,15 +161,53 @@ const KeylessPromptInternal = (_props: KeylessPromptProps) => { aria-expanded={isForcedExpanded} aria-controls={contentIdentifier} id={buttonIdentifier} - onClick={() => !claimed && setIsExpanded(prev => !prev)} + onClick={e => { + if (preventClick) { + e.preventDefault(); + e.stopPropagation(); + return; + } + if (!claimed) { + setIsExpanded(prev => !prev); + } + }} css={css` ${basePromptElementStyles}; width: 100%; display: flex; justify-content: space-between; align-items: center; + position: relative; `} > + {/* Drag handle indicator */} +