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
6 changes: 3 additions & 3 deletions apps/docs/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8932,14 +8932,14 @@ export function ZohoDeskIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='0 0 24 24'
viewBox='-24 -6 308 308'
fill='none'
xmlns='http://www.w3.org/2000/svg'
aria-hidden='true'
>
<path
d='M12 2.75c-4.28 0-7.75 3.47-7.75 7.75v3.1A2.6 2.6 0 0 0 3 16.35v1.3A2.6 2.6 0 0 0 5.6 20.25h1.15a.9.9 0 0 0 .9-.9v-4.9a.9.9 0 0 0-.9-.9H6.05v-2.15a5.95 5.95 0 0 1 11.9 0v2.15h-.7a.9.9 0 0 0-.9.9v4.9c0 .17.05.33.13.47-.5.6-1.24.98-2.08.98h-1.02a1.4 1.4 0 0 0-1.31-.9h-1a1.4 1.4 0 0 0 0 2.8h1a1.4 1.4 0 0 0 1.31-.9h1.02c2.06 0 3.74-1.63 3.83-3.67a2.6 2.6 0 0 0 1.44-2.33v-1.3a2.6 2.6 0 0 0-1.25-2.22v-3.1c0-4.28-3.47-7.75-7.75-7.75Z'
fill='currentColor'
d='M22.3,293c-1.6,0-3.3-0.5-4.7-1.4c-2.8-1.8-4.2-5.1-3.7-8.4l24.3-149.3c1.3-8,5.4-15.2,11.5-20.4 c6.1-5.2,14-8.1,22-8.1h100.1l5.6-34.3H168c-4.2,0-8.1-1.8-10.8-5c-2.7-3.2-3.9-7.3-3.2-11.4l6.4-39.7c1.1-6.9,7-11.9,14-11.9h37.3 c10,0,19.4,4.4,25.9,12s9.3,17.6,7.7,27.5l-15,92.4c-0.7,4-3,7.6-6.5,9.8L140,196.5c-5.1,3.1-11.6,2.8-16.3-1l-39-31.1 c-3.7-2.9-4.3-8.3-1.3-11.9c2.9-3.7,8.3-4.3,11.9-1.3l37.5,29.9l81-50.2l14.8-91.1c0.8-4.9-0.6-9.9-3.8-13.7c-3.2-3.8-7.9-6-12.9-6 h-34.9l-5.5,34h9.3c4.2,0,8.1,1.8,10.8,5c2.7,3.2,3.9,7.3,3.2,11.4l-6.5,39.9c-1.1,6.9-7,11.9-14,11.9H71.7 c-8.4,0-15.4,6-16.8,14.3L33.6,267.5L63.5,249c2.2-1.4,4.8-2.1,7.5-2.1h109.5c8.4,0,15.4-6,16.8-14.3l6.8-41.9 c0.8-4.6,5.1-7.8,9.7-7c4.6,0.8,7.8,5.1,7,9.7l-6.8,41.9c-1.3,8-5.4,15.2-11.5,20.4c-6.1,5.2-14,8.1-22,8.1H71.7l-45,27.9 C25.4,292.6,23.8,293,22.3,293z'
fill='#089949'
/>
</svg>
)
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/en/integrations/zoho_desk.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"

<BlockInfoCard
type="zoho_desk"
color="#E42527"
color="#FFFFFF"
/>

