@nodaro/prompts 1.0.1

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.
Files changed (150) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +27 -0
  3. package/dist/index.cjs +21240 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +3599 -0
  6. package/dist/index.d.ts +3599 -0
  7. package/dist/index.js +20854 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +49 -0
  10. package/src/__tests__/__snapshots__/prompt-builder-segments.test.ts.snap +65 -0
  11. package/src/__tests__/action-fx.test.ts +155 -0
  12. package/src/__tests__/apply-picker-json.test.ts +50 -0
  13. package/src/__tests__/assemble-image-input.test.ts +248 -0
  14. package/src/__tests__/assemble-suno-input.test.ts +283 -0
  15. package/src/__tests__/brand-tokens.test.ts +81 -0
  16. package/src/__tests__/build-image-prompt-element-injection.test.ts +118 -0
  17. package/src/__tests__/build-image-prompt-hybrid-format.test.ts +131 -0
  18. package/src/__tests__/build-image-prompt-mentions.test.ts +809 -0
  19. package/src/__tests__/build-image-prompt-reference-cap.test.ts +57 -0
  20. package/src/__tests__/build-image-prompt-reference-numbering.test.ts +237 -0
  21. package/src/__tests__/build-image-prompt-reference-order.test.ts +313 -0
  22. package/src/__tests__/camera-motions-from-connections.test.ts +71 -0
  23. package/src/__tests__/catalog-gapfill.test.ts +53 -0
  24. package/src/__tests__/character-convergence-image.test.ts +217 -0
  25. package/src/__tests__/character-default-role-image.test.ts +167 -0
  26. package/src/__tests__/character-default-role-video.test.ts +168 -0
  27. package/src/__tests__/character-default-role.test.ts +70 -0
  28. package/src/__tests__/character-fx.test.ts +200 -0
  29. package/src/__tests__/entity-prompts-location.test.ts +97 -0
  30. package/src/__tests__/entity-prompts.test.ts +240 -0
  31. package/src/__tests__/expand-extra-refs-role.test.ts +50 -0
  32. package/src/__tests__/factory-presets.test.ts +1045 -0
  33. package/src/__tests__/factory-snippets.test.ts +68 -0
  34. package/src/__tests__/framing-multi.test.ts +71 -0
  35. package/src/__tests__/framing-vantage.test.ts +39 -0
  36. package/src/__tests__/i18n-entry-completeness.test.ts +269 -0
  37. package/src/__tests__/identity-lock.test.ts +46 -0
  38. package/src/__tests__/instrumentation.test.ts +68 -0
  39. package/src/__tests__/lighting-multi.test.ts +68 -0
  40. package/src/__tests__/location-convergence-image.test.ts +124 -0
  41. package/src/__tests__/mention-lock-flag.test.ts +499 -0
  42. package/src/__tests__/multi-picker-spec.test.ts +71 -0
  43. package/src/__tests__/music-genre.test.ts +103 -0
  44. package/src/__tests__/music-mood.test.ts +87 -0
  45. package/src/__tests__/object-creature-convergence-image.test.ts +105 -0
  46. package/src/__tests__/parameter-prompt-hint.test.ts +270 -0
  47. package/src/__tests__/parameter-registry-sync.test.ts +243 -0
  48. package/src/__tests__/person-age.test.ts +80 -0
  49. package/src/__tests__/person-analyzer-invariants.test.ts +47 -0
  50. package/src/__tests__/person-body-axes.test.ts +67 -0
  51. package/src/__tests__/person-facial-geometry.test.ts +156 -0
  52. package/src/__tests__/person-regional-aesthetic.test.ts +161 -0
  53. package/src/__tests__/person-sections.test.ts +18 -0
  54. package/src/__tests__/picker-analyzer-registry.test.ts +108 -0
  55. package/src/__tests__/picker-catalogs-project.test.ts +85 -0
  56. package/src/__tests__/picker-catalogs.test.ts +53 -0
  57. package/src/__tests__/picker-limits.test.ts +20 -0
  58. package/src/__tests__/prompt-builder-segments.test.ts +183 -0
  59. package/src/__tests__/prompt-builder-structured-fields.test.ts +40 -0
  60. package/src/__tests__/prompt-builder.test.ts +1773 -0
  61. package/src/__tests__/prompt-wizard-categories.test.ts +31 -0
  62. package/src/__tests__/provider-prompt-doctrine.test.ts +49 -0
  63. package/src/__tests__/resolve-prompt-append.test.ts +58 -0
  64. package/src/__tests__/resolve-prompt.test.ts +44 -0
  65. package/src/__tests__/role-picker-shared.test.ts +208 -0
  66. package/src/__tests__/seedance-2-inputs.test.ts +173 -0
  67. package/src/__tests__/seedance-extend.test.ts +44 -0
  68. package/src/__tests__/sound-aggregator.test.ts +350 -0
  69. package/src/__tests__/style-presets.test.ts +34 -0
  70. package/src/__tests__/temporal-multi.test.ts +74 -0
  71. package/src/__tests__/transitions.test.ts +213 -0
  72. package/src/__tests__/video-reference-features.test.ts +41 -0
  73. package/src/__tests__/video-reference-leading-refs.test.ts +90 -0
  74. package/src/__tests__/video-reference-resolver.test.ts +233 -0
  75. package/src/__tests__/video-reference-roles.test.ts +96 -0
  76. package/src/__tests__/voice-character.test.ts +48 -0
  77. package/src/__tests__/voice-delivery.test.ts +41 -0
  78. package/src/__tests__/wardrobe.test.ts +25 -0
  79. package/src/action-fx.ts +255 -0
  80. package/src/aesthetic.ts +435 -0
  81. package/src/assemble-image-input.ts +236 -0
  82. package/src/assemble-suno-input.ts +147 -0
  83. package/src/atmosphere.ts +104 -0
  84. package/src/backdrop.ts +131 -0
  85. package/src/brand-tokens.ts +154 -0
  86. package/src/camera-format.ts +76 -0
  87. package/src/camera-motions.ts +615 -0
  88. package/src/character-fx.ts +274 -0
  89. package/src/color-look.ts +103 -0
  90. package/src/composition-effects.ts +67 -0
  91. package/src/entity-prompts.ts +231 -0
  92. package/src/era.ts +292 -0
  93. package/src/exposure-settings.ts +142 -0
  94. package/src/factory-presets/generate-image.ts +1644 -0
  95. package/src/factory-presets/generate-video.ts +1116 -0
  96. package/src/factory-presets/index.ts +46 -0
  97. package/src/factory-presets/lottie-overlay.ts +166 -0
  98. package/src/factory-presets/motion-graphics.ts +350 -0
  99. package/src/factory-presets/music.ts +734 -0
  100. package/src/factory-presets/sfx.ts +136 -0
  101. package/src/factory-presets/shared-image.ts +207 -0
  102. package/src/factory-presets/switchx.ts +65 -0
  103. package/src/factory-presets/text.ts +292 -0
  104. package/src/factory-presets/types.ts +51 -0
  105. package/src/factory-presets/video-edit.ts +136 -0
  106. package/src/factory-presets/voice.ts +172 -0
  107. package/src/factory-presets.ts +2 -0
  108. package/src/factory-snippets/catalog.ts +105 -0
  109. package/src/factory-snippets/index.ts +16 -0
  110. package/src/factory-snippets/types.ts +33 -0
  111. package/src/framing.ts +634 -0
  112. package/src/held-prop.ts +187 -0
  113. package/src/identity-lock.ts +213 -0
  114. package/src/index.ts +66 -0
  115. package/src/instrumentation.ts +337 -0
  116. package/src/lens.ts +59 -0
  117. package/src/lighting.ts +229 -0
  118. package/src/loop-subject.ts +276 -0
  119. package/src/materials.ts +184 -0
  120. package/src/mood.ts +186 -0
  121. package/src/music-genre.ts +662 -0
  122. package/src/music-mood.ts +137 -0
  123. package/src/object-asset-presets.ts +81 -0
  124. package/src/parameter-prompt-hint.ts +281 -0
  125. package/src/person.ts +1368 -0
  126. package/src/photo-genre.ts +151 -0
  127. package/src/photographer.ts +612 -0
  128. package/src/picker-analyzer-registry.ts +374 -0
  129. package/src/picker-catalogs.ts +858 -0
  130. package/src/pose.ts +246 -0
  131. package/src/post-process-effects.ts +94 -0
  132. package/src/prompt-builder-structured-fields.ts +116 -0
  133. package/src/prompt-builder.ts +2964 -0
  134. package/src/prompt-templates.ts +50 -0
  135. package/src/prompt-wizard-categories.ts +334 -0
  136. package/src/provider-prompt-doctrine.ts +85 -0
  137. package/src/render-quality.ts +89 -0
  138. package/src/resolve-prompt.ts +120 -0
  139. package/src/seedance-2-inputs.ts +100 -0
  140. package/src/setting.ts +130 -0
  141. package/src/sound-aggregator.ts +241 -0
  142. package/src/style-presets.ts +162 -0
  143. package/src/style.ts +99 -0
  144. package/src/styling.ts +585 -0
  145. package/src/temporal.ts +151 -0
  146. package/src/transitions.ts +333 -0
  147. package/src/video-reference-resolver.ts +823 -0
  148. package/src/voice-character.ts +237 -0
  149. package/src/voice-delivery.ts +142 -0
  150. package/src/wardrobe.ts +179 -0
