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,6 +1,7 @@
'use client'

import { useCallback, useEffect, useRef } from 'react'
import { isUserSuppliedToolParam } from '@/lib/workflows/tool-input/param-visibility'
import {
buildToolSubBlockId,
resolveToolParamSync,
Expand Down Expand Up @@ -122,8 +123,10 @@ export function ToolSubBlockRenderer({
pushParamValueToStore(toolParamValue)
}, [toolParamValue, pushParamValueToStore])

const visibility = subBlock.paramVisibility ?? 'user-or-llm'
const isOptionalForUser = visibility !== 'user-only'
// Shared with the fork-sync gate so "is this the user's to fill?" is answered the same way
// in the editor and when a sync decides whether a blank value blocks. `required` itself
// stays for the field below to resolve in its own value context.
const isOptionalForUser = !isUserSuppliedToolParam(subBlock)

const config = {
...subBlock,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @vitest-environment node
*/
import { describe, expect, it } from 'vitest'
import { dependentFieldNoun } from '@/ee/workspace-forking/components/fork-sync/dependent-field-noun'

describe('dependentFieldNoun', () => {
it('strips a leading imperative verb so copy does not stutter', () => {
// The defect this exists to prevent: `Select ${title.toLowerCase()}` on a title that
// already reads as an instruction rendered "Select select issue".
expect(dependentFieldNoun('Select Issue')).toBe('issue')
expect(dependentFieldNoun('Select Project')).toBe('project')
expect(dependentFieldNoun('Choose Document')).toBe('document')
expect(dependentFieldNoun('Pick a Table')).toBe('a table')
})

it('leaves a title that merely starts with those letters alone', () => {
// The trailing `\s+` is what separates the verb from a word that begins with it.
expect(dependentFieldNoun('Selected Files')).toBe('selected files')
expect(dependentFieldNoun('Selection')).toBe('selection')
})

it('falls back to the whole title when stripping would leave nothing', () => {
// A bare verb has no noun to extract; an empty result would render "Select " and
// "No found".
expect(dependentFieldNoun('Select')).toBe('select')
expect(dependentFieldNoun('Select ')).toBe('select ')
})

it('passes a plain noun through lowercased', () => {
expect(dependentFieldNoun('Label')).toBe('label')
expect(dependentFieldNoun('Conflict Column')).toBe('conflict column')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Leading imperative verb on a field title. Titles are labels, and some already read as an
* instruction ("Select Issue", "Choose Project"), so composing surrounding copy onto them
* verbatim produces "Select select issue". The trailing `\s+` is load-bearing: it stops
* "Selected Files" and "Selection" from being mangled into "ed Files" / "ion".
*/
const LEADING_IMPERATIVE_VERB = /^(?:select|choose|pick)\s+/i

/**
* The bare noun of a dependent field's title, for copy that supplies its own verb
* ("Select {noun}", "Search {noun}...", "No {noun} found").
*
* Falls back to the whole title when stripping would leave nothing — a title that is only a
* verb has no noun to extract, and an empty noun would render "Select " and "No found".
*/
export function dependentFieldNoun(title: string): string {
const stripped = title.replace(LEADING_IMPERATIVE_VERB, '').trim()
return (stripped || title).toLowerCase()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { useMemo } from 'react'
import { ChipCombobox, type ComboboxOption, Loader } from '@sim/emcn'
import { dependentFieldNoun } from '@/ee/workspace-forking/components/fork-sync/dependent-field-noun'
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
import { useSelectorOptions } from '@/hooks/selectors/use-selector-query'

Expand Down Expand Up @@ -46,6 +47,8 @@ export function DependentFieldSelector({
[options]
)

const noun = dependentFieldNoun(title)

if (isLoading && enabled) {
return (
<div className='flex h-[30px] items-center gap-2 rounded-lg border border-[var(--border-1)] bg-[var(--surface-5)] px-2 text-[var(--text-muted)] text-small dark:bg-[var(--surface-4)]'>
Expand All @@ -62,10 +65,10 @@ export function DependentFieldSelector({
value={value || undefined}
onChange={(next) => onChange(next)}
searchable
searchPlaceholder={`Search ${title.toLowerCase()}...`}
placeholder={`Select ${title.toLowerCase()}`}
searchPlaceholder={`Search ${noun}...`}
placeholder={`Select ${noun}`}
disabled={!enabled}
emptyMessage={`No ${title.toLowerCase()} found`}
emptyMessage={`No ${noun} found`}
/>
)
}
Loading
Loading