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
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export const WORKFLOW_SEARCH_HIGHLIGHT_CLASS =
'rounded-sm bg-orange-400 shadow-[3px_0_0_#fb923c,-3px_0_0_#fb923c]'
export const WORKFLOW_SEARCH_HIGHLIGHT_CLASS = 'rounded-sm bg-orange-400 font-normal text-inherit'
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Info } from 'lucide-react'
import { Checkbox, Label, Tooltip } from '@/components/emcn'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import type { ActiveSearchTarget } from '@/stores/panel/editor/store'

interface CheckboxListOption {
label: string
Expand All @@ -11,18 +14,23 @@ interface CheckboxListOption {

interface CheckboxListProps {
blockId: string
subBlockId: string
options: CheckboxListOption[]
isPreview?: boolean
subBlockValues?: Record<string, any>
disabled?: boolean
activeSearchTarget?: ActiveSearchTarget | null
}

interface CheckboxItemProps {
blockId: string
subBlockId: string
option: CheckboxListOption
index: number
isPreview: boolean
subBlockValues?: Record<string, any>
disabled: boolean
activeSearchTarget?: ActiveSearchTarget | null
}

/**
Expand All @@ -33,8 +41,24 @@ interface CheckboxItemProps {
* case we fall back to `option.defaultChecked` for the displayed state. Any
* explicit boolean (including `false`) takes precedence over the default.
*/
function CheckboxItem({ blockId, option, isPreview, subBlockValues, disabled }: CheckboxItemProps) {
function CheckboxItem({
blockId,
subBlockId,
option,
index,
isPreview,
subBlockValues,
disabled,
activeSearchTarget,
}: CheckboxItemProps) {
const [storeValue, setStoreValue] = useSubBlockValue<boolean>(blockId, option.id)
const workflowSearchHighlight = getWorkflowSearchLabelHighlight({
activeSearchTarget,
blockId,
subBlockId,
valuePath: ['options', index],
label: option.label,
})

const previewValue = isPreview && subBlockValues ? subBlockValues[option.id]?.value : undefined
const rawValue = isPreview ? previewValue : storeValue
Expand All @@ -58,7 +82,7 @@ function CheckboxItem({ blockId, option, isPreview, subBlockValues, disabled }:
htmlFor={`${blockId}-${option.id}`}
className='cursor-pointer font-medium font-sans text-[var(--text-primary)] text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50'
>
{option.label}
{formatDisplayText(option.label, { workflowSearchHighlight })}
</Label>
{option.description && (
<Tooltip.Root>
Expand All @@ -76,21 +100,26 @@ function CheckboxItem({ blockId, option, isPreview, subBlockValues, disabled }:

export function CheckboxList({
blockId,
subBlockId,
options,
isPreview = false,
subBlockValues,
disabled = false,
activeSearchTarget,
}: CheckboxListProps) {
return (
<div className='flex flex-col gap-y-2.5 pt-1'>
{options.map((option) => (
{options.map((option, index) => (
<CheckboxItem
key={option.id}
blockId={blockId}
subBlockId={subBlockId}
option={option}
index={index}
isPreview={isPreview}
subBlockValues={subBlockValues}
disabled={disabled}
activeSearchTarget={activeSearchTarget}
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@ import {
SYSTEM_REFERENCE_PREFIXES,
splitReferenceSegment,
} from '@/lib/workflows/sanitization/references'
import { WORKFLOW_SEARCH_HIGHLIGHT_CLASS } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/constants'
import {
checkEnvVarTrigger,
EnvVarDropdown,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown'
import {
getValidWorkflowSearchRange,
type WorkflowSearchTextHighlight,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
import {
checkTagTrigger,
TagDropdown,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { getActiveWorkflowSearchHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import type { WandControlHandlers } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block'
import { restoreCursorAfterInsertion } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/utils'
Expand All @@ -41,6 +47,7 @@ import { createEnvVarPattern, createReferencePattern } from '@/executor/utils/re
import { useTagSelection } from '@/hooks/kb/use-tag-selection'
import { createShouldHighlightEnvVar, useAvailableEnvVarKeys } from '@/hooks/use-available-env-vars'
import { useCodeUndoRedo } from '@/hooks/use-code-undo-redo'
import type { ActiveSearchTarget } from '@/stores/panel/editor/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'

const logger = createLogger('Code')
Expand Down Expand Up @@ -80,6 +87,16 @@ const applyDarkModeTokenStyling = (highlightedCode: string): string => {
return highlightedCode
}

const WORKFLOW_SEARCH_MATCH_PLACEHOLDER = '__WORKFLOW_SEARCH_MATCH__'

const escapeHtml = (value: string): string =>
value
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;')

/**
* Type definition for code placeholders during syntax highlighting.
*/
Expand All @@ -99,11 +116,20 @@ interface CodePlaceholder {
const createHighlightFunction = (
effectiveLanguage: 'javascript' | 'python' | 'json',
shouldHighlightReference: (part: string) => boolean,
shouldHighlightEnvVar: (varName: string) => boolean
shouldHighlightEnvVar: (varName: string) => boolean,
workflowSearchHighlight?: WorkflowSearchTextHighlight | null
) => {
return (codeToHighlight: string): string => {
const placeholders: CodePlaceholder[] = []
let processedCode = codeToHighlight
const workflowSearchRange = getValidWorkflowSearchRange(
codeToHighlight,
workflowSearchHighlight
)

if (workflowSearchRange) {
processedCode = `${codeToHighlight.slice(0, workflowSearchRange.start)}${WORKFLOW_SEARCH_MATCH_PLACEHOLDER}${codeToHighlight.slice(workflowSearchRange.end)}`
}

processedCode = processedCode.replace(createEnvVarPattern(), (match) => {
const varName = match.slice(2, -2).trim()
Expand Down Expand Up @@ -144,6 +170,14 @@ const createHighlightFunction = (
}
})

if (workflowSearchRange) {
const matchText = codeToHighlight.slice(workflowSearchRange.start, workflowSearchRange.end)
highlightedCode = highlightedCode.replace(
WORKFLOW_SEARCH_MATCH_PLACEHOLDER,
`<mark class="${WORKFLOW_SEARCH_HIGHLIGHT_CLASS}">${escapeHtml(matchText)}</mark>`
)
}

return highlightedCode
}
}
Expand Down Expand Up @@ -178,6 +212,7 @@ interface CodeProps {
wandControlRef?: React.MutableRefObject<WandControlHandlers | null>
/** Whether to hide the internal wand button (controlled by parent) */
hideInternalWand?: boolean
activeSearchTarget?: ActiveSearchTarget | null
}

export const Code = memo(function Code({
Expand All @@ -197,6 +232,7 @@ export const Code = memo(function Code({
wandConfig,
wandControlRef,
hideInternalWand = false,
activeSearchTarget,
}: CodeProps) {
const params = useParams()
const workspaceId = params.workspaceId as string
Expand Down Expand Up @@ -641,11 +677,21 @@ export const Code = memo(function Code({
() => createShouldHighlightEnvVar(availableEnvVars),
[availableEnvVars]
)
const workflowSearchHighlight = getActiveWorkflowSearchHighlight({
activeSearchTarget,
subBlockId,
valuePath: [],
})

const highlightCode = useMemo(
() =>
createHighlightFunction(effectiveLanguage, shouldHighlightReference, shouldHighlightEnvVar),
[effectiveLanguage, shouldHighlightReference, shouldHighlightEnvVar]
createHighlightFunction(
effectiveLanguage,
shouldHighlightReference,
shouldHighlightEnvVar,
workflowSearchHighlight
),
[effectiveLanguage, shouldHighlightReference, shouldHighlightEnvVar, workflowSearchHighlight]
)

const handleValueChange = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import { cn } from '@/lib/core/utils/cn'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
import { SubBlockInputController } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sub-block-input-controller'
import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { getDependsOnFields } from '@/blocks/utils'
import { usePermissionConfig } from '@/hooks/use-permission-config'
import { getProviderFromModel } from '@/providers/utils'
import type { ActiveSearchTarget } from '@/stores/panel/editor/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
Expand Down Expand Up @@ -68,6 +70,7 @@ interface ComboBoxProps {
) => Promise<{ label: string; id: string } | null>
/** Field dependencies that trigger option refetch when changed */
dependsOn?: SubBlockConfig['dependsOn']
activeSearchTarget?: ActiveSearchTarget | null
}

export const ComboBox = memo(function ComboBox({
Expand All @@ -84,6 +87,7 @@ export const ComboBox = memo(function ComboBox({
fetchOptions,
fetchOptionById,
dependsOn,
activeSearchTarget,
}: ComboBoxProps) {
// Hooks and context
const [storeValue, setStoreValue] = useSubBlockValue<string>(blockId, subBlockId)
Expand Down Expand Up @@ -449,18 +453,26 @@ export const ComboBox = memo(function ComboBox({
const overlayContent = useMemo(() => {
const SelectedIcon = selectedOptionIcon
const displayLabel = inputValue
const workflowSearchHighlight = getWorkflowSearchLabelHighlight({
activeSearchTarget,
blockId,
subBlockId,
valuePath: [],
label: displayLabel,
})
return (
<div className='flex w-full items-center truncate [scrollbar-width:none]'>
{SelectedIcon && <SelectedIcon className='mr-2 size-3 flex-shrink-0' />}
<div className='truncate'>
{formatDisplayText(displayLabel, {
accessiblePrefixes,
highlightAll: !accessiblePrefixes,
workflowSearchHighlight,
})}
</div>
</div>
)
}, [inputValue, accessiblePrefixes, selectedOption, selectedOptionIcon])
}, [activeSearchTarget, blockId, inputValue, accessiblePrefixes, selectedOptionIcon, subBlockId])

const ctrlOnChangeRef = useRef<
((e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void) | null
Expand Down
Loading
Loading