@@ -0,0 +1,615 @@
1
+ /**
2
+ * Canonical catalog of camera motions for video / image-to-video generation.
3
+ *
4
+ * Shared between frontend (picker UI, prompt hint injection) and backend
5
+ * (orchestrator payload builder). The `promptHint` is a natural-language
6
+ * cue that gets appended to the user prompt when the node has camera
7
+ * motion enabled.
8
+ */
9
+
10
+ export type CameraMotionCategory =
11
+ | "default"
12
+ | "pan"
13
+ | "tilt"
14
+ | "zoom"
15
+ | "dolly"
16
+ | "truck"
17
+ | "pedestal"
18
+ | "roll"
19
+ | "orbit"
20
+ | "crane"
21
+ | "tracking"
22
+ | "special"
23
+
24
+ export interface CameraMotion {
25
+ readonly id: string
26
+ readonly label: string
27
+ readonly category: CameraMotionCategory
28
+ readonly description: string
29
+ readonly promptHint: string
30
+ }
31
+
32
+ export const CAMERA_MOTIONS: ReadonlyArray<CameraMotion> = [
33
+ // Basic
34
+ {
35
+ id: "auto",
36
+ label: "Auto",
37
+ category: "default",
38
+ description: "Let the model choose appropriate camera motion",
39
+ promptHint: "",
40
+ },
41
+ {
42
+ id: "static",
43
+ label: "Static",
44
+ category: "default",
45
+ description: "Fixed camera, no movement",
46
+ promptHint: "locked off static camera, no camera movement",
47
+ },
48
+ {
49
+ id: "handheld",
50
+ label: "Handheld",
51
+ category: "default",
52
+ description: "Natural handheld shake",
53
+ promptHint: "handheld camera with subtle natural shake and micro movements",
54
+ },
55
+ {
56
+ id: "steadicam",
57
+ label: "Steadicam",
58
+ category: "default",
59
+ description: "Smooth stabilized walking shot",
60
+ promptHint: "smooth steadicam shot, gliding stabilized movement through the scene",
61
+ },
62
+
63
+ // Pan — rotation on vertical axis
64
+ {
65
+ id: "pan-left",
66
+ label: "Pan Left",
67
+ category: "pan",
68
+ description: "Rotate camera horizontally to the left",
69
+ promptHint: "slow camera pan left, rotating horizontally",
70
+ },
71
+ {
72
+ id: "pan-right",
73
+ label: "Pan Right",
74
+ category: "pan",
75
+ description: "Rotate camera horizontally to the right",
76
+ promptHint: "slow camera pan right, rotating horizontally",
77
+ },
78
+ {
79
+ id: "whip-pan-left",
80
+ label: "Whip Pan Left",
81
+ category: "pan",
82
+ description: "Fast whip pan left with motion blur",
83
+ promptHint: "fast whip pan to the left with heavy motion blur",
84
+ },
85
+ {
86
+ id: "whip-pan-right",
87
+ label: "Whip Pan Right",
88
+ category: "pan",
89
+ description: "Fast whip pan right with motion blur",
90
+ promptHint: "fast whip pan to the right with heavy motion blur",
91
+ },
92
+
93
+ // Tilt — rotation on horizontal axis
94
+ {
95
+ id: "tilt-up",
96
+ label: "Tilt Up",
97
+ category: "tilt",
98
+ description: "Tilt camera upward",
99
+ promptHint: "camera tilts upward, revealing upward from the ground",
100
+ },
101
+ {
102
+ id: "tilt-down",
103
+ label: "Tilt Down",
104
+ category: "tilt",
105
+ description: "Tilt camera downward",
106
+ promptHint: "camera tilts downward, revealing downward from above",
107
+ },
108
+
109
+ // Zoom — lens only
110
+ {
111
+ id: "zoom-in",
112
+ label: "Zoom In",
113
+ category: "zoom",
114
+ description: "Lens zoom toward subject",
115
+ promptHint: "slow zoom in on the subject, fixed camera position",
116
+ },
117
+ {
118
+ id: "zoom-out",
119
+ label: "Zoom Out",
120
+ category: "zoom",
121
+ description: "Lens zoom away from subject",
122
+ promptHint: "slow zoom out, fixed camera position",
123
+ },
124
+ {
125
+ id: "crash-zoom-in",
126
+ label: "Crash Zoom In",
127
+ category: "zoom",
128
+ description: "Snappy whip-style zoom in",
129
+ promptHint: "fast crash zoom in, snappy whip-style sudden dramatic zoom toward subject",
130
+ },
131
+ {
132
+ id: "crash-zoom-out",
133
+ label: "Crash Zoom Out",
134
+ category: "zoom",
135
+ description: "Snappy whip-style zoom out",
136
+ promptHint: "fast crash zoom out, snappy whip-style sudden dramatic pull back",
137
+ },
138
+
139
+ // Dolly — physical forward/back
140
+ {
141
+ id: "dolly-in",
142
+ label: "Dolly In",
143
+ category: "dolly",
144
+ description: "Push camera toward subject (parallax)",
145
+ promptHint: "dolly in, camera pushes forward toward the subject with natural parallax",
146
+ },
147
+ {
148
+ id: "dolly-out",
149
+ label: "Dolly Out",
150
+ category: "dolly",
151
+ description: "Pull camera away (parallax)",
152
+ promptHint: "dolly out, camera pulls back away from the subject with natural parallax",
153
+ },
154
+ {
155
+ id: "dolly-zoom",
156
+ label: "Dolly Zoom",
157
+ category: "dolly",
158
+ description: "Vertigo effect: dolly opposes zoom",
159
+ promptHint: "dolly zoom vertigo effect, background scale changes while subject stays the same size",
160
+ },
161
+ {
162
+ id: "push-in",
163
+ label: "Push In",
164
+ category: "dolly",
165
+ description: "Slow subtle push toward subject",
166
+ promptHint: "slow push in, gentle forward movement toward the subject",
167
+ },
168
+ {
169
+ id: "pull-out",
170
+ label: "Pull Out",
171
+ category: "dolly",
172
+ description: "Slow subtle pull back from subject",
173
+ promptHint: "slow pull out, gentle backward movement away from the subject",
174
+ },
175
+ {
176
+ id: "breathing",
177
+ label: "Breathing Camera",
178
+ category: "dolly",
179
+ description: "Subtle continuous push/pull oscillation",
180
+ promptHint: "breathing camera, subtle continuous breathing-like push-in and pull-out oscillation with an organic handheld feel",
181
+ },
182
+ {
183
+ id: "push-pull",
184
+ label: "Push-Pull / Swing",
185
+ category: "dolly",
186
+ description: "Camera swings toward subject then away",
187
+ promptHint: "push-pull swing, the camera moves toward the subject then back away in a single move, a swinging approach-and-retreat that creates emphasis or hesitation",
188
+ },
189
+ {
190
+ id: "creep-in",
191
+ label: "Creep-In",
192
+ category: "dolly",
193
+ description: "Imperceptibly slow push-in over time",
194
+ promptHint: "creep-in, imperceptibly slow push-in over many seconds, building dread or tension",
195
+ },
196
+ {
197
+ id: "creep-out",
198
+ label: "Creep-Out",
199
+ category: "dolly",
200
+ description: "Imperceptibly slow pull-out over time",
201
+ promptHint: "creep-out, imperceptibly slow pull-out over many seconds, isolating the subject in space",
202
+ },
203
+
204
+ // Truck — lateral slide
205
+ {
206
+ id: "truck-left",
207
+ label: "Truck Left",
208
+ category: "truck",
209
+ description: "Slide camera body laterally left",
210
+ promptHint: "truck left, camera slides laterally to the left",
211
+ },
212
+ {
213
+ id: "truck-right",
214
+ label: "Truck Right",
215
+ category: "truck",
216
+ description: "Slide camera body laterally right",
217
+ promptHint: "truck right, camera slides laterally to the right",
218
+ },
219
+
220
+ // Pedestal — vertical slide
221
+ {
222
+ id: "pedestal-up",
223
+ label: "Pedestal Up",
224
+ category: "pedestal",
225
+ description: "Raise camera body vertically",
226
+ promptHint: "pedestal up, camera rises vertically while keeping the horizon level",
227
+ },
228
+ {
229
+ id: "pedestal-down",
230
+ label: "Pedestal Down",
231
+ category: "pedestal",
232
+ description: "Lower camera body vertically",
233
+ promptHint: "pedestal down, camera descends vertically while keeping the horizon level",
234
+ },
235
+
236
+ // Roll — rotation on lens axis
237
+ {
238
+ id: "roll-left",
239
+ label: "Roll Left",
240
+ category: "roll",
241
+ description: "Rotate camera counterclockwise",
242
+ promptHint: "camera rolls counterclockwise around the lens axis",
243
+ },
244
+ {
245
+ id: "roll-right",
246
+ label: "Roll Right",
247
+ category: "roll",
248
+ description: "Rotate camera clockwise",
249
+ promptHint: "camera rolls clockwise around the lens axis",
250
+ },
251
+ {
252
+ id: "dutch-angle",
253
+ label: "Dutch Angle",
254
+ category: "roll",
255
+ description: "Static tilted frame for tension",
256
+ promptHint: "dutch angle, canted tilted frame for tension",
257
+ },
258
+ {
259
+ id: "spin-360",
260
+ label: "Full 360 Spin",
261
+ category: "roll",
262
+ description: "Camera rotates a full 360° on its axis",
263
+ promptHint: "full 360 degree spin, the camera rotates a complete revolution on its own lens axis",
264
+ },
265
+
266
+ // Orbit / Arc
267
+ {
268
+ id: "orbit-left",
269
+ label: "Orbit Left",
270
+ category: "orbit",
271
+ description: "Full circle around subject to the left",
272
+ promptHint: "camera orbits around the subject to the left in a circular path",
273
+ },
274
+ {
275
+ id: "orbit-right",
276
+ label: "Orbit Right",
277
+ category: "orbit",
278
+ description: "Full circle around subject to the right",
279
+ promptHint: "camera orbits around the subject to the right in a circular path",
280
+ },
281
+ {
282
+ id: "arc-left",
283
+ label: "Arc Left",
284
+ category: "orbit",
285
+ description: "Partial arc around subject left",
286
+ promptHint: "camera arcs to the left around the subject",
287
+ },
288
+ {
289
+ id: "arc-right",
290
+ label: "Arc Right",
291
+ category: "orbit",
292
+ description: "Partial arc around subject right",
293
+ promptHint: "camera arcs to the right around the subject",
294
+ },
295
+ {
296
+ id: "orbit-360",
297
+ label: "Full 360 Orbit",
298
+ category: "orbit",
299
+ description: "Full circular arc around subject",
300
+ promptHint: "full 360 degree orbit, the camera arcs a complete revolution around the subject",
301
+ },
302
+
303
+ // Crane / Jib
304
+ {
305
+ id: "crane-up",
306
+ label: "Crane Up",
307
+ category: "crane",
308
+ description: "Sweeping crane rise revealing scene",
309
+ promptHint: "crane up, camera rises on a jib revealing the wider scene",
310
+ },
311
+ {
312
+ id: "crane-down",
313
+ label: "Crane Down",
314
+ category: "crane",
315
+ description: "Sweeping crane descent",
316
+ promptHint: "crane down, camera descends from above toward the subject",
317
+ },
318
+ {
319
+ id: "boom-up",
320
+ label: "Boom Up",
321
+ category: "crane",
322
+ description: "Boom arm rise",
323
+ promptHint: "boom up, camera lifts on a boom arm while keeping subject framed",
324
+ },
325
+ {
326
+ id: "boom-down",
327
+ label: "Boom Down",
328
+ category: "crane",
329
+ description: "Boom arm descent",
330
+ promptHint: "boom down, camera lowers on a boom arm while keeping subject framed",
331
+ },
332
+
333
+ // Tracking / Follow
334
+ {
335
+ id: "tracking-shot",
336
+ label: "Tracking Shot",
337
+ category: "tracking",
338
+ description: "Camera tracks moving subject alongside",
339
+ promptHint: "tracking shot, camera moves alongside the subject keeping them in frame",
340
+ },
341
+ {
342
+ id: "follow",
343
+ label: "Follow",
344
+ category: "tracking",
345
+ description: "Follow subject from behind",
346
+ promptHint: "follow shot, camera trails the subject from behind at a constant distance",
347
+ },
348
+ {
349
+ id: "lead",
350
+ label: "Lead",
351
+ category: "tracking",
352
+ description: "Move ahead of advancing subject",
353
+ promptHint: "lead shot, camera moves backward ahead of the advancing subject",
354
+ },
355
+ {
356
+ id: "drone-follow",
357
+ label: "Drone Follow",
358
+ category: "tracking",
359
+ description: "Elevated drone tracking subject",
360
+ promptHint: "aerial drone follow shot, elevated camera smoothly tracking the subject from above and behind",
361
+ },
362
+ {
363
+ id: "dolly-track",
364
+ label: "Dolly Track",
365
+ category: "tracking",
366
+ description: "Dolly on parallel track alongside subject",
367
+ promptHint: "dolly on a parallel track alongside the subject, smooth lateral tracking with strong foreground parallax",
368
+ },
369
+ {
370
+ id: "gimbal-walk",
371
+ label: "Gimbal Walk",
372
+ category: "tracking",
373
+ description: "Smooth walking shot on a 3-axis gimbal",
374
+ promptHint: "gimbal walk, smooth walking shot on a 3-axis gimbal with floating steady forward motion",
375
+ },
376
+ {
377
+ id: "ronin-glide",
378
+ label: "Ronin Glide",
379
+ category: "tracking",
380
+ description: "Slow gliding move on a Ronin/Movi gimbal",
381
+ promptHint: "ronin glide, slow gliding move on a Ronin or Movi gimbal, cinematic float without any shake",
382
+ },
383
+ {
384
+ id: "serpentine",
385
+ label: "Serpentine Track",
386
+ category: "tracking",
387
+ description: "Camera weaves through obstacles in S-curves",
388
+ promptHint: "serpentine track, the camera weaves through obstacles in S-curves, snaking forward along a winding path",
389
+ },
390
+
391
+ // Special angles / rigs
392
+ {
393
+ id: "pov",
394
+ label: "POV",
395
+ category: "special",
396
+ description: "First person point of view",
397
+ promptHint: "POV shot, first person perspective as seen through the subject's eyes",
398
+ },
399
+ {
400
+ id: "over-the-shoulder",
401
+ label: "Over The Shoulder",
402
+ category: "special",
403
+ description: "Frame past a character's shoulder",
404
+ promptHint: "over the shoulder shot, framing past one character's shoulder onto another",
405
+ },
406
+ {
407
+ id: "birds-eye",
408
+ label: "Bird's Eye",
409
+ category: "special",
410
+ description: "Direct top-down overhead view",
411
+ promptHint: "bird's eye view, direct overhead top-down shot looking straight down",
412
+ },
413
+ {
414
+ id: "worms-eye",
415
+ label: "Worm's Eye",
416
+ category: "special",
417
+ description: "Extreme low angle looking up",
418
+ promptHint: "worm's eye view, extreme low angle looking up at the subject",
419
+ },
420
+ {
421
+ id: "aerial",
422
+ label: "Aerial",
423
+ category: "special",
424
+ description: "High altitude drone-style shot",
425
+ promptHint: "aerial drone shot, high altitude slow forward movement over the landscape",
426
+ },
427
+ {
428
+ id: "helicopter",
429
+ label: "Helicopter",
430
+ category: "special",
431
+ description: "Wide high-altitude sweeping aerial",
432
+ promptHint: "helicopter shot, high altitude wide sweeping aerial pass with strong lateral movement",
433
+ },
434
+ {
435
+ id: "fly-over",
436
+ label: "Fly Over",
437
+ category: "special",
438
+ description: "Low fast aerial pass over the scene",
439
+ promptHint: "fly over shot, low altitude drone passing quickly over the scene with strong forward motion",
440
+ },
441
+ {
442
+ id: "flythrough",
443
+ label: "Flythrough",
444
+ category: "special",
445
+ description: "Camera flies through space",
446
+ promptHint: "flythrough shot, camera moving forward through the environment, weaving through obstacles",
447
+ },
448
+ {
449
+ id: "reveal",
450
+ label: "Reveal",
451
+ category: "special",
452
+ description: "Gradually reveal wider scene",
453
+ promptHint: "reveal shot, camera rises and tilts to gradually reveal the subject and wider scene",
454
+ },
455
+ {
456
+ id: "snorricam",
457
+ label: "Snorricam",
458
+ category: "special",
459
+ description: "Body-mounted camera (subject locked to frame)",
460
+ promptHint: "snorricam body-mounted shot, subject locked in frame while the world moves around them",
461
+ },
462
+ {
463
+ id: "rack-focus",
464
+ label: "Rack Focus",
465
+ category: "special",
466
+ description: "Pull focus between foreground and background",
467
+ promptHint: "rack focus, lens focus shifts from a foreground subject to a background subject (or vice versa), the unfocused plane blurs",
468
+ },
469
+
470
+ // Modern / social-video vocabulary
471
+ {
472
+ id: "handheld-vlog",
473
+ label: "Handheld Vlog",
474
+ category: "default",
475
+ description: "Casual vlog-style handheld",
476
+ promptHint: "casual handheld vlog-style camera, slight wandering and natural shake, talking-to-camera framing",
477
+ },
478
+ {
479
+ id: "pov-walk",
480
+ label: "POV Walk",
481
+ category: "tracking",
482
+ description: "First-person walking POV",
483
+ promptHint: "first-person POV walking camera, GoPro-style head-mounted perspective with natural footstep movement",
484
+ },
485
+ {
486
+ id: "velocity-edit",
487
+ label: "Velocity Edit",
488
+ category: "special",
489
+ description: "TikTok speed-ramp pacing",
490
+ promptHint: "rapid speed-ramped camera move with characteristic TikTok velocity-edit pacing, dynamic acceleration into and out of the shot",
491
+ },
492
+ {
493
+ id: "match-cut-zoom",
494
+ label: "Match Cut Zoom",
495
+ category: "zoom",
496
+ description: "Beat-timed zoom for cuts",
497
+ promptHint: "rapid zoom timed to a beat, suggesting a match cut to the next shot",
498
+ },
499
+ {
500
+ id: "screen-tap",
501
+ label: "Screen Tap",
502
+ category: "special",
503
+ description: "On-screen finger-tap transition",
504
+ promptHint: "camera transition triggered by an on-screen finger tap, TikTok-native pacing with snap to the next subject",
505
+ },
506
+ {
507
+ id: "phone-flip",
508
+ label: "Phone Flip",
509
+ category: "special",
510
+ description: "Front/rear camera flip",
511
+ promptHint: "camera-flip transition where the phone visibly rotates between the front and rear sensors, brief blur during the swap",
512
+ },
513
+ {
514
+ id: "gentle-drift",
515
+ label: "Gentle Drift",
516
+ category: "default",
517
+ description: "Slow ambient floating motion",
518
+ promptHint: "gentle camera drift, a slow ambient floating motion with no specific direction, the camera barely moves but never sits perfectly still, evocative of contemplative atmospheric shots",
519
+ },
520
+ {
521
+ id: "parallax",
522
+ label: "Parallax",
523
+ category: "default",
524
+ description: "Lateral motion with foreground/background depth separation",
525
+ promptHint: "parallax camera motion, lateral movement that emphasizes the depth separation between foreground and background elements, foreground objects appearing to move faster than distant ones",
526
+ },
527
+ ]
528
+
529
+ export const CAMERA_MOTION_CATEGORY_ORDER: ReadonlyArray<CameraMotionCategory> = [
530
+ "default",
531
+ "pan",
532
+ "tilt",
533
+ "zoom",
534
+ "dolly",
535
+ "truck",
536
+ "pedestal",
537
+ "roll",
538
+ "orbit",
539
+ "crane",
540
+ "tracking",
541
+ "special",
542
+ ]
543
+
544
+ export const CAMERA_MOTION_CATEGORY_LABELS: Record<CameraMotionCategory, string> = {
545
+ default: "Basic",
546
+ pan: "Pan",
547
+ tilt: "Tilt",
548
+ zoom: "Zoom",
549
+ dolly: "Dolly",
550
+ truck: "Truck",
551
+ pedestal: "Pedestal",
552
+ roll: "Roll",
553
+ orbit: "Orbit & Arc",
554
+ crane: "Crane & Boom",
555
+ tracking: "Tracking",
556
+ special: "Angles & Rigs",
557
+ }
558
+
559
+ const motionById = new Map<string, CameraMotion>(
560
+ CAMERA_MOTIONS.map((m) => [m.id, m]),
561
+ )
562
+
563
+ export function getCameraMotion(id: string | undefined | null): CameraMotion | undefined {
564
+ if (!id) return undefined
565
+ return motionById.get(id)
566
+ }
567
+
568
+ /** Human-readable label for the given motion id. Falls back to the id if unknown. */
569
+ export function getCameraMotionLabel(id: string | undefined | null, fallback?: string): string {
570
+ const m = getCameraMotion(id)
571
+ if (m) return m.label
572
+ if (fallback !== undefined) return fallback
573
+ return (id ?? "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
574
+ }
575
+
576
+ /** Descriptive prompt hint for the given motion id. Empty string when motion is "auto" or unknown. */
577
+ export function getCameraMotionPromptHint(id: string | undefined | null): string {
578
+ return getCameraMotion(id)?.promptHint ?? ""
579
+ }
580
+
581
+ export const CAMERA_MOTION_IDS: ReadonlyArray<string> = CAMERA_MOTIONS.map((m) => m.id)
582
+
583
+ // ---------------------------------------------------------------------------
584
+ // Graph-aware composer — start/end input handles
585
+ // ---------------------------------------------------------------------------
586
+
587
+ /**
588
+ * Compose a structural prompt-hint sentence from a camera-motion id plus
589
+ * arrays of start-state and end-state promptHints (collected by walking
590
+ * the source camera-motion node's startState / endState input handles
591
+ * upstream to find connected parameter nodes).
592
+ *
593
+ * - No connected nodes → bare motion promptHint.
594
+ * - Start only → "<motion>, beginning with <start hints joined>".
595
+ * - End only → "<motion>, ending with <end hints joined>".
596
+ * - Both → "<motion>, beginning with <start>, ending with <end>".
597
+ *
598
+ * Hints within each side are joined with " and " for grammatical flow.
599
+ * If multiple nodes are connected (e.g. Framing + Lighting + Tone), all
600
+ * three contribute their hint to the clause.
601
+ */
602
+ export function composeCameraMotionHintFromConnections(
603
+ motionId: string | undefined,
604
+ startHints: ReadonlyArray<string>,
605
+ endHints: ReadonlyArray<string>,
606
+ ): string {
607
+ const base = getCameraMotionPromptHint(motionId)
608
+ if (!base) return ""
609
+ const parts: string[] = [base]
610
+ const startClause = startHints.filter((h) => h && h.length > 0).join(" and ")
611
+ const endClause = endHints.filter((h) => h && h.length > 0).join(" and ")
612
+ if (startClause) parts.push(`beginning with ${startClause}`)
613
+ if (endClause) parts.push(`ending with ${endClause}`)
614
+ return parts.join(", ")
615
+ }