@nodaro/prompts 1.19.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +11175 -1797
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +280 -41
- package/dist/index.d.ts +280 -41
- package/dist/index.js +10049 -686
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/adult-only-ratchet.test.ts +26 -1
- package/src/__tests__/character-motion-audit.test.ts +77 -0
- package/src/__tests__/character-motion-docs-example.test.ts +26 -0
- package/src/__tests__/character-motion-exit-visibility.test.ts +183 -0
- package/src/__tests__/character-motion-partner-referent.test.ts +225 -0
- package/src/__tests__/character-motion-timing-catalogs.test.ts +76 -0
- package/src/__tests__/character-motion.test.ts +214 -0
- package/src/__tests__/fixtures/parameter-hint-golden.json +136 -0
- package/src/__tests__/frame-delivery.test.ts +93 -0
- package/src/__tests__/graph-composed-unwired-identity.test.ts +166 -0
- package/src/__tests__/parameter-hint-mode.test.ts +28 -0
- package/src/__tests__/parameter-prompt-hint.test.ts +165 -0
- package/src/__tests__/parameter-registry-sync.test.ts +2 -0
- package/src/__tests__/picker-analyzer-registry.test.ts +1 -1
- package/src/__tests__/transition-timing-catalogs.test.ts +1 -1
- package/src/__tests__/video-reference-features.test.ts +10 -1
- package/src/age-floor.ts +1 -1
- package/src/character-motion/animals-pets.ts +116 -0
- package/src/character-motion/athletic-stunts.ts +159 -0
- package/src/character-motion/camera-interaction.ts +96 -0
- package/src/character-motion/combat-weapons.ts +147 -0
- package/src/character-motion/dance.ts +125 -0
- package/src/character-motion/entrances-exits.ts +92 -0
- package/src/character-motion/evasive-falls.ts +92 -0
- package/src/character-motion/everyday-actions.ts +237 -0
- package/src/character-motion/face-expression.ts +129 -0
- package/src/character-motion/gestures.ts +215 -0
- package/src/character-motion/head-gestures.ts +70 -0
- package/src/character-motion/idle-ambient.ts +89 -0
- package/src/character-motion/posture-shifts.ts +120 -0
- package/src/character-motion/runway.ts +77 -0
- package/src/character-motion/stage-performance.ts +96 -0
- package/src/character-motion/turns-looks.ts +90 -0
- package/src/character-motion/two-person.ts +245 -0
- package/src/character-motion/types.ts +45 -0
- package/src/character-motion/unnatural-horror.ts +57 -0
- package/src/character-motion/vehicles-mounts.ts +105 -0
- package/src/character-motion/walks-runs.ts +97 -0
- package/src/character-motion-diagnostics.ts +49 -0
- package/src/character-motion.ts +362 -0
- package/src/frame-delivery.ts +106 -0
- package/src/index.ts +5 -0
- package/src/parameter-prompt-hint.ts +71 -0
- package/src/picker-analyzer-registry.ts +9 -5
- package/src/picker-catalogs.ts +33 -1
- package/src/picker-wiring.ts +2 -0
- package/src/prompt-wizard-categories.ts +0 -2
- package/src/provider-prompt-doctrine.ts +1 -1
package/src/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ export * from "./camera-format.js"
|
|
|
51
51
|
export * from "./camera-motions.js"
|
|
52
52
|
export * from "./hint-join.js"
|
|
53
53
|
export * from "./character-fx.js"
|
|
54
|
+
export * from "./character-motion.js"
|
|
54
55
|
export * from "./color-look.js"
|
|
55
56
|
export * from "./composition-effects.js"
|
|
56
57
|
export * from "./era.js"
|
|
@@ -91,3 +92,7 @@ export * from "./object-asset-presets.js"
|
|
|
91
92
|
export * from "./factory-snippets/index.js"
|
|
92
93
|
export * from "./picker-wiring.js"
|
|
93
94
|
export * from "./surround-fill.js"
|
|
95
|
+
|
|
96
|
+
// --- Start/end frame delivery: one plan for the dispatch and the editor ---
|
|
97
|
+
export * from "./frame-delivery.js"
|
|
98
|
+
export * from "./character-motion-diagnostics.js"
|
|
@@ -38,6 +38,8 @@ import { composeCameraMotionHintFromConnections } from "./camera-motions.js"
|
|
|
38
38
|
import { joinHintFragments } from "./hint-join.js"
|
|
39
39
|
import { composeTransitionHintFromConnections, type TransitionDuration, type TransitionIntensity, type TransitionPosition, type TransitionTiming } from "./transitions.js"
|
|
40
40
|
import { composeCharacterFxHintFromConnections, type CharacterFxDuration, type CharacterFxIntensity, type CharacterFxPosition, type CharacterFxTiming } from "./character-fx.js"
|
|
41
|
+
import { composeCharacterMotionHintFromConnections, type CharacterMotionPace, type CharacterMotionPosition, type CharacterMotionTiming } from "./character-motion.js"
|
|
42
|
+
import { containsMinorAgeHint, isMinorAge } from "./age-floor.js"
|
|
41
43
|
import { buildMaterialHints } from "./materials.js"
|
|
42
44
|
import { curatedAnimalPromptHint, curatedAnimalTerm, curatedVehicleText, curatedWeaponText, curatedFurnitureText } from "./shared-catalog-overlay.js"
|
|
43
45
|
import { getPhotoGenrePromptHint, getPhotoGenreTerm } from "./photo-genre.js"
|
|
@@ -73,6 +75,55 @@ function extractCharacterRefName(node: HintNodeLike): string | undefined {
|
|
|
73
75
|
return undefined
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
/** Character Motion's name read: the shared character / face / object / location
|
|
79
|
+
* read, then a creature's `creatureName`. Local to Character Motion so the
|
|
80
|
+
* Character FX prompt, which shares `extractCharacterRefName`, stays unchanged. */
|
|
81
|
+
function extractCharacterMotionRefName(node: HintNodeLike): string | undefined {
|
|
82
|
+
const name = extractCharacterRefName(node)
|
|
83
|
+
if (name) return name
|
|
84
|
+
const creatureName = asStr(((node.data ?? {}) as Record<string, unknown>).creatureName).trim()
|
|
85
|
+
return creatureName.length > 0 ? creatureName : undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Free-text fields a character / face / object / location ref node describes its subject in.
|
|
89
|
+
* A creature ref uses the same `description` / `canonicalDescription` names. */
|
|
90
|
+
const REF_DESCRIPTION_FIELDS = ["description", "seedPrompt", "canonicalDescription"] as const
|
|
91
|
+
|
|
92
|
+
/** True when a character / face / object / location ref node describes a minor:
|
|
93
|
+
* its structured `person` age (the primary signal) or an age phrase in its
|
|
94
|
+
* free-text description. Feeds the Character Motion floor (adult-only moves
|
|
95
|
+
* dropped) — errs toward flooring, never toward letting an adult-only move through. */
|
|
96
|
+
function extractCharacterRefMinor(node: HintNodeLike): boolean {
|
|
97
|
+
const d = (node.data ?? {}) as Record<string, unknown>
|
|
98
|
+
const person = d.person
|
|
99
|
+
if (person && typeof person === "object" && !Array.isArray(person) && isMinorAge(person as Parameters<typeof isMinorAge>[0])) {
|
|
100
|
+
return true
|
|
101
|
+
}
|
|
102
|
+
return REF_DESCRIPTION_FIELDS.some((field) => containsMinorAgeHint(asStr(d[field])))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The preview, diagnostics and execution use identical graph bindings. */
|
|
106
|
+
export function getCharacterMotionBindings(node: HintNodeLike, ctx?: HintGraphContext) {
|
|
107
|
+
const targetNames: string[] = []
|
|
108
|
+
const partnerNames: string[] = []
|
|
109
|
+
const targetIds = new Set<string>()
|
|
110
|
+
const partnerIds = new Set<string>()
|
|
111
|
+
let subjectMinor = false
|
|
112
|
+
for (const edge of ctx?.edges ?? []) {
|
|
113
|
+
if (edge.target !== node.id || (edge.targetHandle !== "target" && edge.targetHandle !== "partner")) continue
|
|
114
|
+
const src = ctx?.nodes.find(n => n.id === edge.source)
|
|
115
|
+
if (!src) continue
|
|
116
|
+
if (edge.targetHandle === "target") targetIds.add(src.id)
|
|
117
|
+
else partnerIds.add(src.id)
|
|
118
|
+
if (extractCharacterRefMinor(src)) subjectMinor = true
|
|
119
|
+
const name = extractCharacterMotionRefName(src)
|
|
120
|
+
if (!name) continue
|
|
121
|
+
const names = edge.targetHandle === "target" ? targetNames : partnerNames
|
|
122
|
+
if (!names.includes(name)) names.push(name)
|
|
123
|
+
}
|
|
124
|
+
return { targetNames, partnerNames, subjectMinor, selfPairing: [...targetIds].some(id => partnerIds.has(id)) }
|
|
125
|
+
}
|
|
126
|
+
|
|
76
127
|
/** Compose `[preText, mainHint, postText]` into a comma-joined string,
|
|
77
128
|
* honoring the user's free-text fragments around the structured hint.
|
|
78
129
|
* Helpers like `getStylePromptHint` that already include preText/postText
|
|
@@ -219,6 +270,26 @@ function resolveParameterHint(
|
|
|
219
270
|
return withCustomText(data, composeCharacterFxHintFromConnections(effectId, targetNames, timing, mode))
|
|
220
271
|
}
|
|
221
272
|
|
|
273
|
+
if (node.type === "character-motion") {
|
|
274
|
+
const raw = data.characterMotion
|
|
275
|
+
const motionId: string | string[] | undefined =
|
|
276
|
+
Array.isArray(raw)
|
|
277
|
+
? raw.filter((s): s is string => typeof s === "string" && s.length > 0)
|
|
278
|
+
: (asStr(raw) || undefined)
|
|
279
|
+
const timing: CharacterMotionTiming = {
|
|
280
|
+
position: asStr(data.position) as CharacterMotionPosition | undefined,
|
|
281
|
+
pace: asStr(data.pace) as CharacterMotionPace | undefined,
|
|
282
|
+
}
|
|
283
|
+
if (!ctx) {
|
|
284
|
+
return withCustomText(data, composeCharacterMotionHintFromConnections(motionId, [], [], timing, mode))
|
|
285
|
+
}
|
|
286
|
+
const { targetNames, partnerNames, subjectMinor } = getCharacterMotionBindings(node, ctx)
|
|
287
|
+
return withCustomText(
|
|
288
|
+
data,
|
|
289
|
+
composeCharacterMotionHintFromConnections(motionId, targetNames, partnerNames, timing, mode, { subjectMinor }),
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
222
293
|
const base = resolveBaseHint(node.type, data, mode)
|
|
223
294
|
if (base) return base
|
|
224
295
|
// Pack-extend fallback: a single-dim pack entry the per-catalog getter (which
|
|
@@ -29,6 +29,7 @@ import { ACTION_FX } from "./action-fx.js"
|
|
|
29
29
|
import { LOOP_SUBJECTS } from "./loop-subject.js"
|
|
30
30
|
import { TRANSITIONS } from "./transitions.js"
|
|
31
31
|
import { CHARACTER_FX } from "./character-fx.js"
|
|
32
|
+
import { CHARACTER_MOTIONS } from "./character-motion.js"
|
|
32
33
|
import { POSES } from "./pose.js"
|
|
33
34
|
import { MATERIALS } from "./materials.js"
|
|
34
35
|
import { HELD_PROPS } from "./held-prop.js"
|
|
@@ -183,6 +184,7 @@ export const PICKER_ANALYZER_REGISTRY = {
|
|
|
183
184
|
"loop-subject": { kind: "flat", toolName: "emit_loop_subject", field: "loopSubject", label: "Loop Subject", entries: LOOP_SUBJECTS as ReadonlyArray<AnalyzerEntry> },
|
|
184
185
|
transition: { kind: "flat", toolName: "emit_transition", field: "transition", label: "Transition", entries: TRANSITIONS as ReadonlyArray<AnalyzerEntry> },
|
|
185
186
|
"character-fx": { kind: "flat", toolName: "emit_character_fx", field: "characterFx", label: "Character FX", entries: CHARACTER_FX as ReadonlyArray<AnalyzerEntry> },
|
|
187
|
+
"character-motion": { kind: "flat", toolName: "emit_character_motion", field: "characterMotion", label: "Character Motion", entries: CHARACTER_MOTIONS as ReadonlyArray<AnalyzerEntry> },
|
|
186
188
|
pose: { kind: "flat", toolName: "emit_pose", field: "pose", label: "Pose", entries: POSES as ReadonlyArray<AnalyzerEntry> },
|
|
187
189
|
material: { kind: "flat", toolName: "emit_material", field: "material", label: "Material", entries: MATERIALS as ReadonlyArray<AnalyzerEntry> },
|
|
188
190
|
"held-prop": { kind: "flat", toolName: "emit_held_prop", field: "heldProp", label: "Held Prop", entries: HELD_PROPS as ReadonlyArray<AnalyzerEntry> },
|
|
@@ -301,18 +303,20 @@ export type PickerType = keyof typeof PICKER_ANALYZER_REGISTRY
|
|
|
301
303
|
export const PICKER_TYPES = Object.keys(PICKER_ANALYZER_REGISTRY) as PickerType[]
|
|
302
304
|
|
|
303
305
|
/**
|
|
304
|
-
* Family grouping for BATCHED analysis. A single call across
|
|
305
|
-
*
|
|
306
|
+
* Family grouping for BATCHED analysis. A single call across the first 38
|
|
307
|
+
* catalogs carried a ~211k-char legend (~53k tokens — measured 2026-08-09, the
|
|
306
308
|
* measure-first probe from the text-to-picker spec), which is slow, costly,
|
|
307
309
|
* and dilutes per-section accuracy. The text-to-picker route fans out one
|
|
308
|
-
* structured call per family (6-15k tokens each) and merges.
|
|
309
|
-
*
|
|
310
|
+
* structured call per family (6-15k tokens each) and merges. The character
|
|
311
|
+
* family is the exception: Character Motion's 1005-id legend alone is ~82k
|
|
312
|
+
* chars (~46k input tokens measured 2026-09-15), making it the heaviest call.
|
|
313
|
+
* Mirrors the build-brief's §5 UI grouping so Cine can reuse the same partition.
|
|
310
314
|
*/
|
|
311
315
|
export const PICKER_ANALYZER_FAMILIES: Readonly<Record<string, ReadonlyArray<PickerType>>> = {
|
|
312
316
|
scene: ["setting", "atmosphere", "backdrop", "era", "temporal"],
|
|
313
317
|
look: ["style", "color-look", "mood", "aesthetic", "photographer", "photo-genre", "render-quality", "composition-effects", "post-process-effects"],
|
|
314
318
|
camera: ["framing", "camera-motion", "lens", "camera-format", "lighting", "exposure-settings"],
|
|
315
|
-
character: ["person", "styling", "pose", "character-fx"],
|
|
319
|
+
character: ["person", "styling", "pose", "character-fx", "character-motion"],
|
|
316
320
|
elements: ["animal", "vehicle", "weapon", "furniture", "held-prop", "material", "action-fx", "loop-subject", "transition"],
|
|
317
321
|
audio: ["music-genre", "music-mood", "instrumentation", "voice-character", "voice-delivery"],
|
|
318
322
|
}
|
package/src/picker-catalogs.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CharacterMotionMetadata } from "./character-motion/types.js"
|
|
1
2
|
/**
|
|
2
3
|
* Public, discoverable registry of the parameter-picker catalogs.
|
|
3
4
|
*
|
|
@@ -67,6 +68,13 @@ import {
|
|
|
67
68
|
CHARACTER_FX_DURATIONS,
|
|
68
69
|
CHARACTER_FX_INTENSITIES,
|
|
69
70
|
} from "./character-fx.js"
|
|
71
|
+
import {
|
|
72
|
+
CHARACTER_MOTIONS,
|
|
73
|
+
CHARACTER_MOTION_CATEGORY_LABELS,
|
|
74
|
+
CHARACTER_MOTION_CATEGORY_ORDER,
|
|
75
|
+
CHARACTER_MOTION_POSITIONS,
|
|
76
|
+
CHARACTER_MOTION_PACES,
|
|
77
|
+
} from "./character-motion.js"
|
|
70
78
|
import { POSES, POSE_CATEGORY_LABELS, POSE_CATEGORY_ORDER } from "./pose.js"
|
|
71
79
|
import { MATERIALS, MATERIAL_CATEGORY_LABELS, MATERIAL_CATEGORY_ORDER } from "./materials.js"
|
|
72
80
|
import { ANIMALS, ANIMAL_SUBCATEGORY_LABELS, ANIMAL_SUBCATEGORY_ORDER, getAnimalPromptHint } from "@nodaro/shared"
|
|
@@ -91,6 +99,9 @@ import { setComposedCatalogResolver } from "./catalog-overlay.js"
|
|
|
91
99
|
import { deriveTerm, resolveTerm } from "./term.js"
|
|
92
100
|
|
|
93
101
|
export interface PickerOption {
|
|
102
|
+
/** Character Motion only: authored prerequisites, state and search metadata. */
|
|
103
|
+
readonly motion?: CharacterMotionMetadata
|
|
104
|
+
|
|
94
105
|
readonly id: string
|
|
95
106
|
readonly label: string
|
|
96
107
|
readonly description?: string
|
|
@@ -588,6 +599,26 @@ const SINGLE_CATALOGS: readonly PickerCatalog[] = [
|
|
|
588
599
|
["intensity", CHARACTER_FX_INTENSITIES],
|
|
589
600
|
]),
|
|
590
601
|
},
|
|
602
|
+
{
|
|
603
|
+
nodeType: "character-motion",
|
|
604
|
+
label: "Character Motion",
|
|
605
|
+
catalogId: "character-motion",
|
|
606
|
+
kind: "single",
|
|
607
|
+
valueField: "characterMotion",
|
|
608
|
+
defaultValue: "auto",
|
|
609
|
+
categoryOrder: CHARACTER_MOTION_CATEGORY_ORDER,
|
|
610
|
+
categoryLabels: CHARACTER_MOTION_CATEGORY_LABELS,
|
|
611
|
+
options: toOptions(CHARACTER_MOTIONS, "category").map((option, index) => {
|
|
612
|
+
const { id: _id, label: _label, category: _category, description: _description, promptHint: _hint, term: _term, adultOnly: _adult, twoPerson: _duo, ...motion } = CHARACTER_MOTIONS[index]!
|
|
613
|
+
return { ...option, motion }
|
|
614
|
+
}),
|
|
615
|
+
// Position + Pace, this node's own wording (a movement begins / plays out;
|
|
616
|
+
// an effect manifests; a transition occurs) — never the sibling rows.
|
|
617
|
+
dimensions: perFieldDims([
|
|
618
|
+
["position", CHARACTER_MOTION_POSITIONS],
|
|
619
|
+
["pace", CHARACTER_MOTION_PACES],
|
|
620
|
+
]),
|
|
621
|
+
},
|
|
591
622
|
|
|
592
623
|
// -------- "Subject / Object" family --------
|
|
593
624
|
{
|
|
@@ -915,8 +946,9 @@ function projectOption(o: PickerOption, detail: PickerCatalogDetail): ProjectedP
|
|
|
915
946
|
promptHint: o.promptHint,
|
|
916
947
|
term: o.term,
|
|
917
948
|
icon: o.icon,
|
|
949
|
+
...(o.motion ? { motion: o.motion } : {}),
|
|
918
950
|
}
|
|
919
|
-
: { id: o.id, label: o.label, category: o.category, term: o.term, icon: o.icon }
|
|
951
|
+
: { id: o.id, label: o.label, category: o.category, term: o.term, icon: o.icon, ...(o.motion ? { motion: o.motion } : {}) }
|
|
920
952
|
}
|
|
921
953
|
|
|
922
954
|
/** Project a catalog to the wire shape: compact by default, optional category/field filter. */
|
package/src/picker-wiring.ts
CHANGED
|
@@ -51,6 +51,7 @@ import { ACTION_FX, ACTION_FX_CATEGORY_LABELS, ACTION_FX_CATEGORY_ORDER } from "
|
|
|
51
51
|
import { LOOP_SUBJECTS, LOOP_SUBJECT_CATEGORY_LABELS, LOOP_SUBJECT_CATEGORY_ORDER } from "./loop-subject.js"
|
|
52
52
|
import { TRANSITIONS, TRANSITION_CATEGORY_LABELS, TRANSITION_CATEGORY_ORDER } from "./transitions.js"
|
|
53
53
|
import { CHARACTER_FX, CHARACTER_FX_CATEGORY_LABELS, CHARACTER_FX_CATEGORY_ORDER } from "./character-fx.js"
|
|
54
|
+
import { CHARACTER_MOTIONS, CHARACTER_MOTION_CATEGORY_LABELS, CHARACTER_MOTION_CATEGORY_ORDER } from "./character-motion.js"
|
|
54
55
|
import { FRAMINGS, FRAMING_CATEGORY_ORDER, FRAMING_FIELD_BY_CATEGORY } from "./framing.js"
|
|
55
56
|
import { LIGHTINGS, LIGHTING_CATEGORY_ORDER, LIGHTING_FIELD_BY_CATEGORY } from "./lighting.js"
|
|
56
57
|
import { STYLINGS, STYLING_DIMENSION_ORDER, STYLING_FIELD_BY_DIMENSION } from "./styling.js"
|
|
@@ -159,6 +160,7 @@ export const SINGLE_PICKER_WIRING: ReadonlyArray<SingleDimPickerWiring> = [
|
|
|
159
160
|
{ kind: "single", nodeType: "camera-format", label: "Camera / Film", valueField: "cameraFormat", defaultValue: "35mm-film", catalogId: "camera-format", entries: mapCat(CAMERA_FORMATS) },
|
|
160
161
|
{ kind: "single", nodeType: "transition", label: "Transition", valueField: "transition", defaultValue: "auto", catalogId: "transitions", entries: mapCat(TRANSITIONS, "category"), groupOrder: TRANSITION_CATEGORY_ORDER as ReadonlyArray<string>, groupLabels: TRANSITION_CATEGORY_LABELS },
|
|
161
162
|
{ kind: "single", nodeType: "character-fx", label: "Character FX", valueField: "characterFx", defaultValue: "auto", catalogId: "character-fx", entries: mapCat(CHARACTER_FX, "category"), groupOrder: CHARACTER_FX_CATEGORY_ORDER as ReadonlyArray<string>, groupLabels: CHARACTER_FX_CATEGORY_LABELS },
|
|
163
|
+
{ kind: "single", nodeType: "character-motion", label: "Character Motion", valueField: "characterMotion", defaultValue: "auto", catalogId: "character-motion", entries: mapCat(CHARACTER_MOTIONS, "category"), groupOrder: CHARACTER_MOTION_CATEGORY_ORDER as ReadonlyArray<string>, groupLabels: CHARACTER_MOTION_CATEGORY_LABELS },
|
|
162
164
|
|
|
163
165
|
// -------- "Subject / Object" family --------
|
|
164
166
|
{ kind: "single", nodeType: "pose", label: "Pose", valueField: "pose", defaultValue: "standing-upright", catalogId: "pose", entries: mapCat(POSES, "category"), groupOrder: POSE_CATEGORY_ORDER as ReadonlyArray<string>, groupLabels: POSE_CATEGORY_LABELS },
|
|
@@ -196,7 +196,6 @@ export const PROVIDER_CAPABILITIES: Record<string, Record<string, string>> = {
|
|
|
196
196
|
"gpt-image-2-5-sunburst-i2i": "Precision GPT Image 2.5 edits — up to 16 sources, tightest control on demanding retouches; slower",
|
|
197
197
|
"ideogram-edit": "Instruction-based editing with text preservation",
|
|
198
198
|
"ideogram-remix": "Style remixing while preserving structure",
|
|
199
|
-
"ideogram-reframe": "Aspect ratio changes with AI fill",
|
|
200
199
|
"qwen-i2i": "Versatile transformation",
|
|
201
200
|
"qwen-edit": "Instruction-based editing",
|
|
202
201
|
"seedream-edit": "Artistic style editing",
|
|
@@ -221,7 +220,6 @@ export const PROVIDER_CAPABILITIES: Record<string, Record<string, string>> = {
|
|
|
221
220
|
"gpt-image-2-5-sunburst-i2i": "Precision GPT Image 2.5 edits — up to 16 sources, tightest control on demanding retouches; slower",
|
|
222
221
|
"ideogram-edit": "Instruction-based editing with text preservation",
|
|
223
222
|
"ideogram-remix": "Style remixing while preserving structure",
|
|
224
|
-
"ideogram-reframe": "Aspect ratio changes with AI fill",
|
|
225
223
|
"qwen-i2i": "Versatile transformation",
|
|
226
224
|
"qwen-edit": "Instruction-based editing",
|
|
227
225
|
"seedream-edit": "Artistic style editing",
|
|
@@ -227,7 +227,7 @@ Example: "Medium shot, a tired corporate worker, rubbing his temples in exhausti
|
|
|
227
227
|
|
|
228
228
|
**Frames & references**
|
|
229
229
|
- Start + end frame: wire both (imageUrls [start, end]) and describe the camera path between them — "a smooth 180-degree arc shot, starting front-facing and circling to end on the POV from behind her".
|
|
230
|
-
- Reference images (ingredients): attach character/object/scene refs and name them in the prompt ("using the provided images for the detective and the office, …"). Reference runs DO generate audio.
|
|
230
|
+
- Reference images (ingredients) — **Fast (veo3.1) and Lite (veo3_lite) only**; the Quality SKU (veo3) has no reference-to-video mode at the provider, so wire it with frames alone. Where they are available: attach character/object/scene refs and name them in the prompt ("using the provided images for the detective and the office, …"). Reference runs DO generate audio.
|
|
231
231
|
|
|
232
232
|
**Constraints**
|
|
233
233
|
- Negative prompting works by positive description: write "a desolate landscape with no buildings or roads", not "no buildings".
|