Skip to content

Commit 0dbb2a6

Browse files
myxamediyarclaude
andcommitted
fix(video): pick fal settings from the active start frame input
The Reference Image is a basic upload / advanced reference pair, and the serializer sends only the active member. The fal settings fields counted a file left in the inactive member too, so switching to an empty advanced input kept showing the start frame endpoint's settings while the run called the prompt endpoint. Resolve the active member with `resolveActiveCanonicalValue`, and pass `__canonicalModes` to dropdown option functions as the editor already does for conditions, so a field's visibility and its options agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent b548760 commit 0dbb2a6

3 files changed

Lines changed: 72 additions & 9 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,19 @@ export const Dropdown = memo(function Dropdown({
164164
}, [multiSelect, value])
165165

166166
// Derived option lists read the block's own values (a model's valid reasoning efforts);
167-
// `dependsOn` already re-renders this control when one of those siblings changes.
167+
// `dependsOn` already re-renders this control when one of those siblings changes. They also get
168+
// `__canonicalModes`, as conditions do, so both read the same active member of a canonical pair.
168169
const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId)
169170
const blockValues = useSubBlockStore((state) =>
170171
activeWorkflowId ? state.workflowValues[activeWorkflowId]?.[blockId] : undefined
171172
)
172173
const evaluatedOptions = useMemo(() => {
173-
if (typeof options === 'function') return options({ values: blockValues ?? {} })
174-
return options ?? EMPTY_OPTIONS
175-
}, [options, blockValues])
174+
if (typeof options !== 'function') return options ?? EMPTY_OPTIONS
175+
const values = blockValues ?? {}
176+
return options({
177+
values: canonicalModes ? { ...values, __canonicalModes: canonicalModes } : values,
178+
})
179+
}, [options, blockValues, canonicalModes])
176180

177181
const reactiveOptions = useMemo(
178182
() =>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { evaluateSubBlockCondition } from '@/lib/workflows/subblocks/visibility'
3+
import { VideoGeneratorV3Block } from '@/blocks/blocks/video_generator'
4+
5+
const frame = { id: 'f', name: 'frame.png', url: '', size: 3, type: 'image/png', key: 'k' }
6+
7+
function aspectRatioField() {
8+
const field = VideoGeneratorV3Block.subBlocks.find((subBlock) => subBlock.id === 'falAspectRatio')
9+
if (!field) throw new Error('Video Generator must expose a fal aspect ratio field')
10+
return field
11+
}
12+
13+
function optionIds(values: Record<string, unknown>) {
14+
const { options } = aspectRatioField()
15+
if (typeof options !== 'function') throw new Error('Fal aspect ratio options must be derived')
16+
return options({ values }).map((option) => (typeof option === 'string' ? option : option.id))
17+
}
18+
19+
describe('Video Generator fal start frame', () => {
20+
// Happy Horse takes an aspect ratio from a prompt but not from a start frame.
21+
const base = { provider: 'falai', model: 'alibaba/happy-horse/text-to-video' }
22+
23+
it('reads the prompt endpoint when the stored upload is in the inactive mode', () => {
24+
const values = {
25+
...base,
26+
visualReferenceUpload: frame,
27+
visualReferenceInput: '',
28+
__canonicalModes: { visualReference: 'advanced' },
29+
}
30+
31+
expect(evaluateSubBlockCondition(aspectRatioField().condition, values)).toBe(true)
32+
expect(optionIds(values)).toContain('16:9')
33+
})
34+
35+
it('reads the start frame endpoint when the upload is the active mode', () => {
36+
const values = { ...base, visualReferenceUpload: frame }
37+
38+
expect(evaluateSubBlockCondition(aspectRatioField().condition, values)).toBe(false)
39+
expect(optionIds(values)).not.toContain('16:9')
40+
})
41+
})

‎apps/sim/blocks/blocks/video_generator.ts‎

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { isRecordLike } from '@sim/utils/object'
12
import { VideoIcon } from '@/components/icons'
23
import {
34
directVideoDefaults,
@@ -10,6 +11,11 @@ import {
1011
getFalVideoModel,
1112
getFalVideoVariant,
1213
} from '@/lib/media/fal-video-catalog'
14+
import {
15+
type CanonicalGroup,
16+
type CanonicalModeOverrides,
17+
resolveActiveCanonicalValue,
18+
} from '@/lib/workflows/subblocks/visibility'
1319
import { AuthMode, type BlockConfig, IntegrationType, type SubBlockConfig } from '@/blocks/types'
1420
import { normalizeFileInput, parseOptionalBooleanInput } from '@/blocks/utils'
1521

@@ -52,12 +58,24 @@ const endpointModels = falVideoModels.filter((model) => !model.legacyId)
5258
const START_FRAME_FIELDS = ['visualReferenceInput', 'visualReferenceUpload']
5359
const HIDDEN = { field: 'provider', value: [] }
5460

61+
const START_FRAME_GROUP: CanonicalGroup = {
62+
canonicalId: 'visualReference',
63+
basicId: 'visualReferenceUpload',
64+
advancedIds: ['visualReferenceInput'],
65+
}
66+
67+
/**
68+
* Whether the run will send a start frame: the serialized `visualReference`, or else only the
69+
* upload/reference member in the active mode, since the serializer drops the inactive one.
70+
*/
5571
function hasStartFrame(values?: Record<string, unknown>) {
56-
return [
57-
values?.visualReference,
58-
values?.visualReferenceInput,
59-
values?.visualReferenceUpload,
60-
].some((reference) => (Array.isArray(reference) ? reference.length > 0 : Boolean(reference)))
72+
if (!values) return false
73+
const overrides = isRecordLike(values.__canonicalModes)
74+
? (values.__canonicalModes as CanonicalModeOverrides)
75+
: undefined
76+
const reference =
77+
values.visualReference ?? resolveActiveCanonicalValue(START_FRAME_GROUP, values, overrides)
78+
return Array.isArray(reference) ? reference.length > 0 : Boolean(reference)
6179
}
6280

6381
/** Settings of the endpoint a fal run will call: image-to-video once a start frame is attached. */

0 commit comments

Comments
 (0)