Skip to content

Commit 5c19344

Browse files
authored
improvement(kb-connectors): align connector UI surfaces (#4728)
1 parent 500f35a commit 5c19344

8 files changed

Lines changed: 249 additions & 161 deletions

File tree

apps/sim/app/(landing)/components/footer/footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface FooterItem {
1414
}
1515

1616
const PRODUCT_LINKS: FooterItem[] = [
17-
{ label: 'Mothership', href: 'https://docs.sim.ai', external: true },
17+
{ label: 'Mothership', href: 'https://docs.sim.ai/mothership', external: true },
1818
{ label: 'Workflows', href: 'https://docs.sim.ai', external: true },
1919
{ label: 'Knowledge Base', href: 'https://docs.sim.ai/knowledgebase', external: true },
2020
{ label: 'Tables', href: 'https://docs.sim.ai/tables', external: true },

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from '@/components/emcn'
2929
import { Database, DatabaseX } from '@/components/emcn/icons'
3030
import { SearchHighlight } from '@/components/ui/search-highlight'
31+
import { cn } from '@/lib/core/utils/cn'
3132
import { ADD_CONNECTOR_SEARCH_PARAM } from '@/lib/credentials/client-state'
3233
import { ALL_TAG_SLOTS, type AllTagSlot, getFieldTypeForSlot } from '@/lib/knowledge/constants'
3334
import type { DocumentSortField, SortOrder } from '@/lib/knowledge/documents/types'
@@ -920,19 +921,35 @@ export function KnowledgeBase({
920921
const def = CONNECTOR_REGISTRY[connector.connectorType]
921922
const ConnectorIcon = def?.icon
922923
return (
923-
<button
924+
<Button
924925
key={connector.id}
925926
type='button'
927+
variant='ghost'
928+
size='sm'
926929
onClick={() => setShowConnectorsModal(true)}
927-
className='flex shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-[var(--text-secondary)] text-caption shadow-[inset_0_0_0_1px_var(--border)] transition-colors hover-hover:bg-[var(--surface-3)]'
930+
className='h-7 max-w-[180px] shrink-0 justify-start gap-1.5 rounded-lg border border-[var(--border-muted)] bg-[var(--surface-2)] px-2 text-[var(--text-secondary)] text-caption hover-hover:bg-[var(--surface-active)] hover-hover:text-[var(--text-primary)]'
928931
>
929-
{connector.status === 'syncing' ? (
930-
<Loader className='size-[14px]' animate />
931-
) : (
932-
ConnectorIcon && <ConnectorIcon className='size-[14px]' />
933-
)}
934-
{def?.name || connector.connectorType}
935-
</button>
932+
<span className='relative flex size-4 flex-shrink-0 items-center justify-center'>
933+
{connector.status === 'syncing' ? (
934+
<Loader className='size-[14px]' animate />
935+
) : (
936+
ConnectorIcon && <ConnectorIcon className='size-[14px]' />
937+
)}
938+
{connector.status !== 'active' && connector.status !== 'syncing' && (
939+
<span
940+
className={cn(
941+
'-right-0.5 -top-0.5 absolute size-1.5 rounded-xs border border-[var(--surface-2)]',
942+
connector.status === 'error'
943+
? 'bg-[var(--text-error)]'
944+
: connector.status === 'disabled'
945+
? 'bg-[var(--caution)]'
946+
: 'bg-[var(--text-muted)]'
947+
)}
948+
/>
949+
)}
950+
</span>
951+
<span className='truncate'>{def?.name || connector.connectorType}</span>
952+
</Button>
936953
)
937954
})}
938955
</>
@@ -1317,6 +1334,7 @@ export function KnowledgeBase({
13171334
connectors={connectors}
13181335
isLoading={isLoadingConnectors}
13191336
canEdit={userPermissions.canEdit}
1337+
className='mt-0'
13201338
/>
13211339
</ModalBody>
13221340
</ModalContent>

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -240,23 +240,23 @@ export function AddConnectorModal({
240240
: `Configure the ${connectorConfig?.name} connector settings`}
241241
</ModalDescription>
242242

243-
<ModalBody>
243+
<ModalBody className='pb-3'>
244244
{step === 'select-type' ? (
245-
<div className='flex flex-col gap-2'>
246-
<div className='flex items-center gap-2 rounded-lg border border-[var(--border)] bg-transparent px-2 py-[5px] transition-colors duration-100 dark:bg-[var(--surface-4)] dark:hover-hover:border-[var(--border-1)] dark:hover-hover:bg-[var(--surface-5)]'>
245+
<div className='flex min-h-0 flex-col gap-2'>
246+
<div className='flex items-center gap-2 rounded-lg border border-[var(--border)] bg-[var(--surface-2)] px-2 py-[5px] transition-colors duration-100 hover-hover:border-[var(--border-1)] hover-hover:bg-[var(--surface-3)]'>
247247
<Search
248-
className='size-[14px] flex-shrink-0 text-[var(--text-tertiary)]'
248+
className='size-[14px] flex-shrink-0 text-[var(--text-icon)]'
249249
strokeWidth={2}
250250
/>
251251
<Input
252252
placeholder='Search sources...'
253253
value={searchTerm}
254254
onChange={(e) => setSearchTerm(e.target.value)}
255-
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'
255+
className='h-auto flex-1 border-0 bg-transparent p-0 leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
256256
/>
257257
</div>
258-
<div className='min-h-[400px] overflow-y-auto'>
259-
<div className='flex flex-col gap-0.5'>
258+
<div className='min-h-[400px] overflow-y-auto [scrollbar-gutter:stable]'>
259+
<div className='flex flex-col gap-0.5 pr-1'>
260260
{filteredEntries.map(([type, config]) => (
261261
<ConnectorTypeCard
262262
key={type}
@@ -265,7 +265,7 @@ export function AddConnectorModal({
265265
/>
266266
))}
267267
{filteredEntries.length === 0 && (
268-
<div className='py-4 text-center text-[var(--text-muted)] text-sm'>
268+
<div className='rounded-lg bg-[var(--surface-3)] px-3 py-8 text-center text-[var(--text-muted)] text-small'>
269269
{CONNECTOR_ENTRIES.length === 0
270270
? 'No connectors available.'
271271
: `No sources found matching "${searchTerm}"`}
@@ -276,7 +276,6 @@ export function AddConnectorModal({
276276
</div>
277277
) : connectorConfig ? (
278278
<div className='flex flex-col gap-3'>
279-
{/* Auth: API key input or OAuth credential selection */}
280279
{isApiKeyMode ? (
281280
<div className='flex flex-col gap-2'>
282281
<Label>
@@ -336,7 +335,6 @@ export function AddConnectorModal({
336335
</div>
337336
)}
338337

339-
{/* Config fields */}
340338
{connectorConfig.configFields.map((field) => {
341339
if (!isFieldVisible(field)) return null
342340

@@ -357,13 +355,14 @@ export function AddConnectorModal({
357355
{field.description && (
358356
<Tooltip.Root>
359357
<Tooltip.Trigger asChild>
360-
<button
358+
<Button
361359
type='button'
362-
className='flex size-[14px] cursor-help items-center justify-center text-[var(--text-muted)] transition-colors hover-hover:text-[var(--text-secondary)]'
360+
variant='ghost'
361+
className='flex size-[14px] cursor-help items-center justify-center p-0 text-[var(--text-muted)] transition-colors hover-hover:text-[var(--text-secondary)]'
363362
aria-label={`About ${field.title}`}
364363
>
365364
<Info className='size-[12px]' />
366-
</button>
365+
</Button>
367366
</Tooltip.Trigger>
368367
<Tooltip.Content side='top'>{field.description}</Tooltip.Content>
369368
</Tooltip.Root>
@@ -372,13 +371,14 @@ export function AddConnectorModal({
372371
{hasCanonicalPair && canonicalId && (
373372
<Tooltip.Root>
374373
<Tooltip.Trigger asChild>
375-
<button
374+
<Button
376375
type='button'
377-
className='flex size-[18px] items-center justify-center rounded-[3px] text-[var(--text-muted)] transition-colors hover-hover:bg-[var(--surface-3)] hover-hover:text-[var(--text-secondary)]'
376+
variant='ghost'
377+
className='flex size-[18px] items-center justify-center rounded-[3px] p-0 text-[var(--text-muted)] transition-colors hover-hover:bg-[var(--surface-3)] hover-hover:text-[var(--text-secondary)]'
378378
onClick={() => toggleCanonicalMode(canonicalId)}
379379
>
380380
<ArrowLeftRight className='size-[12px]' />
381-
</button>
381+
</Button>
382382
</Tooltip.Trigger>
383383
<Tooltip.Content side='top'>
384384
{field.mode === 'basic'
@@ -429,48 +429,50 @@ export function AddConnectorModal({
429429
)
430430
})}
431431

432-
{/* Tag definitions (opt-out) */}
433432
{connectorConfig.tagDefinitions && connectorConfig.tagDefinitions.length > 0 && (
434433
<div className='flex flex-col gap-2'>
435434
<Label>Metadata Tags</Label>
436-
{connectorConfig.tagDefinitions.map((tagDef) => (
437-
<div
438-
key={tagDef.id}
439-
role='checkbox'
440-
aria-checked={!disabledTagIds.has(tagDef.id)}
441-
tabIndex={0}
442-
className='flex cursor-pointer items-center gap-2 rounded-sm p-0.5 text-small'
443-
onClick={() => toggleTagDefinition(tagDef.id)}
444-
onKeyDown={(event) => {
445-
if (event.target !== event.currentTarget) return
446-
handleKeyboardActivation(event, () => toggleTagDefinition(tagDef.id))
447-
}}
448-
>
449-
<Checkbox
450-
checked={!disabledTagIds.has(tagDef.id)}
451-
onClick={(e) => e.stopPropagation()}
452-
onCheckedChange={(checked) => {
453-
setDisabledTagIds((prev) => {
454-
const next = new Set(prev)
455-
if (checked) {
456-
next.delete(tagDef.id)
457-
} else {
458-
next.add(tagDef.id)
459-
}
460-
return next
461-
})
435+
<div className='flex flex-col gap-0.5 rounded-lg border border-[var(--border-muted)] bg-[var(--surface-2)] p-1'>
436+
{connectorConfig.tagDefinitions.map((tagDef) => (
437+
<div
438+
key={tagDef.id}
439+
role='checkbox'
440+
aria-checked={!disabledTagIds.has(tagDef.id)}
441+
tabIndex={0}
442+
className='flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-small transition-colors hover-hover:bg-[var(--surface-active)]'
443+
onClick={() => toggleTagDefinition(tagDef.id)}
444+
onKeyDown={(event) => {
445+
if (event.target !== event.currentTarget) return
446+
handleKeyboardActivation(event, () => toggleTagDefinition(tagDef.id))
462447
}}
463-
/>
464-
<span className='text-[var(--text-primary)]'>{tagDef.displayName}</span>
465-
<span className='text-[var(--text-muted)] text-xs'>
466-
({tagDef.fieldType})
467-
</span>
468-
</div>
469-
))}
448+
>
449+
<Checkbox
450+
checked={!disabledTagIds.has(tagDef.id)}
451+
onClick={(e) => e.stopPropagation()}
452+
onCheckedChange={(checked) => {
453+
setDisabledTagIds((prev) => {
454+
const next = new Set(prev)
455+
if (checked) {
456+
next.delete(tagDef.id)
457+
} else {
458+
next.add(tagDef.id)
459+
}
460+
return next
461+
})
462+
}}
463+
/>
464+
<span className='min-w-0 flex-1 truncate text-[var(--text-primary)]'>
465+
{tagDef.displayName}
466+
</span>
467+
<span className='flex-shrink-0 text-[var(--text-muted)] text-xs'>
468+
{tagDef.fieldType}
469+
</span>
470+
</div>
471+
))}
472+
</div>
470473
</div>
471474
)}
472475

473-
{/* Sync interval */}
474476
<div className='flex flex-col gap-2'>
475477
<Label>Sync Frequency</Label>
476478
<ButtonGroup
@@ -550,14 +552,14 @@ function ConnectorTypeCard({ config, onClick }: ConnectorTypeCardProps) {
550552
return (
551553
<button
552554
type='button'
553-
className='flex items-center gap-2.5 rounded-md px-2.5 py-2 text-left transition-colors hover-hover:bg-[var(--surface-3)]'
555+
className='group flex items-center gap-2.5 rounded-lg p-2 text-left transition-colors hover-hover:bg-[var(--surface-active)]'
554556
onClick={onClick}
555557
>
556-
<Icon className='size-[18px] flex-shrink-0' />
557-
<div className='flex min-w-0 flex-col gap-[1px]'>
558-
<span className='truncate font-medium text-[var(--text-primary)] text-small'>
559-
{config.name}
560-
</span>
558+
<div className='flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--surface-4)] transition-colors group-hover:bg-[var(--surface-5)]'>
559+
<Icon className='size-[18px] text-[var(--text-secondary)]' />
560+
</div>
561+
<div className='flex min-w-0 flex-1 flex-col gap-[1px]'>
562+
<span className='truncate text-[14px] text-[var(--text-body)]'>{config.name}</span>
561563
<span className='truncate text-[var(--text-muted)] text-xs'>{config.description}</span>
562564
</div>
563565
</button>

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connector-selector-field/connector-selector-field.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function ConnectorSelectorField({
7272

7373
if (isLoading && isEnabled) {
7474
return (
75-
<div className='flex items-center gap-2 rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 text-[var(--text-muted)] text-sm'>
75+
<div className='flex items-center gap-2 rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 text-[var(--text-muted)] text-caption'>
7676
<Loader className='size-3.5' animate />
7777
Loading…
7878
</div>
@@ -84,6 +84,7 @@ export function ConnectorSelectorField({
8484
return (
8585
<Combobox
8686
multiSelect
87+
size='sm'
8788
options={comboboxOptions}
8889
multiSelectValues={multiValues}
8990
onMultiSelectChange={(values) => onChange(values)}
@@ -97,13 +98,15 @@ export function ConnectorSelectorField({
9798
: field.placeholder || `Select ${field.title.toLowerCase()}`
9899
}
99100
disabled={disabled || !credentialId || !depsResolved}
101+
emptyMessage={`No ${field.title.toLowerCase()} found`}
100102
/>
101103
)
102104
}
103105

104106
const singleValue = Array.isArray(value) ? value[0] : value
105107
return (
106108
<Combobox
109+
size='sm'
107110
options={comboboxOptions}
108111
value={singleValue || undefined}
109112
onChange={(next) => onChange(next)}
@@ -117,6 +120,7 @@ export function ConnectorSelectorField({
117120
: field.placeholder || `Select ${field.title.toLowerCase()}`
118121
}
119122
disabled={disabled || !credentialId || !depsResolved}
123+
emptyMessage={`No ${field.title.toLowerCase()} found`}
120124
/>
121125
)
122126
}

0 commit comments

Comments
 (0)