{/* MANUAL-CONTENT-START:intro */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
import { useActiveSearchTarget } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/providers/active-search-target-provider'
import type { SubBlockConfig } from '@/blocks/types'
import { useKnowledgeBasesList } from '@/hooks/kb/use-knowledge'
import { useFolderMap } from '@/hooks/queries/folders'
import { fetchKnowledgeBase } from '@/hooks/queries/kb/knowledge'
import { collectDuplicateNames, disambiguateLabelByFolder } from '@/hooks/queries/utils/folder-tree'
import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys'

interface KnowledgeBaseSelectorProps {
Expand Down Expand Up @@ -43,6 +45,8 @@ export function KnowledgeBaseSelector({
error,
} = useKnowledgeBasesList(workspaceId)

const { data: knowledgeBaseFolders = {} } = useFolderMap(workspaceId, 'knowledge_base')

const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id)

const value = isPreview ? previewValue : storeValue
Expand Down Expand Up @@ -90,13 +94,32 @@ export function KnowledgeBaseSelector({
return Array.from(merged.values())
}, [knowledgeBases, selectedKnowledgeBaseQueries])

const options = useMemo<ComboboxOption[]>(() => {
return combinedKnowledgeBases.map((kb) => ({
label: kb.name,
value: kb.id,
icon: PackageSearchIcon,
}))
}, [combinedKnowledgeBases])
/**
* Display names, with the folder path appended when two knowledge bases share
* a name — otherwise the dropdown rows and the selected chips are
* indistinguishable from one another. Built in the same pass as the options so
* the chips and the dropdown can never disagree.
*/
const { options, labelById } = useMemo(() => {
const duplicateNames = collectDuplicateNames(combinedKnowledgeBases.map((kb) => kb.name))
const labelById = new Map<string, string>()
const options: ComboboxOption[] = combinedKnowledgeBases.map((kb) => {
const label = disambiguateLabelByFolder(
kb.name,
kb.folderId,
knowledgeBaseFolders,
duplicateNames
)
labelById.set(kb.id, label)
return { label, value: kb.id, icon: PackageSearchIcon }
})
return { options, labelById }
}, [combinedKnowledgeBases, knowledgeBaseFolders])

const labelOf = useCallback(
(kb: KnowledgeBaseData) => labelById.get(kb.id) ?? kb.name,
[labelById]
)

/**
* Compute selected knowledge bases for tag display
Expand Down Expand Up @@ -172,7 +195,7 @@ export function KnowledgeBaseSelector({
blockId,
subBlockId: subBlock.id,
valuePath: [index],
label: kb.name,
label: labelOf(kb),
})
return (
<div
Expand All @@ -181,14 +204,14 @@ export function KnowledgeBaseSelector({
>
<PackageSearchIcon className='mr-1 size-3 text-[var(--brand-knowledge)]' />
<span className='font-medium text-[var(--brand-knowledge)]'>
{formatDisplayText(kb.name, { workflowSearchHighlight })}
{formatDisplayText(labelOf(kb), { workflowSearchHighlight })}
</span>
{!disabled && !isPreview && (
<button
type='button'
onClick={() => handleRemoveKnowledgeBase(kb.id)}
className='ml-1 text-[color-mix(in_srgb,var(--brand-knowledge)_60%,transparent)] hover-hover:text-[var(--brand-knowledge)]'
aria-label={`Remove ${kb.name}`}
aria-label={`Remove ${labelOf(kb)}`}
>
<X className='size-3' />
</button>
Expand Down Expand Up @@ -220,11 +243,13 @@ export function KnowledgeBaseSelector({
blockId,
subBlockId: subBlock.id,
valuePath: [],
label: selectedKnowledgeBases[0].name,
label: labelOf(selectedKnowledgeBases[0]),
})
return workflowSearchHighlight ? (
<span className='truncate text-[var(--text-primary)]'>
{formatDisplayText(selectedKnowledgeBases[0].name, { workflowSearchHighlight })}
{formatDisplayText(labelOf(selectedKnowledgeBases[0]), {
workflowSearchHighlight,
})}
</span>
) : undefined
})()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useActiveSearchTarget } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/providers/active-search-target-provider'
import type { SubBlockConfig } from '@/blocks/types'
import { useFolderMap } from '@/hooks/queries/folders'
import { useTablesList } from '@/hooks/queries/tables'
import { collectDuplicateNames, disambiguateLabelByFolder } from '@/hooks/queries/utils/folder-tree'

interface TableSelectorProps {
blockId: string
Expand Down Expand Up @@ -45,15 +47,33 @@ export function TableSelector({
error,
} = useTablesList(isPreview || disabled ? undefined : workspaceId)

const { data: tableFolders = {} } = useFolderMap(
isPreview || disabled ? undefined : workspaceId,
'table'
)

const value = isPreview ? previewValue : storeValue
const tableId = typeof value === 'string' ? value : null

/**
* Two tables can share a name in different folders, so a colliding name is
* suffixed with its folder path. Table names are lowercased for display (the
* pre-existing styling here), and collisions are detected on that same
* lowercased form so `Leads` and `leads` — identical once displayed — are
* disambiguated too. The folder path keeps its authored casing.
*/
const options = useMemo<ComboboxOption[]>(() => {
const duplicateNames = collectDuplicateNames(tables.map((table) => table.name.toLowerCase()))
return tables.map((table) => ({
label: table.name.toLowerCase(),
label: disambiguateLabelByFolder(
table.name.toLowerCase(),
table.folderId,
tableFolders,
duplicateNames
),
value: table.id,
}))
}, [tables])
}, [tables, tableFolders])

