Skip to content

Commit 86872a9

Browse files
committed
fix(mobile): address interaction and viewport regressions
1 parent dd2b2b1 commit 86872a9

30 files changed

Lines changed: 358 additions & 168 deletions

File tree

‎apps/desktop/src/renderer/credential-picker/index.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function CredentialPicker({ configuration, api }: CredentialPickerProps) {
5757
align='start'
5858
sideOffset={0}
5959
avoidCollisions={false}
60-
className='w-[320px] max-w-none'
60+
className='w-[320px] max-w-none max-md:max-w-none!'
6161
aria-label='Saved passwords'
6262
onOpenAutoFocus={(event) => {
6363
event.preventDefault()

‎apps/sim/app/(interfaces)/chat/[identifier]/chat.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

33
import { type ReactNode, type RefObject, useCallback, useMemo, useRef, useState } from 'react'
4+
import { usePrefersReducedMotion } from '@sim/emcn'
45
import { createLogger } from '@sim/logger'
56
import { generateId } from '@sim/utils/id'
67
import {
@@ -70,6 +71,7 @@ function ChatViewport({ children }: ChatViewportProps) {
7071
}
7172

7273
export default function ChatClient({ identifier }: { identifier: string }) {
74+
const prefersReducedMotion = usePrefersReducedMotion()
7375
const [messages, setMessages] = useState<ChatMessage[]>([])
7476
const [isLoading, setIsLoading] = useState(false)
7577
const messagesEndRef = useRef<HTMLDivElement>(null)
@@ -113,7 +115,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
113115
* With `force` (jump button), re-pins to bottom.
114116
*/
115117
const scrollToBottom = (options?: { behavior?: ScrollBehavior; force?: boolean }) => {
116-
const behavior = options?.behavior ?? 'smooth'
118+
const behavior = prefersReducedMotion ? 'auto' : (options?.behavior ?? 'smooth')
117119
const force = options?.force === true
118120
if (!force && !stickToBottomRef.current) return
119121
if (!messagesEndRef.current) return
@@ -143,7 +145,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
143145

144146
container.scrollTo({
145147
top: container.scrollTop + messageRect.top - containerRect.top,
146-
behavior: 'smooth',
148+
behavior: prefersReducedMotion ? 'auto' : 'smooth',
147149
})
148150
}
149151

‎apps/sim/app/(interfaces)/chat/components/input/input.tsx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { createLogger } from '@sim/logger'
88
import { generateId } from '@sim/utils/id'
99
import { CHAT_ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation'
1010
import { PublicChatActionButton } from '@/app/(interfaces)/chat/components/input/public-chat-action-button'
11-
import { isMobileViewport } from '@/hooks/use-is-mobile'
11+
import { isTouchMobileViewport } from '@/hooks/use-is-mobile'
1212

1313
const logger = createLogger('ChatInput')
1414

@@ -110,7 +110,12 @@ export const ChatInput: React.FC<{
110110
}
111111

112112
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
113-
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing && !isMobileViewport()) {
113+
if (
114+
e.key === 'Enter' &&
115+
!e.shiftKey &&
116+
!e.nativeEvent.isComposing &&
117+
!isTouchMobileViewport()
118+
) {
114119
e.preventDefault()
115120
handleSubmit()
116121
}

‎apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -547,8 +547,12 @@ function BreadcrumbLocationPopover({
547547
}}
548548
onFocus={openPopover}
549549
onBlur={scheduleClose}
550-
onMouseEnter={openPopover}
551-
onMouseLeave={scheduleClose}
550+
onPointerEnter={(event) => {
551+
if (event.pointerType === 'mouse') openPopover()
552+
}}
553+
onPointerLeave={(event) => {
554+
if (event.pointerType === 'mouse') scheduleClose()
555+
}}
552556
onDragOver={drag?.onDragOver}
553557
onDragLeave={drag?.onDragLeave}
554558
onDrop={drag?.onDrop}
@@ -577,8 +581,12 @@ function BreadcrumbLocationPopover({
577581
maxHeight={420}
578582
border
579583
className={POPOVER_ANIMATION_CLASSES}
580-
onMouseEnter={openPopover}
581-
onMouseLeave={scheduleClose}
584+
onPointerEnter={(event) => {
585+
if (event.pointerType === 'mouse') openPopover()
586+
}}
587+
onPointerLeave={(event) => {
588+
if (event.pointerType === 'mouse') scheduleClose()
589+
}}
582590
>
583591
<PopoverSection>
584592
<span className='inline-flex items-center gap-1'>

‎apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/zoomable-preview.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ export function ZoomablePreview({
276276
<div
277277
ref={contentRef}
278278
className={cn(
279-
'flex items-center justify-center will-change-transform',
279+
'flex items-center justify-center',
280+
(zoom !== 1 || offset.x !== 0 || offset.y !== 0) && 'will-change-transform',
280281
contentClassName
281282
)}
282283
style={{

‎apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/use-prompt-editor.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import {
3939
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/utils'
4040
import { type McpServer, useMcpToolServers } from '@/hooks/queries/mcp'
4141
import { type SkillDefinition, useSkills } from '@/hooks/queries/skills'
42-
import { isMobileViewport } from '@/hooks/use-is-mobile'
42+
import { isTouchMobileViewport } from '@/hooks/use-is-mobile'
4343
import type { ChatContext } from '@/stores/panel'
4444

4545
/**
@@ -961,7 +961,7 @@ export function usePromptEditor({
961961
e.key === 'Enter' &&
962962
!e.shiftKey &&
963963
!e.nativeEvent.isComposing &&
964-
!isMobileViewport() &&
964+
!isTouchMobileViewport() &&
965965
policy?.onSubmit
966966
) {
967967
e.preventDefault()

‎apps/sim/app/workspace/[workspaceId]/home/hooks/use-resource-panel.ts‎

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,15 @@ export function useResourcePanelController() {
9494
isResourceCollapsedRef.current = collapsed
9595
setIsResourceCollapsedState(collapsed)
9696
}, [])
97-
const resourceCollapseOwnedByUserRef = useRef(false)
97+
const [resourceCollapseOwnedByUser, setResourceCollapseOwnedByUser] = useState(false)
9898
const resourceSelectionOwnedByUserRef = useRef(false)
9999

100100
function handleResourceEvent(resourceId: string, options?: ResourceEventOptions) {
101101
const activeResourceId = effectiveActiveResourceIdRef.current
102102
const presentation = resolveResourceEventPresentation({
103103
activeResourceId,
104104
activationRequested: shouldActivateResourceEvent(activeResourceId, resourceId, options),
105-
panelCollapseOwnedByUser: resourceCollapseOwnedByUserRef.current,
105+
panelCollapseOwnedByUser: resourceCollapseOwnedByUser,
106106
panelCollapsed: isResourceCollapsedRef.current,
107107
resourceId,
108108
selectionOwnedByUser: resourceSelectionOwnedByUserRef.current,
@@ -156,7 +156,8 @@ export function useResourcePanelController() {
156156
resourceActivityIds,
157157
setResourceActivityIds,
158158
isResourceCollapsedRef,
159-
resourceCollapseOwnedByUserRef,
159+
resourceCollapseOwnedByUser,
160+
setResourceCollapseOwnedByUser,
160161
resourceSelectionOwnedByUserRef,
161162
effectiveActiveResourceIdRef,
162163
onResourceEvent: handleResourceEvent,
@@ -196,7 +197,8 @@ export function useChatResourcePanel(
196197
setSkipResourceTransition,
197198
setResourceActivityIds,
198199
isResourceCollapsedRef,
199-
resourceCollapseOwnedByUserRef,
200+
resourceCollapseOwnedByUser,
201+
setResourceCollapseOwnedByUser,
200202
resourceSelectionOwnedByUserRef,
201203
effectiveActiveResourceIdRef,
202204
onResourceEvent: handleResourceEvent,
@@ -207,10 +209,10 @@ export function useChatResourcePanel(
207209
const resourceAttentionChatIdRef = useRef(resolvedChatId)
208210

209211
const collapseResource = useCallback(() => {
210-
resourceCollapseOwnedByUserRef.current = true
212+
setResourceCollapseOwnedByUser(true)
211213
resourceSelectionOwnedByUserRef.current = true
212214
setResourceCollapsed(true)
213-
}, [setResourceCollapsed])
215+
}, [setResourceCollapsed, setResourceCollapseOwnedByUser])
214216

215217
const clearResourceActivity = useCallback((resourceId: string) => {
216218
setResourceActivityIds((current) => {
@@ -222,7 +224,7 @@ export function useChatResourcePanel(
222224
}, [])
223225

224226
const expandResource = () => {
225-
resourceCollapseOwnedByUserRef.current = false
227+
setResourceCollapseOwnedByUser(false)
226228
resourceSelectionOwnedByUserRef.current = true
227229
const activeResourceId = effectiveActiveResourceIdRef.current
228230
if (activeResourceId) clearResourceActivity(activeResourceId)
@@ -254,13 +256,13 @@ export function useChatResourcePanel(
254256

255257
const addResourceFromUser = useCallback(
256258
(resource: MothershipResource) => {
257-
resourceCollapseOwnedByUserRef.current = false
259+
setResourceCollapseOwnedByUser(false)
258260
resourceSelectionOwnedByUserRef.current = true
259261
addResource(resource)
260262
selectResourceFromUser(getChatResourceSelectionId(resource))
261263
setResourceCollapsed(false)
262264
},
263-
[addResource, selectResourceFromUser, setResourceCollapsed]
265+
[addResource, selectResourceFromUser, setResourceCollapsed, setResourceCollapseOwnedByUser]
264266
)
265267

266268
const handleResourceResizePointerDown = useCallback(
@@ -295,25 +297,25 @@ export function useChatResourcePanel(
295297
setResourceCollapsed(true)
296298
}
297299
if (!resolvedChatId || (previousChatId && previousChatId !== resolvedChatId)) {
298-
resourceCollapseOwnedByUserRef.current = false
300+
setResourceCollapseOwnedByUser(false)
299301
resourceSelectionOwnedByUserRef.current = false
300302
setResourceActivityIds(new Set())
301303
}
302-
}, [resolvedChatId, setResourceCollapsed])
304+
}, [resolvedChatId, setResourceCollapsed, setResourceCollapseOwnedByUser])
303305

304306
useEffect(() => {
305307
if (
306308
isCompactLayout !== false ||
307309
!(resources.length > 0 && isResourceCollapsedRef.current) ||
308-
resourceCollapseOwnedByUserRef.current
310+
resourceCollapseOwnedByUser
309311
) {
310312
return
311313
}
312314
setResourceCollapsed(false)
313315
setSkipResourceTransition(true)
314316
const id = requestAnimationFrame(() => setSkipResourceTransition(false))
315317
return () => cancelAnimationFrame(id)
316-
}, [isCompactLayout, resources, setResourceCollapsed])
318+
}, [isCompactLayout, resources, resourceCollapseOwnedByUser, setResourceCollapsed])
317319

318320
useEffect(() => {
319321
if (resources.length === 0 && !isResourceCollapsedRef.current) {
@@ -333,6 +335,7 @@ export function useChatResourcePanel(
333335
...controller,
334336
initialScrollBlocked:
335337
resources.length > 0 &&
338+
!resourceCollapseOwnedByUser &&
336339
(isCompactLayout === null || (isCompactLayout === false && isResourceCollapsed)),
337340
desktopTabResourceOptions,
338341
mothershipRef,

‎apps/sim/app/workspace/[workspaceId]/logs/logs.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import type {
3636
WorkflowLogSummary,
3737
} from '@/lib/api/contracts/logs'
3838
import { dollarsToCredits } from '@/lib/billing/credits/conversion'
39+
import { getContextMenuPosition } from '@/lib/core/utils/context-menu'
3940
import { formatDateShort } from '@/lib/core/utils/date-display'
4041
import {
4142
getEndDateFromTimeRange,
@@ -537,7 +538,7 @@ export default function Logs() {
537538
(e: React.MouseEvent, rowId: string) => {
538539
e.preventDefault()
539540
const log = logs.find((l) => l.id === rowId) ?? null
540-
setContextMenuPosition({ x: e.clientX, y: e.clientY })
541+
setContextMenuPosition(getContextMenuPosition(e))
541542
setContextMenuLog(log)
542543
setContextMenuOpen(true)
543544
},

‎apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/views-menu/views-menu.tsx‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ export const ViewsMenu = memo(function ViewsMenu({
116116
onClick={openPopover}
117117
onFocus={openPopover}
118118
onBlur={scheduleClose}
119-
onMouseEnter={openPopover}
120-
onMouseLeave={scheduleClose}
119+
onPointerEnter={(event) => {
120+
if (event.pointerType === 'mouse') openPopover()
121+
}}
122+
onPointerLeave={(event) => {
123+
if (event.pointerType === 'mouse') scheduleClose()
124+
}}
121125
className={cn(chipVariants(), 'max-w-[220px]')}
122126
>
123127
<OverflowText
@@ -137,8 +141,12 @@ export const ViewsMenu = memo(function ViewsMenu({
137141
maxHeight={420}
138142
border
139143
className={POPOVER_ANIMATION_CLASSES}
140-
onMouseEnter={openPopover}
141-
onMouseLeave={scheduleClose}
144+
onPointerEnter={(event) => {
145+
if (event.pointerType === 'mouse') openPopover()
146+
}}
147+
onPointerLeave={(event) => {
148+
if (event.pointerType === 'mouse') scheduleClose()
149+
}}
142150
onFocusCapture={cancelScheduledClose}
143151
>
144152
<PopoverSection>Views</PopoverSection>

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/hooks/use-panel-resize.ts‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,6 @@ function getPanelContainer(): HTMLElement | null {
2020
return document.querySelector<HTMLElement>('.panel-container')
2121
}
2222

23-
/**
24-
* The toast stack also insets its right edge by `--panel-width`, but is
25-
* portalled to `<body>` and so shares no ancestor with the panel. See
26-
* `use-terminal-resize.ts` for why this is written alongside the primary.
27-
*/
28-
function getToastViewport(): (HTMLElement | null)[] {
29-
return [document.querySelector<HTMLElement>('[data-toast-viewport]')]
30-
}
31-
3223
/**
3324
* Handles panel drag-resize with zero React renders during the drag. The
3425
* `--panel-width` variable is written to `.panel-container` (a scoped style
@@ -45,7 +36,6 @@ export function usePanelResize() {
4536
cursor: 'ew-resize',
4637
cssVar: '--panel-width',
4738
getTarget: getPanelContainer,
48-
getExtraTargets: getToastViewport,
4939
compute: computePanelWidth,
5040
commit: (width) => {
5141
const panel = getPanelContainer()

0 commit comments

Comments
 (0)