Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/sim/app/(landing)/components/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface FooterItem {
}

const PRODUCT_LINKS: FooterItem[] = [
{ label: 'Mothership', href: 'https://docs.sim.ai', external: true },
{ label: 'Mothership', href: 'https://docs.sim.ai/mothership', external: true },
{ label: 'Workflows', href: 'https://docs.sim.ai', external: true },
{ label: 'Knowledge Base', href: 'https://docs.sim.ai/knowledgebase', external: true },
{ label: 'Tables', href: 'https://docs.sim.ai/tables', external: true },
Expand Down
36 changes: 27 additions & 9 deletions apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from '@/components/emcn'
import { Database, DatabaseX } from '@/components/emcn/icons'
import { SearchHighlight } from '@/components/ui/search-highlight'
import { cn } from '@/lib/core/utils/cn'
import { ADD_CONNECTOR_SEARCH_PARAM } from '@/lib/credentials/client-state'
import { ALL_TAG_SLOTS, type AllTagSlot, getFieldTypeForSlot } from '@/lib/knowledge/constants'
import type { DocumentSortField, SortOrder } from '@/lib/knowledge/documents/types'
Expand Down Expand Up @@ -920,19 +921,35 @@ export function KnowledgeBase({
const def = CONNECTOR_REGISTRY[connector.connectorType]
const ConnectorIcon = def?.icon
return (
<button
<Button
key={connector.id}
type='button'
variant='ghost'
size='sm'
onClick={() => setShowConnectorsModal(true)}
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)]'
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)]'
>
{connector.status === 'syncing' ? (
<Loader className='size-[14px]' animate />
) : (
ConnectorIcon && <ConnectorIcon className='size-[14px]' />
)}
{def?.name || connector.connectorType}
</button>
<span className='relative flex size-4 flex-shrink-0 items-center justify-center'>
{connector.status === 'syncing' ? (
<Loader className='size-[14px]' animate />
) : (
ConnectorIcon && <ConnectorIcon className='size-[14px]' />
)}
{connector.status !== 'active' && connector.status !== 'syncing' && (
<span
className={cn(
'-right-0.5 -top-0.5 absolute size-1.5 rounded-xs border border-[var(--surface-2)]',
connector.status === 'error'
? 'bg-[var(--text-error)]'
: connector.status === 'disabled'
? 'bg-[var(--caution)]'
: 'bg-[var(--text-muted)]'
)}
/>
)}
</span>
<span className='truncate'>{def?.name || connector.connectorType}</span>
</Button>
)
})}
</>
Expand Down Expand Up @@ -1317,6 +1334,7 @@ export function KnowledgeBase({
connectors={connectors}
isLoading={isLoadingConnectors}
canEdit={userPermissions.canEdit}
className='mt-0'
/>
</ModalBody>
</ModalContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,23 +240,23 @@ export function AddConnectorModal({
: `Configure the ${connectorConfig?.name} connector settings`}
</ModalDescription>

<ModalBody>
<ModalBody className='pb-3'>
{step === 'select-type' ? (
<div className='flex flex-col gap-2'>
<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)]'>
<div className='flex min-h-0 flex-col gap-2'>
<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)]'>
<Search
className='size-[14px] flex-shrink-0 text-[var(--text-tertiary)]'
className='size-[14px] flex-shrink-0 text-[var(--text-icon)]'
strokeWidth={2}
/>
<Input
placeholder='Search sources...'
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
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'
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'
/>
</div>
<div className='min-h-[400px] overflow-y-auto'>
<div className='flex flex-col gap-0.5'>
<div className='min-h-[400px] overflow-y-auto [scrollbar-gutter:stable]'>
<div className='flex flex-col gap-0.5 pr-1'>
{filteredEntries.map(([type, config]) => (
<ConnectorTypeCard
key={type}
Expand All @@ -265,7 +265,7 @@ export function AddConnectorModal({
/>
))}
{filteredEntries.length === 0 && (
<div className='py-4 text-center text-[var(--text-muted)] text-sm'>
<div className='rounded-lg bg-[var(--surface-3)] px-3 py-8 text-center text-[var(--text-muted)] text-small'>
{CONNECTOR_ENTRIES.length === 0
? 'No connectors available.'
: `No sources found matching "${searchTerm}"`}
Expand All @@ -276,7 +276,6 @@ export function AddConnectorModal({
</div>
) : connectorConfig ? (
<div className='flex flex-col gap-3'>
{/* Auth: API key input or OAuth credential selection */}
{isApiKeyMode ? (
<div className='flex flex-col gap-2'>
<Label>
Expand Down Expand Up @@ -336,7 +335,6 @@ export function AddConnectorModal({
</div>
)}

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

Expand All @@ -357,13 +355,14 @@ export function AddConnectorModal({
{field.description && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<button
<Button
type='button'
className='flex size-[14px] cursor-help items-center justify-center text-[var(--text-muted)] transition-colors hover-hover:text-[var(--text-secondary)]'
variant='ghost'
className='flex size-[14px] cursor-help items-center justify-center p-0 text-[var(--text-muted)] transition-colors hover-hover:text-[var(--text-secondary)]'
aria-label={`About ${field.title}`}
>
<Info className='size-[12px]' />
</button>
</Button>
</Tooltip.Trigger>
<Tooltip.Content side='top'>{field.description}</Tooltip.Content>
</Tooltip.Root>
Expand All @@ -372,13 +371,14 @@ export function AddConnectorModal({
{hasCanonicalPair && canonicalId && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<button
<Button
type='button'
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)]'
variant='ghost'
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)]'
onClick={() => toggleCanonicalMode(canonicalId)}
>
<ArrowLeftRight className='size-[12px]' />
</button>
</Button>
</Tooltip.Trigger>
<Tooltip.Content side='top'>
{field.mode === 'basic'
Expand Down Expand Up @@ -429,48 +429,50 @@ export function AddConnectorModal({
)
})}

{/* Tag definitions (opt-out) */}
{connectorConfig.tagDefinitions && connectorConfig.tagDefinitions.length > 0 && (
<div className='flex flex-col gap-2'>
<Label>Metadata Tags</Label>
{connectorConfig.tagDefinitions.map((tagDef) => (
<div
key={tagDef.id}
role='checkbox'
aria-checked={!disabledTagIds.has(tagDef.id)}
tabIndex={0}
className='flex cursor-pointer items-center gap-2 rounded-sm p-0.5 text-small'
onClick={() => toggleTagDefinition(tagDef.id)}
onKeyDown={(event) => {
if (event.target !== event.currentTarget) return
handleKeyboardActivation(event, () => toggleTagDefinition(tagDef.id))
}}
>
<Checkbox
checked={!disabledTagIds.has(tagDef.id)}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) => {
setDisabledTagIds((prev) => {
const next = new Set(prev)
if (checked) {
next.delete(tagDef.id)
} else {
next.add(tagDef.id)
}
return next
})
<div className='flex flex-col gap-0.5 rounded-lg border border-[var(--border-muted)] bg-[var(--surface-2)] p-1'>
{connectorConfig.tagDefinitions.map((tagDef) => (
<div
key={tagDef.id}
role='checkbox'
aria-checked={!disabledTagIds.has(tagDef.id)}
tabIndex={0}
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)]'
onClick={() => toggleTagDefinition(tagDef.id)}
onKeyDown={(event) => {
if (event.target !== event.currentTarget) return
handleKeyboardActivation(event, () => toggleTagDefinition(tagDef.id))
}}
/>
<span className='text-[var(--text-primary)]'>{tagDef.displayName}</span>
<span className='text-[var(--text-muted)] text-xs'>
({tagDef.fieldType})
</span>
</div>
))}
>
<Checkbox
checked={!disabledTagIds.has(tagDef.id)}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) => {
setDisabledTagIds((prev) => {
const next = new Set(prev)
if (checked) {
next.delete(tagDef.id)
} else {
next.add(tagDef.id)
}
return next
})
}}
/>
<span className='min-w-0 flex-1 truncate text-[var(--text-primary)]'>
{tagDef.displayName}
</span>
<span className='flex-shrink-0 text-[var(--text-muted)] text-xs'>
{tagDef.fieldType}
</span>
</div>
))}
</div>
</div>
)}

{/* Sync interval */}
<div className='flex flex-col gap-2'>
<Label>Sync Frequency</Label>
<ButtonGroup
Expand Down Expand Up @@ -550,14 +552,14 @@ function ConnectorTypeCard({ config, onClick }: ConnectorTypeCardProps) {
return (
<button
type='button'
className='flex items-center gap-2.5 rounded-md px-2.5 py-2 text-left transition-colors hover-hover:bg-[var(--surface-3)]'
className='group flex items-center gap-2.5 rounded-lg p-2 text-left transition-colors hover-hover:bg-[var(--surface-active)]'
onClick={onClick}
>
<Icon className='size-[18px] flex-shrink-0' />
<div className='flex min-w-0 flex-col gap-[1px]'>
<span className='truncate font-medium text-[var(--text-primary)] text-small'>
{config.name}
</span>
<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)]'>
<Icon className='size-[18px] text-[var(--text-secondary)]' />
</div>
<div className='flex min-w-0 flex-1 flex-col gap-[1px]'>
<span className='truncate text-[14px] text-[var(--text-body)]'>{config.name}</span>
<span className='truncate text-[var(--text-muted)] text-xs'>{config.description}</span>
</div>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function ConnectorSelectorField({

if (isLoading && isEnabled) {
return (
<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'>
<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'>
<Loader className='size-3.5' animate />
Loading…
</div>
Expand All @@ -84,6 +84,7 @@ export function ConnectorSelectorField({
return (
<Combobox
multiSelect
size='sm'
options={comboboxOptions}
multiSelectValues={multiValues}
onMultiSelectChange={(values) => onChange(values)}
Expand All @@ -97,13 +98,15 @@ export function ConnectorSelectorField({
: field.placeholder || `Select ${field.title.toLowerCase()}`
}
disabled={disabled || !credentialId || !depsResolved}
emptyMessage={`No ${field.title.toLowerCase()} found`}
/>
)
}

const singleValue = Array.isArray(value) ? value[0] : value
return (
<Combobox
size='sm'
options={comboboxOptions}
value={singleValue || undefined}
onChange={(next) => onChange(next)}
Expand All @@ -117,6 +120,7 @@ export function ConnectorSelectorField({
: field.placeholder || `Select ${field.title.toLowerCase()}`
}
disabled={disabled || !credentialId || !depsResolved}
emptyMessage={`No ${field.title.toLowerCase()} found`}
/>
)
}
Expand Down
Loading
Loading