diff --git a/pages/index.vue b/pages/index.vue
index 2f2f0c4..1747b58 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -109,10 +109,10 @@
Paste a block. Text before the first marker is shot 1. Put shot 2, shot 3 on their own lines to queue extensions — a line like Shot Type will not start a new shot. Every shot uses the duration slider.
- Identity boilerplate is prepended to the first shot only.
+ Identity boilerplate is prepended to the first shot only, then concatenated into Reference to Video.
- Write camera, action, and audio. We prepend the <Picture 1> identity lock automatically.
+ Write camera, action, and audio. That is the dynamic action prompt. We concatenate the <Picture 1> identity prefix in front of it and send the result to Reference to Video, not Image to Video.
- Prompt sent to MiniMax
- {{ composedIdentityPrompt }}
+ Prompt sent to Reference to Video
+ Identity prefix
+ {{ identityBoilerplate(extraIdentityPictures) }}
+ Dynamic action prompt
+ {{ identityActionPrompt }}
+ Concatenated result
+ {{ composedIdentityPrompt }}
Add a first shot, then shot 2 for an extension.
@@ -1296,11 +1301,12 @@ const generateLabel = computed(() => {
const parsedShots = computed(() => parseShotScript(prompt.value))
const identityPrompting = computed(() => studioMode.value === 'video' && videoWorkflow.value === 'v2' && useIdentityRefs.value)
const extraIdentityPictures = computed(() => identityRefs.value.flatMap((item, index) => item ? [index + 2] : []))
+const identityActionPrompt = computed(() => {
+ if (shotScriptMode.value) return parsedShots.value[0]?.prompt || prompt.value
+ return prompt.value
+})
const composedIdentityPrompt = computed(() => {
- const action = shotScriptMode.value
- ? (parsedShots.value[0]?.prompt || prompt.value)
- : prompt.value
- return buildIdentityPrompt(action, extraIdentityPictures.value)
+ return buildIdentityPrompt(identityActionPrompt.value, extraIdentityPictures.value)
})
const promptPlaceholder = computed(() => {
if (studioMode.value === 'edit') {
diff --git a/server/utils/comfy.ts b/server/utils/comfy.ts
index 15a5ef0..2c3c1cf 100644
--- a/server/utils/comfy.ts
+++ b/server/utils/comfy.ts
@@ -163,12 +163,14 @@ export function extractPromptFromHistory(entry: unknown) {
const prompt = (entry as { prompt?: unknown[] })?.prompt
const graph = Array.isArray(prompt) ? prompt[2] : null
if (!graph || typeof graph !== 'object') return ''
+ let imageToVideo = ''
for (const node of Object.values(graph as Record
}>)) {
- if (node?.class_type === 'MiniMaxH3ImageToVideo') {
- return String(node.inputs?.prompt || node.inputs?.text || '').trim()
- }
+ const text = String(node.inputs?.prompt || node.inputs?.text || '').trim()
+ if (!text) continue
+ if (node?.class_type === 'MiniMaxH3ReferenceToVideo') return text
+ if (node?.class_type === 'MiniMaxH3ImageToVideo') imageToVideo = text
}
- return ''
+ return imageToVideo
}
export function extractClipMetaFromHistory(entry: unknown) {
diff --git a/server/utils/workflow.ts b/server/utils/workflow.ts
index 0b43345..37e0d87 100644
--- a/server/utils/workflow.ts
+++ b/server/utils/workflow.ts
@@ -73,7 +73,8 @@ export function buildWorkflow(params: GenerateParams) {
const refs = Array.from({ length: 4 }, (_, index) => String(params.referenceImageNames?.[index] || '').trim())
const extraPictures = refs.flatMap((name, index) => name ? [index + 2] : [])
const useRefs = version === 'v2' && params.useIdentityRefs === true
- const graphPrompt = useRefs ? buildIdentityPrompt(params.prompt, extraPictures) : params.prompt
+ const actionPrompt = params.prompt
+ const graphPrompt = useRefs ? buildIdentityPrompt(actionPrompt, extraPictures) : actionPrompt
for (const [id, node] of Object.entries(graph)) {
if (IMAGE_CLASSES.has(node.class_type) && 'image' in node.inputs && id === '114') {
@@ -81,19 +82,20 @@ export function buildWorkflow(params: GenerateParams) {
}
if (PROMPT_CLASSES.has(node.class_type) || node.class_type.includes('Qwen3')) {
- if ('prompt' in node.inputs) node.inputs.prompt = graphPrompt
- if ('text' in node.inputs) node.inputs.text = graphPrompt
+ const nodePrompt = promptForNode(node.class_type, useRefs, actionPrompt, graphPrompt)
+ if ('prompt' in node.inputs) node.inputs.prompt = nodePrompt
+ if ('text' in node.inputs) node.inputs.text = nodePrompt
}
if (node.class_type === 'MiniMaxH3ImageToVideo') {
- node.inputs.prompt = graphPrompt
+ node.inputs.prompt = useRefs ? '' : actionPrompt
if (typeof node.inputs.width === 'number') node.inputs.width = width
if (typeof node.inputs.height === 'number') node.inputs.height = height
if (typeof node.inputs.length === 'number') node.inputs.length = params.length
}
if (node.class_type === 'MiniMaxH3ReferenceToVideo') {
- node.inputs.prompt = graphPrompt
+ node.inputs.prompt = useRefs ? graphPrompt : actionPrompt
if (!Array.isArray(node.inputs.length)) node.inputs.length = ['174', 1]
}
@@ -206,6 +208,12 @@ export function buildWorkflow(params: GenerateParams) {
return graph
}
+function promptForNode(classType: string, useRefs: boolean, actionPrompt: string, graphPrompt: string) {
+ if (classType === 'MiniMaxH3ImageToVideo') return useRefs ? '' : actionPrompt
+ if (classType === 'MiniMaxH3ReferenceToVideo') return useRefs ? graphPrompt : actionPrompt
+ return graphPrompt
+}
+
function applyV2IdentityPath(
graph: WorkflowGraph,
params: GenerateParams,