Skip to content

Commit c0db9de

Browse files
fix(ui): Focus first text input by default (#4134)
* Auto-focus input boxes for modals and copilot * Fix focus in emcn modal * Fix integrations manager focus * Change modal tabs to auto focus on first text input * Auto-focus mothership task chats --------- Co-authored-by: Theodore Li <theo@sim.ai>
1 parent 7491d70 commit c0db9de

File tree

15 files changed

+174
-69
lines changed

15 files changed

+174
-69
lines changed

apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ export function UserInput({
361361
} catch {
362362
// Invalid JSON — ignore
363363
}
364+
textareaRef.current?.focus()
364365
return
365366
}
366367
const resourceJson = e.dataTransfer.getData(SIM_RESOURCE_DRAG_TYPE)
@@ -374,11 +375,13 @@ export function UserInput({
374375
} catch {
375376
// Invalid JSON — ignore
376377
}
378+
textareaRef.current?.focus()
377379
return
378380
}
379381
filesRef.current.handleDrop(e)
382+
requestAnimationFrame(() => textareaRef.current?.focus())
380383
},
381-
[handleResourceSelect]
384+
[handleResourceSelect, textareaRef]
382385
)
383386

384387
const handleDragEnter = useCallback((e: React.DragEvent) => {
@@ -407,10 +410,11 @@ export function UserInput({
407410
}, [isSending, textareaRef])
408411

409412
useEffect(() => {
410-
if (isInitialView) {
413+
const raf = window.requestAnimationFrame(() => {
411414
textareaRef.current?.focus()
412-
}
413-
}, [isInitialView, textareaRef])
415+
})
416+
return () => window.cancelAnimationFrame(raf)
417+
}, [textareaRef])
414418

415419
const handleContainerClick = useCallback(
416420
(e: React.MouseEvent<HTMLDivElement>) => {

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/rename-document-modal/rename-document-modal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ export function RenameDocumentModal({
9494
placeholder='Enter document name'
9595
className={cn(error && 'border-[var(--text-error)]')}
9696
disabled={isSubmitting}
97-
autoFocus
9897
maxLength={255}
9998
autoComplete='off'
10099
autoCorrect='off'

apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ export function ScheduleModal({ open, onOpenChange, workspaceId, schedule }: Sch
324324
}}
325325
placeholder='e.g., Daily report generation'
326326
className='h-9'
327-
autoFocus
328327
autoComplete='off'
329328
/>
330329
</div>

apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/components/create-api-key-modal/create-api-key-modal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ export function CreateApiKeyModal({
168168
}}
169169
placeholder='e.g., Development, Production'
170170
className='h-9'
171-
autoFocus
172171
name='api_key_label'
173172
autoComplete='off'
174173
autoCorrect='off'

apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,6 @@ export function BYOK() {
354354
}}
355355
placeholder={PROVIDERS.find((p) => p.id === editingProvider)?.placeholder}
356356
className='h-9 pr-9'
357-
autoFocus
358357
name='byok_api_key'
359358
autoComplete='off'
360359
autoCorrect='off'

apps/sim/app/workspace/[workspaceId]/settings/components/copilot/copilot.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,6 @@ export function Copilot() {
279279
}}
280280
placeholder='e.g., Development, Production'
281281
className='h-9'
282-
autoFocus
283282
/>
284283
{createError && (
285284
<p className='text-[var(--text-error)] text-small leading-tight'>{createError}</p>

apps/sim/app/workspace/[workspaceId]/settings/components/inbox/inbox-enable-toggle.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ export function InboxEnableToggle() {
9595
onChange={(e) => setEnableUsername(e.target.value)}
9696
placeholder='e.g., acme'
9797
className='h-9'
98-
autoFocus
9998
/>
10099
<p className='text-[var(--text-muted)] text-caption'>
101100
Leave blank for an auto-generated address.

apps/sim/app/workspace/[workspaceId]/settings/components/inbox/inbox-settings-tab.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ export function InboxSettingsTab() {
250250
}}
251251
placeholder='user@example.com'
252252
className='h-9'
253-
autoFocus
254253
/>
255254
</div>
256255
<div className='flex flex-col gap-1'>
@@ -304,7 +303,6 @@ export function InboxSettingsTab() {
304303
}}
305304
placeholder='e.g., new-acme'
306305
className='h-9'
307-
autoFocus
308306
/>
309307
{editAddressError && (
310308
<p className='text-[var(--text-error)] text-small leading-tight'>

apps/sim/app/workspace/[workspaceId]/settings/components/integrations/integrations-manager.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
Badge,
1111
Button,
1212
Combobox,
13+
focusFirstTextInputIn,
1314
Input,
1415
Label,
1516
Modal,
@@ -84,6 +85,15 @@ export function IntegrationsManager() {
8485
const [selectedDescriptionDraft, setSelectedDescriptionDraft] = useState('')
8586
const [selectedDisplayNameDraft, setSelectedDisplayNameDraft] = useState('')
8687
const [createStep, setCreateStep] = useState<1 | 2>(1)
88+
const createModalContentRef = useRef<HTMLDivElement>(null)
89+
90+
useEffect(() => {
91+
if (!showCreateModal || createStep !== 2) return
92+
const id = window.setTimeout(() => {
93+
focusFirstTextInputIn(createModalContentRef.current)
94+
}, 0)
95+
return () => window.clearTimeout(id)
96+
}, [showCreateModal, createStep])
8797
const [serviceSearch, setServiceSearch] = useState('')
8898
const [copyIdSuccess, setCopyIdSuccess] = useState(false)
8999
const [credentialToDelete, setCredentialToDelete] = useState<WorkspaceCredential | null>(null)
@@ -769,7 +779,7 @@ export function IntegrationsManager() {
769779
if (!open) resetCreateForm()
770780
}}
771781
>
772-
<ModalContent size='md'>
782+
<ModalContent size='md' ref={createModalContentRef}>
773783
{createStep === 1 ? (
774784
<>
775785
<ModalHeader>Connect Integration</ModalHeader>
@@ -785,7 +795,6 @@ export function IntegrationsManager() {
785795
value={serviceSearch}
786796
onChange={(e) => setServiceSearch(e.target.value)}
787797
className='h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
788-
autoFocus
789798
/>
790799
</div>
791800
<div className='flex max-h-[320px] flex-col overflow-y-auto'>
@@ -916,7 +925,6 @@ export function IntegrationsManager() {
916925
autoComplete='off'
917926
data-lpignore='true'
918927
className='mt-1.5'
919-
autoFocus
920928
/>
921929
</div>
922930
<div>
@@ -1053,7 +1061,6 @@ export function IntegrationsManager() {
10531061
'min-h-[120px] resize-none border-0 font-mono text-[12px]',
10541062
saDragActive && 'opacity-30'
10551063
)}
1056-
autoFocus
10571064
/>
10581065
</div>
10591066
<div className='mt-1.5'>

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,17 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
439439
return () => window.removeEventListener('mothership-send-message', handler)
440440
}, [setActiveTab, copilotSendMessage])
441441

442+
useEffect(() => {
443+
if (activeTab !== 'copilot') return
444+
const id = window.setTimeout(() => {
445+
const textarea = document.querySelector<HTMLTextAreaElement>(
446+
"[data-tab-content='copilot'] textarea"
447+
)
448+
textarea?.focus()
449+
}, 0)
450+
return () => window.clearTimeout(id)
451+
}, [activeTab])
452+
442453
/**
443454
* Handles tab click events
444455
*/

0 commit comments

Comments
 (0)