Skip to content

Commit 5df9cee

Browse files
committed
fix(mobile): reveal requested panels and attachment scroll cues
1 parent da38b07 commit 5df9cee

3 files changed

Lines changed: 75 additions & 43 deletions

File tree

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

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
import type React from 'react'
44
import { useLayoutEffect, useRef, useState } from 'react'
5-
import { Badge, Button, cn, Tooltip } from '@sim/emcn'
5+
import {
6+
Badge,
7+
Button,
8+
cn,
9+
scrollFadeAttributes,
10+
scrollFadeClass,
11+
Tooltip,
12+
useScrollEdges,
13+
} from '@sim/emcn'
614
import { ArrowUp, Paperclip, StopFilled, X } from '@sim/emcn/icons'
715
import { createLogger } from '@sim/logger'
816
import { generateId } from '@sim/utils/id'
@@ -30,11 +38,13 @@ export const ChatInput: React.FC<{
3038
}> = ({ onSubmit, isStreaming = false, onStopStreaming }) => {
3139
const fileInputRef = useRef<HTMLInputElement>(null)
3240
const textareaRef = useRef<HTMLTextAreaElement>(null)
41+
const attachmentsRef = useRef<HTMLDivElement>(null)
3342
const [inputValue, setInputValue] = useState('')
3443
const [attachedFiles, setAttachedFiles] = useState<AttachedFile[]>([])
3544
const [uploadErrors, setUploadErrors] = useState<string[]>([])
3645
const [dragCounter, setDragCounter] = useState(0)
3746
const isDragOver = dragCounter > 0
47+
const attachmentEdges = useScrollEdges(attachmentsRef, { enabled: attachedFiles.length > 0 })
3848

3949
useLayoutEffect(() => {
4050
const el = textareaRef.current
@@ -172,45 +182,54 @@ export const ChatInput: React.FC<{
172182
}}
173183
>
174184
{attachedFiles.length > 0 && (
175-
<div className='mb-1.5 flex flex-wrap gap-1.5 max-md:max-h-24 max-md:overflow-y-auto max-md:overscroll-contain'>
176-
{attachedFiles.map((file) => (
177-
<Tooltip.Root key={file.id}>
178-
<Tooltip.Trigger asChild>
179-
<div className='group relative size-[56px] shrink-0 cursor-pointer overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-3)]'>
180-
{file.dataUrl ? (
181-
<img
182-
src={file.dataUrl}
183-
alt={file.name}
184-
className='size-full object-cover'
185-
/>
186-
) : (
187-
<div className='flex size-full flex-col items-center justify-center gap-0.5 text-[var(--text-muted)]'>
188-
<Paperclip className='size-[18px]' />
189-
<span className='max-w-[48px] truncate px-[2px] text-[9px]'>
190-
{file.name.split('.').pop()}
185+
<div
186+
ref={attachmentsRef}
187+
className={cn(
188+
'mb-1.5 max-md:max-h-24 max-md:overflow-y-auto max-md:overscroll-contain',
189+
scrollFadeClass
190+
)}
191+
{...scrollFadeAttributes(attachmentEdges)}
192+
>
193+
<div className='flex flex-wrap gap-1.5'>
194+
{attachedFiles.map((file) => (
195+
<Tooltip.Root key={file.id}>
196+
<Tooltip.Trigger asChild>
197+
<div className='group relative size-[56px] shrink-0 cursor-pointer overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-3)]'>
198+
{file.dataUrl ? (
199+
<img
200+
src={file.dataUrl}
201+
alt={file.name}
202+
className='size-full object-cover'
203+
/>
204+
) : (
205+
<div className='flex size-full flex-col items-center justify-center gap-0.5 text-[var(--text-muted)]'>
206+
<Paperclip className='size-[18px]' />
207+
<span className='max-w-[48px] truncate px-[2px] text-[9px]'>
208+
{file.name.split('.').pop()}
209+
</span>
210+
</div>
211+
)}
212+
<Button
213+
variant='ghost'
214+
aria-label={`Remove ${file.name}`}
215+
onClick={(e) => {
216+
e.stopPropagation()
217+
handleRemoveFile(file.id)
218+
}}
219+
className='absolute top-[2px] right-[2px] size-11 items-start justify-end p-0 focus-visible:opacity-100 md:pointer-fine:size-[16px] md:pointer-fine:opacity-0 md:pointer-fine:group-hover:opacity-100'
220+
>
221+
<span className='flex size-7 items-center justify-center rounded-full bg-black/60 text-white md:pointer-fine:size-4'>
222+
<X className='size-[10px]' />
191223
</span>
192-
</div>
193-
)}
194-
<Button
195-
variant='ghost'
196-
aria-label={`Remove ${file.name}`}
197-
onClick={(e) => {
198-
e.stopPropagation()
199-
handleRemoveFile(file.id)
200-
}}
201-
className='absolute top-[2px] right-[2px] size-11 items-start justify-end p-0 focus-visible:opacity-100 md:pointer-fine:size-[16px] md:pointer-fine:opacity-0 md:pointer-fine:group-hover:opacity-100'
202-
>
203-
<span className='flex size-7 items-center justify-center rounded-full bg-black/60 text-white md:pointer-fine:size-4'>
204-
<X className='size-[10px]' />
205-
</span>
206-
</Button>
207-
</div>
208-
</Tooltip.Trigger>
209-
<Tooltip.Content side='top'>
210-
<p className='max-w-[200px] truncate'>{file.name}</p>
211-
</Tooltip.Content>
212-
</Tooltip.Root>
213-
))}
224+
</Button>
225+
</div>
226+
</Tooltip.Trigger>
227+
<Tooltip.Content side='top'>
228+
<p className='max-w-[200px] truncate'>{file.name}</p>
229+
</Tooltip.Content>
230+
</Tooltip.Root>
231+
))}
232+
</div>
214233
</div>
215234
)}
216235

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,18 @@ export const Panel = memo(function Panel() {
522522
[copilotSendMessage]
523523
)
524524

525+
const openPanel = useCallback(
526+
(tab: PanelTab) => {
527+
if (panelRef.current && getComputedStyle(panelRef.current).position === 'absolute') {
528+
setIsChatOpen(false)
529+
setVariablesOpen(false)
530+
}
531+
setIsMobilePanelOpen(true)
532+
setActiveTab(tab)
533+
},
534+
[setIsChatOpen, setVariablesOpen, setIsMobilePanelOpen, setActiveTab]
535+
)
536+
525537
/**
526538
* Mark hydration as complete on mount
527539
* This allows React to take over visibility control from CSS
@@ -545,14 +557,14 @@ export const Panel = memo(function Panel() {
545557
/** A mode-bearing send (Ask) belongs to the home chat, which has the mode; left unclaimed, it is stored for that surface. */
546558
if (detail.requestMode) return
547559
e.preventDefault()
548-
setActiveTab('copilot')
560+
openPanel('copilot')
549561
copilotSendMessage(detail.message, detail.fileAttachments, detail.contexts, {
550562
...(detail.resumeUserMessageId ? { resumeUserMessageId: detail.resumeUserMessageId } : {}),
551563
})
552564
}
553565
window.addEventListener(MOTHERSHIP_SEND_MESSAGE_EVENT, handler)
554566
return () => window.removeEventListener(MOTHERSHIP_SEND_MESSAGE_EVENT, handler)
555-
}, [isCopilotTabAvailable, setActiveTab, copilotSendMessage])
567+
}, [isCopilotTabAvailable, openPanel, copilotSendMessage])
556568

557569
useEffect(() => {
558570
if (activeTab !== 'copilot') return
@@ -725,8 +737,7 @@ export const Panel = memo(function Panel() {
725737
{
726738
id: 'focus-toolbar-search',
727739
handler: () => {
728-
setIsMobilePanelOpen(true)
729-
setActiveTab('toolbar')
740+
openPanel('toolbar')
730741
toolbarRef.current?.focusSearch()
731742
},
732743
overrides: {

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,7 @@ const WorkflowContent = React.memo(
992992
subBlockValues
993993
)
994994
usePanelEditorStore.getState().setCurrentBlockId(id)
995+
if (type === 'note') usePanelStore.getState().setIsMobilePanelOpen(false)
995996
},
996997
[
997998
collaborativeBatchAddBlocks,
@@ -1533,6 +1534,7 @@ const WorkflowContent = React.memo(
15331534
requestNoteRename(block.id)
15341535
return
15351536
}
1537+
usePanelStore.getState().setIsMobilePanelOpen(true)
15361538
usePanelEditorStore.getState().setCurrentBlockId(block.id)
15371539
usePanelEditorStore.getState().triggerRename()
15381540
}, [contextMenuBlocks])

0 commit comments

Comments
 (0)