const handleChange = useCallback(
(selectedValue: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,35 @@ export const MemoizedActionItem = memo(
prev.shortcut === next.shortcut
)

/**
* Right-aligned folder breadcrumb. All but the last segment collapse first so a
* deep path degrades to the immediate parent rather than truncating the whole
* trail. Renders nothing at the workspace root.
*/
function FolderPathSuffix({ folderPath }: { folderPath?: string[] }) {
if (!folderPath || folderPath.length === 0) return null
return (
<span className='ml-auto flex min-w-0 pl-2 text-[var(--text-subtle)] text-small'>
{folderPath.length > 1 && (
<>
<span className='min-w-0 truncate [flex-shrink:9999]'>
{folderPath.slice(0, -1).join(' / ')}
</span>
<span className='flex-shrink-0 whitespace-pre'> / </span>
</>
)}
<span className='min-w-0 truncate'>{folderPath[folderPath.length - 1]}</span>
</span>
)
}

/** Element-wise compare so a rebuilt-but-identical path array skips the re-render. */
function sameFolderPath(a?: string[], b?: string[]): boolean {
if (a === b) return true
if (a?.length !== b?.length) return false
return (a ?? []).every((segment, i) => segment === b?.[i])
}

export const MemoizedWorkflowItem = memo(
function WorkflowItem({
value,
Expand All @@ -101,29 +130,15 @@ export const MemoizedWorkflowItem = memo(
<span className='truncate'>{name}</span>
{isCurrent && <span className='flex-shrink-0 whitespace-pre'> (current)</span>}
</span>
{folderPath && folderPath.length > 0 && (
<span className='ml-auto flex min-w-0 pl-2 text-[var(--text-subtle)] text-small'>
{folderPath.length > 1 && (
<>
<span className='min-w-0 truncate [flex-shrink:9999]'>
{folderPath.slice(0, -1).join(' / ')}
</span>
<span className='flex-shrink-0 whitespace-pre'> / </span>
</>
)}
<span className='min-w-0 truncate'>{folderPath[folderPath.length - 1]}</span>
</span>
)}
<FolderPathSuffix folderPath={folderPath} />
</Command.Item>
)
},
(prev, next) =>
prev.value === next.value &&
prev.name === next.name &&
prev.isCurrent === next.isCurrent &&
(prev.folderPath === next.folderPath ||
(prev.folderPath?.length === next.folderPath?.length &&
(prev.folderPath ?? []).every((segment, i) => segment === next.folderPath?.[i])))
sameFolderPath(prev.folderPath, next.folderPath)
)

export const MemoizedFileItem = memo(
Expand All @@ -143,31 +158,17 @@ export const MemoizedFileItem = memo(
<div className='relative flex size-[16px] flex-shrink-0 items-center justify-center'>
<File className='size-[14px] text-[var(--text-icon)]' />
</div>
<span className='flex min-w-0 max-w-[75%] flex-shrink-0 font-base text-[var(--text-body)]'>
<span className='flex min-w-0 max-w-[75%] flex-shrink-0 text-[var(--text-body)]'>
<span className='truncate'>{name}</span>
</span>
{folderPath && folderPath.length > 0 && (
<span className='ml-auto flex min-w-0 pl-2 font-base text-[var(--text-subtle)] text-small'>
{folderPath.length > 1 && (
<>
<span className='min-w-0 truncate [flex-shrink:9999]'>
{folderPath.slice(0, -1).join(' / ')}
</span>
<span className='flex-shrink-0 whitespace-pre'> / </span>
</>
)}
<span className='min-w-0 truncate'>{folderPath[folderPath.length - 1]}</span>
</span>
)}
<FolderPathSuffix folderPath={folderPath} />
</Command.Item>
)
},
(prev, next) =>
prev.value === next.value &&
prev.name === next.name &&
(prev.folderPath === next.folderPath ||
(prev.folderPath?.length === next.folderPath?.length &&
(prev.folderPath ?? []).every((segment, i) => segment === next.folderPath?.[i])))
sameFolderPath(prev.folderPath, next.folderPath)
)

export const MemoizedTaskItem = memo(
Expand Down Expand Up @@ -253,18 +254,27 @@ export const MemoizedIconItem = memo(
onSelect,
name,
icon: Icon,
folderPath,
}: {
value: string
onSelect: () => void
name: string
icon: ComponentType<{ className?: string }>
folderPath?: string[]
}) {
return (
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
<Icon className='size-[16px] flex-shrink-0 text-[var(--text-icon)]' />
<span className='truncate text-[var(--text-body)]'>{name}</span>
<span className='flex min-w-0 max-w-[75%] flex-shrink-0 text-[var(--text-body)]'>
<span className='truncate'>{name}</span>
</span>
<FolderPathSuffix folderPath={folderPath} />
</Command.Item>
)
},
(prev, next) => prev.value === next.value && prev.name === next.name && prev.icon === next.icon
(prev, next) =>
prev.value === next.value &&
prev.name === next.name &&
prev.icon === next.icon &&
sameFolderPath(prev.folderPath, next.folderPath)
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import type {
ActionItem,
FileItem,
FolderedItem,
IntegrationSearchItem,
PageItem,
TaskItem,
Expand Down Expand Up @@ -343,19 +344,20 @@ function createIconGroup(
items,
onSelect,
}: {
items: TaskItem[]
onSelect: (item: TaskItem) => void
items: FolderedItem[]
onSelect: (item: FolderedItem) => void
}) {
if (items.length === 0) return null
return (
<Command.Group heading={heading} className={GROUP_HEADING_CLASSNAME}>
{items.map((item) => (
<MemoizedIconItem
key={item.id}
value={`${item.name} ${prefix}-${item.id}`}
value={`${item.name} ${item.folderPath?.join(' / ') ?? ''} ${prefix}-${item.id}`}
onSelect={() => onSelect(item)}
name={item.name}
icon={icon}
folderPath={item.folderPath}
/>
))}
</Command.Group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,21 +621,44 @@ export function SearchModal({
}, [isOnWorkflowPage, docs, deferredSearch])

const filteredTables = useMemo(
() => filterAndCap(tables, (t) => t.name, deferredSearch),
() =>
filterAndCap(
tables,
(t) => t.name,
deferredSearch,
(t) => t.folderPath?.join(' ')
),
[tables, deferredSearch]
)
const filteredFiles = useMemo(
() => filterAndCap(files, (f) => `${f.name} ${f.folderPath?.join(' ') ?? ''}`, deferredSearch),
() =>
filterAndCap(
files,
(f) => f.name,
deferredSearch,
(f) => f.folderPath?.join(' ')
),
[files, deferredSearch]
)
const filteredKnowledgeBases = useMemo(
() => filterAndCap(knowledgeBases, (kb) => kb.name, deferredSearch),
() =>
filterAndCap(
knowledgeBases,
(kb) => kb.name,
deferredSearch,
(kb) => kb.folderPath?.join(' ')
),
[knowledgeBases, deferredSearch]
)

const filteredWorkflows = useMemo(
() =>
filterAndCap(workflows, (w) => `${w.name} ${w.folderPath?.join(' ') ?? ''}`, deferredSearch),
filterAndCap(
workflows,
(w) => w.name,
deferredSearch,
(w) => w.folderPath?.join(' ')
),
Comment thread
waleedlatif1 marked this conversation as resolved.
[workflows, deferredSearch]
)
const filteredChats = useMemo(
Expand Down
Loading
Loading