@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,229 @@
1
+ /**
2
+ * Canonical catalog of lighting choices.
3
+ *
4
+ * Lighting is a multi-category dimension of a shot — light comes from a
5
+ * direction, has a stylistic intent (three-point, Rembrandt, low-key, etc.),
6
+ * and is anchored to a time of day. Independent of optics (lens), framing,
7
+ * camera motion, and capture medium (camera-format).
8
+ *
9
+ * Shared between the picker UI and the prompt-hint injection on both the
10
+ * frontend DAG executor and the backend orchestrator.
11
+ */
12
+
13
+ export type LightingCategory =
14
+ | "time-of-day"
15
+ | "style"
16
+ | "direction"
17
+ | "lighting-ratio"
18
+ | "color-temperature"
19
+
20
+ export interface Lighting {
21
+ readonly id: string
22
+ readonly label: string
23
+ readonly category: LightingCategory
24
+ readonly description: string
25
+ readonly promptHint: string
26
+ }
27
+
28
+ export const LIGHTINGS: ReadonlyArray<Lighting> = [
29
+ // Time of day (15)
30
+ { id: "dawn", label: "Dawn", category: "time-of-day", description: "Pre-sunrise pale glow", promptHint: "dawn lighting, the pre-sunrise pale glow with deep blue ambient tones transitioning to soft pink and orange at the horizon, low contrast, cool ambient" },
31
+ { id: "sunrise", label: "Sunrise", category: "time-of-day", description: "Warm low sun, long shadows", promptHint: "sunrise lighting, warm low-angle sun with long shadows and soft golden tones" },
32
+ { id: "morning", label: "Morning", category: "time-of-day", description: "Bright fresh morning light", promptHint: "morning lighting, bright fresh ambient daylight with soft directional sun, warm white tones and clean shadows" },
33
+ { id: "noon", label: "Noon", category: "time-of-day", description: "Harsh overhead midday sun", promptHint: "midday noon lighting, harsh overhead sun with high contrast and strong vertical shadows" },
34
+ { id: "harsh-midday", label: "Harsh Midday", category: "time-of-day", description: "Bleached white-sun zenith", promptHint: "harsh midday white-sun lighting, sun directly overhead at zenith, blown-out white highlights and short hard shadows pooled directly beneath the subject" },
35
+ { id: "afternoon", label: "Afternoon", category: "time-of-day", description: "Warm late afternoon glow", promptHint: "afternoon lighting, warm directional late-afternoon sun with elongated shadows, saturated amber tones leading into golden hour" },
36
+ { id: "overcast", label: "Overcast", category: "time-of-day", description: "Soft diffused daylight", promptHint: "overcast daylight, soft diffused light from a uniformly cloudy sky with no harsh shadows" },
37
+ { id: "golden-hour", label: "Golden Hour", category: "time-of-day", description: "Warm sunset glow", promptHint: "golden hour lighting, warm sunset glow with soft directional sunlight and saturated golden tones" },
38
+ { id: "dusk", label: "Dusk", category: "time-of-day", description: "Post-sunset fading light", promptHint: "dusk lighting, the post-sunset fading light with deepening blue sky, residual warm horizon glow, and rising ambient cool tones" },
39
+ { id: "blue-hour", label: "Blue Hour", category: "time-of-day", description: "Cool dusk twilight", promptHint: "blue hour twilight lighting, cool desaturated tones just after sunset with soft ambient light" },
40
+ { id: "twilight", label: "Twilight", category: "time-of-day", description: "Between blue hour and night", promptHint: "twilight lighting, the transitional dusk between blue hour and full night, deep indigo sky with the last residual ambient glow on the horizon and emerging artificial city lights" },
41
+ { id: "night", label: "Night", category: "time-of-day", description: "Deep night, low ambient", promptHint: "night lighting, deep dark scene with minimal ambient illumination and high contrast highlights" },
42
+ { id: "midnight", label: "Midnight", category: "time-of-day", description: "Deepest night, near-black sky", promptHint: "midnight lighting, the deepest part of night with a near-black sky, minimal ambient illumination, scattered cool moonlight or starlight as the only natural source" },
43
+ { id: "moonlight", label: "Moonlight", category: "time-of-day", description: "Cool blue moonlit scene", promptHint: "moonlight, cool blue ambient illumination with soft directional moonlit highlights and deep shadows" },
44
+ { id: "neon-night", label: "Neon Night", category: "time-of-day", description: "Saturated neon city night", promptHint: "neon night lighting, saturated magenta and cyan neon glow against deep night ambient, urban cyberpunk feel" },
45
+
46
+ // Style (31)
47
+ { id: "three-point", label: "Three-Point", category: "style", description: "Classic key + fill + back", promptHint: "three-point lighting setup, balanced key light with fill and rim back-light, classical studio look" },
48
+ { id: "rembrandt", label: "Rembrandt", category: "style", description: "Triangle of light on cheek", promptHint: "Rembrandt lighting, distinctive triangle of light on the shadow-side cheek, painterly chiaroscuro" },
49
+ { id: "chiaroscuro", label: "Chiaroscuro", category: "style", description: "Strong light/dark contrast", promptHint: "chiaroscuro lighting, dramatic strong contrast between deep shadows and bright highlights" },
50
+ { id: "silhouette", label: "Silhouette", category: "style", description: "Subject as pure shape", promptHint: "silhouette lighting, subject rendered as a pure dark shape against a much brighter background" },
51
+ { id: "high-key", label: "High-Key", category: "style", description: "Bright, low-contrast", promptHint: "high-key lighting, bright low-contrast scene with minimal shadows and a light overall tone" },
52
+ { id: "low-key", label: "Low-Key", category: "style", description: "Dark, high-contrast", promptHint: "low-key lighting, dark high-contrast scene with deep shadows and selective highlights" },
53
+ { id: "split", label: "Split", category: "style", description: "Half-lit half-shadow face", promptHint: "split lighting, light hitting only one half of the face with the other half in deep shadow" },
54
+ // Classical portrait lighting setups
55
+ { id: "butterfly", label: "Butterfly", category: "style", description: "Glamour, nose-shadow butterfly", promptHint: "butterfly portrait lighting, key light placed directly above and slightly in front of the subject casting a small symmetrical butterfly-shaped shadow under the nose, classic Hollywood glamour with even illumination across both cheeks" },
56
+ { id: "loop", label: "Loop", category: "style", description: "Most natural portrait setup", promptHint: "loop portrait lighting, key light slightly to one side and above eye level casting a small loop-shaped nose shadow on the cheek that does not touch the shadow side, the most natural and flattering classical portrait setup" },
57
+ { id: "broad", label: "Broad", category: "style", description: "Wider face, friendly key", promptHint: "broad portrait lighting, key light striking the side of the face turned toward the camera so the larger lit cheek dominates the frame, makes the face appear wider and more open with a friendly approachable feel" },
58
+ { id: "short", label: "Short", category: "style", description: "Slimming, dramatic key", promptHint: "short portrait lighting, key light striking the side of the face turned away from the camera so the larger plane of the face falls into shadow, slims the face and adds dramatic dimension favored in editorial and male portraiture" },
59
+ { id: "hatchet", label: "Hatchet", category: "style", description: "Overhead skim, deep side shadow", promptHint: "hatchet portrait lighting, hard overhead light skimming the face from one side and leaving the opposite side in deep unrelieved shadow, theatrical and severe with a sharply divided contour" },
60
+ { id: "clamshell", label: "Clamshell", category: "style", description: "Beauty key + bottom reflector", promptHint: "clamshell beauty lighting, soft key light above the subject paired with a reflector or fill below the chin sandwiching the face evenly between two light sources, glossy commercial beauty look with twin catchlights and minimal shadow under the jaw" },
61
+ { id: "hard", label: "Hard", category: "style", description: "Sharp-edged shadows", promptHint: "hard lighting, sharp-edged crisp shadows from an undiffused direct light source" },
62
+ { id: "soft", label: "Soft", category: "style", description: "Diffused gentle light", promptHint: "soft diffused lighting, gentle wraparound light with smooth shadow transitions" },
63
+ { id: "practical", label: "Practical", category: "style", description: "In-scene visible lights", promptHint: "practical lighting, illumination from visible in-scene sources (lamps, windows, screens, candles)" },
64
+ // Modern social-video lighting styles
65
+ { id: "ring-light", label: "Ring Light", category: "style", description: "Beauty/vlog ring catchlight", promptHint: "ring light setup, even frontal illumination with the unmistakable circular catchlight in the eyes typical of beauty and vlog content" },
66
+ { id: "phone-screen-glow", label: "Phone Screen Glow", category: "style", description: "Cool screen underlight", promptHint: "phone screen glow lighting, cool blue underlighting from a phone screen casting upward onto the subject's face" },
67
+ { id: "selfie-natural", label: "Selfie Natural", category: "style", description: "Window-light selfie", promptHint: "window-light selfie aesthetic, soft natural daylight from a single window with casual handheld framing" },
68
+ { id: "natural", label: "Natural", category: "style", description: "Available ambient light", promptHint: "natural lighting, available ambient light with no artificial setup, organic and unpolished" },
69
+ { id: "volumetric", label: "Volumetric", category: "style", description: "Visible light beams in haze", promptHint: "volumetric lighting, visible god-ray light beams cutting through hazy atmosphere with strong directional shafts" },
70
+ { id: "noir", label: "Noir", category: "style", description: "High-contrast B&W film noir", promptHint: "film noir lighting, high-contrast black-and-white aesthetic with hard chiaroscuro shadows, venetian-blind slat shadows, and crushed deep blacks" },
71
+ // Flash & camera-flash styles
72
+ { id: "on-camera-flash", label: "On-Camera Flash", category: "style", description: "Paparazzi/iPhone direct flash", promptHint: "on-camera flash lighting, harsh direct frontal flash with blown-out highlights on skin, hard shadow falloff behind the subject and a darkened background, paparazzi and iPhone-flash signature" },
73
+ { id: "mirror-bounce-flash", label: "Mirror-Bounce Flash", category: "style", description: "Mirror-selfie flash bounce", promptHint: "mirror-selfie flash lighting, yellowish uneven LED-or-flash bouncing off the mirror surface with a hot flare spot, partially obscured face by the phone, classic mirror-selfie aesthetic" },
74
+ { id: "bounced-flash", label: "Bounced Flash", category: "style", description: "Soft ceiling-bounced fill", promptHint: "bounced flash lighting, on-camera flash redirected off a ceiling or wall to create soft top-down fill with gentle shadows, even skin tones and natural ambient feel" },
75
+ { id: "softbox-key", label: "Softbox Key", category: "style", description: "Large diffused fashion key", promptHint: "softbox key lighting, large diffused fashion-studio key light producing gentle wraparound illumination, soft falloff and clean even skin rendering" },
76
+ { id: "beauty-dish", label: "Beauty Dish", category: "style", description: "Hero light, crisp falloff", promptHint: "beauty dish lighting, semi-hard hero light with crisp shadow falloff and a distinctive round catchlight, slightly punchy contour and contrast favored in beauty and editorial portraits" },
77
+ { id: "gridded-snoot", label: "Gridded Snoot", category: "style", description: "Tight focused pool of light", promptHint: "gridded snoot lighting, tightly focused circular pool of light isolating the subject's face or shoulders against deep surrounding shadow, theatrical and controlled" },
78
+ { id: "silk-diffusion", label: "Silk Diffusion", category: "style", description: "Silk-softened gentle key", promptHint: "silk diffusion lighting, gentle key light passed through a large silk to soften shadow edges, producing creamy gradients and flattering skin tones" },
79
+ { id: "kicker-rim", label: "Kicker / Rim Accent", category: "style", description: "Low-side accent separator", promptHint: "kicker rim accent lighting, low side-rear accent light skimming the subject's edge to separate them from a darker background, adds dimension and depth" },
80
+ { id: "candlelight", label: "Candlelight", category: "style", description: "Warm flickering firelight", promptHint: "candlelight or firelight, warm flickering tungsten glow with soft falloff, low color temperature and gentle dancing shadows on nearby surfaces" },
81
+ { id: "edison-tungsten", label: "Edison Tungsten", category: "style", description: "Cozy warm globe-bulb glow", promptHint: "Edison-bulb tungsten lighting, warm low-CRI globe-bulb glow with visible filament hot-spots, cozy bar or cafe atmosphere with amber-orange ambient" },
82
+ { id: "dappled-light", label: "Dappled / Leaf-Filtered", category: "style", description: "Speckled foliage light", promptHint: "dappled leaf-filtered lighting, sunlight broken into a speckled pattern of bright spots and soft shadows as it passes through overhead foliage, organic and painterly" },
83
+ { id: "raking-sidelight", label: "Raking Sidelight", category: "style", description: "Extreme low side, texture", promptHint: "raking sidelight, extreme low-angle side light skimming across the surface to exaggerate texture and microcontour, long parallel shadows reveal every detail" },
84
+ { id: "stage-spotlight", label: "Stage Spotlight", category: "style", description: "Single hard overhead spot", promptHint: "stage spotlight lighting, single hard overhead theatre spotlight isolating the subject in a circular pool of light against pure black surroundings, dust motes catching the beam" },
85
+ { id: "underwater-caustics", label: "Underwater Caustics", category: "style", description: "Rippled refracted patterns", promptHint: "underwater caustics lighting, rippled wavy refracted light patterns dancing across the subject and surroundings, cool aquatic blue-green palette with shimmering highlights" },
86
+ { id: "bioluminescence", label: "Bioluminescence", category: "style", description: "Cool eerie biological glow", promptHint: "bioluminescence lighting, cool eerie cyan-green glow emanating from biological sources, soft self-illuminated highlights against deep ambient darkness, otherworldly atmosphere" },
87
+
88
+ // Direction (8)
89
+ { id: "front", label: "Front", category: "direction", description: "Light from camera direction", promptHint: "front lighting, light coming from the camera direction, flat even illumination across the subject" },
90
+ { id: "three-quarter", label: "3/4 Light", category: "direction", description: "Classic portrait key angle", promptHint: "three-quarter light, classic portrait key angle split between front and side at roughly 45 degrees off-camera, sculpts the face with a visible shadow on the far cheek and a defined nose shadow" },
91
+ { id: "side", label: "Side", category: "direction", description: "Light from one side", promptHint: "side lighting, light coming from the side of the subject, emphasizing texture and form" },
92
+ { id: "back-rim", label: "Back / Rim", category: "direction", description: "Backlight rim around subject", promptHint: "back-light or rim-light, light coming from behind the subject creating a bright rim around their silhouette" },
93
+ { id: "silhouette-backlight", label: "Silhouette Backlight", category: "direction", description: "Bright halo, dark subject", promptHint: "silhouette backlight, strong light source directly behind the subject rendering them as a dark shape with a luminous halo edge and bright background bloom, dramatic contour against the sky or window" },
94
+ { id: "top-overhead", label: "Top / Overhead",category: "direction", description: "Light from directly above", promptHint: "top-down overhead lighting, light coming from directly above the subject creating shadows under the eyes and chin" },
95
+ { id: "under-uplight", label: "Under / Uplight",category: "direction", description: "Light from below", promptHint: "uplighting from below, light coming from below the subject creating an unsettling theatrical look" },
96
+ { id: "window", label: "Window", category: "direction", description: "Soft sidelight from window", promptHint: "window lighting, soft directional light from a single window source with natural falloff" },
97
+
98
+ // Lighting ratio (6) — relative key-to-shadow brightness
99
+ { id: "ratio-1-1", label: "1:1", category: "lighting-ratio", description: "Flat, no shadow contrast", promptHint: "1:1 lighting ratio, flat even illumination across the subject with no contrast between key and fill, beauty and catalog look that flattens dimension" },
100
+ { id: "ratio-1-2", label: "1:2", category: "lighting-ratio", description: "Soft one-stop falloff", promptHint: "1:2 lighting ratio, gentle soft modeling with roughly one stop of falloff from key to shadow side, classic flattering portraiture with mild dimension" },
101
+ { id: "ratio-1-3", label: "1:3", category: "lighting-ratio", description: "Moderate two-stop contrast", promptHint: "1:3 lighting ratio, moderate contrast with about two stops between the lit and shadow sides, well-defined facial dimension and visible but recovered shadow detail" },
102
+ { id: "ratio-1-4", label: "1:4", category: "lighting-ratio", description: "Strong editorial contrast", promptHint: "1:4 lighting ratio, strong contrast with deepening shadows on the unlit side, dramatic editorial feel with sculpted form and minimal shadow detail" },
103
+ { id: "ratio-1-8", label: "1:8", category: "lighting-ratio", description: "Extreme low-key chiaroscuro", promptHint: "1:8 lighting ratio, extreme chiaroscuro with near-black shadow side balanced against a single bright key, classic low-key cinema atmosphere" },
104
+ { id: "ratio-1-16", label: "1:16", category: "lighting-ratio", description: "Single-source film-noir falloff", promptHint: "1:16 lighting ratio, single-source key against pitch-black shadow side, theatrical film-noir falloff with no fill and crushed unlit detail" },
105
+
106
+ // Color temperature (6) — Kelvin from warm to cool
107
+ { id: "temp-2700k", label: "2700K Candle", category: "color-temperature", description: "Deep amber candle/tungsten", promptHint: "2700K warm candle and low-tungsten color temperature, deep amber and orange cast across the scene, intimate and cozy mood with rich golden highlights" },
108
+ { id: "temp-3200k", label: "3200K Tungsten", category: "color-temperature", description: "Warm yellow interior", promptHint: "3200K tungsten color temperature, warm yellow-orange cast typical of classic incandescent home interiors, lived-in domestic ambient" },
109
+ { id: "temp-4000k", label: "4000K Mixed", category: "color-temperature", description: "Neutral white", promptHint: "4000K neutral white color temperature, balanced midpoint between warm tungsten and cool daylight, clean office and mixed-source feel without strong color cast" },
110
+ { id: "temp-5600k", label: "5600K Daylight", category: "color-temperature", description: "Daylight-balanced midday sun", promptHint: "5600K daylight-balanced color temperature, neutral midday white sun with accurate color rendering and no warm or cool bias" },
111
+ { id: "temp-6500k", label: "6500K Overcast", category: "color-temperature", description: "Slightly cool blue cast", promptHint: "6500K overcast-cool color temperature, slightly blue cast typical of overcast skies and high-noon shadow areas, subtly cool and clinical" },
112
+ { id: "temp-9000k", label: "9000K Shade", category: "color-temperature", description: "Distinctly cool blue shade", promptHint: "9000K open-shade color temperature, distinctly cool blue cast found in deep shade and cloud-shadowed mountain air, chilled and atmospheric" },
113
+ ] as const
114
+
115
+ export const LIGHTING_CATEGORY_ORDER: ReadonlyArray<LightingCategory> = [
116
+ "time-of-day",
117
+ "style",
118
+ "direction",
119
+ "lighting-ratio",
120
+ "color-temperature",
121
+ ]
122
+
123
+ export const LIGHTING_CATEGORY_LABELS: Record<LightingCategory, string> = {
124
+ "time-of-day": "Time of Day",
125
+ style: "Style",
126
+ direction: "Direction",
127
+ "lighting-ratio": "Lighting Ratio",
128
+ "color-temperature": "Color Temperature",
129
+ }
130
+
131
+ const lightingById = new Map<string, Lighting>(LIGHTINGS.map((l) => [l.id, l]))
132
+
133
+ export function getLighting(id: string | undefined | null): Lighting | undefined {
134
+ if (!id) return undefined
135
+ return lightingById.get(id)
136
+ }
137
+
138
+ export function getLightingLabel(id: string | undefined | null, fallback?: string): string {
139
+ const l = getLighting(id)
140
+ if (l) return l.label
141
+ if (fallback !== undefined) return fallback
142
+ return (id ?? "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
143
+ }
144
+
145
+ export function getLightingPromptHint(id: string | undefined | null): string {
146
+ return getLighting(id)?.promptHint ?? ""
147
+ }
148
+
149
+ export const LIGHTING_IDS: ReadonlyArray<string> = LIGHTINGS.map((l) => l.id)
150
+
151
+ /**
152
+ * Maps each LightingCategory to the consumer data field name that holds the
153
+ * selected entry id for that category. Multi-category lighting: a consumer
154
+ * (image/video) can independently set a value in each of the 3 dimensions.
155
+ *
156
+ * Field names use 'lighting' prefix where the category name is generic or
157
+ * would collide with other dimensions (e.g. Temporal also has a 'direction'
158
+ * category; 'style' is generic across many dims).
159
+ */
160
+ export const LIGHTING_FIELD_BY_CATEGORY: Record<
161
+ LightingCategory,
162
+ | "timeOfDay"
163
+ | "lightingStyle"
164
+ | "lightingDirection"
165
+ | "lightingRatio"
166
+ | "colorTemperature"
167
+ > = {
168
+ "time-of-day": "timeOfDay",
169
+ style: "lightingStyle",
170
+ direction: "lightingDirection",
171
+ "lighting-ratio": "lightingRatio",
172
+ "color-temperature": "colorTemperature",
173
+ }
174
+
175
+ /**
176
+ * Shape of the per-category lighting fields on LightingData and all 9 consumer
177
+ * data types. All fields optional — user may set zero, one, or all categories.
178
+ */
179
+ export interface LightingValue {
180
+ timeOfDay?: string
181
+ /** Lighting style — single id or up to 2 ids for layered setups
182
+ * (e.g. ["key", "rim"], ["soft", "hard"], ["beauty-dish", "kicker"]). */
183
+ lightingStyle?: string | ReadonlyArray<string>
184
+ lightingDirection?: string
185
+ /** Lighting ratio id from LIGHTINGS (relative key-to-shadow brightness, e.g. "ratio-1-2"). */
186
+ lightingRatio?: string
187
+ /** Color temperature id from LIGHTINGS (Kelvin warmth/coolness, e.g. "temp-5600k"). */
188
+ colorTemperature?: string
189
+ }
190
+
191
+ /**
192
+ * Aggregate all enabled per-category lighting prompt hints from a consumer's
193
+ * data, in canonical category order (time-of-day, style, direction).
194
+ *
195
+ * Accepts a loosely typed record (the helper is shared between strongly typed
196
+ * frontend node data and the backend's `Record<string, unknown>` workflow
197
+ * data). Non-string values are ignored.
198
+ *
199
+ * @param data the consumer data record (must include optional timeOfDay /
200
+ * lightingStyle / lightingDirection fields)
201
+ */
202
+ export function buildLightingHints(
203
+ data: Record<string, unknown> & {
204
+ timeOfDay?: unknown
205
+ lightingStyle?: unknown
206
+ lightingDirection?: unknown
207
+ lightingRatio?: unknown
208
+ colorTemperature?: unknown
209
+ },
210
+ ): string[] {
211
+ const hints: string[] = []
212
+ for (const category of LIGHTING_CATEGORY_ORDER) {
213
+ const field = LIGHTING_FIELD_BY_CATEGORY[category]
214
+ const raw = data[field]
215
+ // lightingStyle accepts string | string[] (multi-pick max 2). Other
216
+ // categories are single-pick and may also tolerate arrays defensively.
217
+ if (typeof raw === "string" && raw.length > 0) {
218
+ const hint = getLightingPromptHint(raw)
219
+ if (hint) hints.push(hint)
220
+ } else if (Array.isArray(raw)) {
221
+ for (const item of raw) {
222
+ if (typeof item !== "string" || item.length === 0) continue
223
+ const hint = getLightingPromptHint(item)
224
+ if (hint) hints.push(hint)
225
+ }
226
+ }
227
+ }
228
+ return hints
229
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Canonical catalog of loop-friendly subject presets ("Loop Subject").
3
+ *
4
+ * The Loop Subject parameter node emits one of these prompt fragments,
5
+ * pre-tuned for VEO 3.1 perfect-loop generation. Each prompt is shaped to
6
+ * either:
7
+ * - have inherently cyclical / ambient motion the model knows from the
8
+ * subject alone (clouds drift, aurora undulates, embers flicker), or
9
+ * - anchor periodicity via the "fractal" / "self-similar" tokens, which
10
+ * in user testing strongly biased VEO toward loop-tolerant output even
11
+ * when the prompt also contained directional cues (vanishing points,
12
+ * receding lines).
13
+ *
14
+ * These are NOT motion prompts — they describe the SCENE for the image
15
+ * generator. The motion side (the perfect-loop seal phrase) is appended
16
+ * separately at the i2v stage. Keeping the two concerns separated lets
17
+ * users animate the same loop-friendly image with different motion intent
18
+ * and lets us refine each library independently.
19
+ */
20
+
21
+ export type LoopSubjectCategory = "realistic" | "abstract"
22
+
23
+ export interface LoopSubject {
24
+ readonly id: string
25
+ readonly label: string
26
+ readonly category: LoopSubjectCategory
27
+ readonly description: string
28
+ /** Drop-in prompt text for Generate Image. Wired into the prompt input
29
+ * via FieldMappings, same as Setting / Motion / Mood Parameter nodes. */
30
+ readonly promptHint: string
31
+ }
32
+
33
+ export const LOOP_SUBJECT_CATEGORY_ORDER: ReadonlyArray<LoopSubjectCategory> = [
34
+ "realistic",
35
+ "abstract",
36
+ ] as const
37
+
38
+ export const LOOP_SUBJECT_CATEGORY_LABELS: Readonly<Record<LoopSubjectCategory, string>> = {
39
+ realistic: "Realistic",
40
+ abstract: "Abstract / VJ",
41
+ } as const
42
+
43
+ export const LOOP_SUBJECTS: ReadonlyArray<LoopSubject> = [
44
+ // -------------------- Realistic --------------------
45
+ {
46
+ id: "aurora",
47
+ label: "Aurora",
48
+ category: "realistic",
49
+ description: "Aurora borealis ribbons over a horizon",
50
+ promptHint: "aurora borealis ribbons in green and violet over a snowy mountain horizon, wide centered framing, low ambient starlight, deep cosmic backdrop, no foreground subject",
51
+ },
52
+ {
53
+ id: "clouds",
54
+ label: "Drifting Clouds",
55
+ category: "realistic",
56
+ description: "Cumulus clouds against open sky",
57
+ promptHint: "soft cumulus clouds against a deep blue sky, wide centered horizon, balanced composition, even ambient daylight, no harsh directional shadows, no foreground subject",
58
+ },
59
+ {
60
+ id: "ocean-waves",
61
+ label: "Ocean Waves",
62
+ category: "realistic",
63
+ description: "Open ocean with cyclical waves",
64
+ promptHint: "endless ocean horizon with rolling cyclical waves, wide centered composition, even daylight, deep blue water, no foreground subject",
65
+ },
66
+ {
67
+ id: "starfield",
68
+ label: "Starfield",
69
+ category: "realistic",
70
+ description: "Deep space stars",
71
+ promptHint: "deep space starfield with countless distant stars and faint nebula gas, centered radial composition, no perspective depth, dark cosmic background, no foreground subject",
72
+ },
73
+ {
74
+ id: "fireplace",
75
+ label: "Fireplace",
76
+ category: "realistic",
77
+ description: "Embers and dancing flame",
78
+ promptHint: "warm fireplace embers and dancing flame, intimate close framing, deep amber and ember-orange palette, ambient firelight glow, no foreground subject",
79
+ },
80
+ {
81
+ id: "rain",
82
+ label: "Rain on Glass",
83
+ category: "realistic",
84
+ description: "Rain droplets on a window",
85
+ promptHint: "close-up of rain droplets running down a glass window, soft out-of-focus city lights bokeh in the background, centered composition, cool blue ambient tone, no foreground subject",
86
+ },
87
+ {
88
+ id: "snowfall",
89
+ label: "Snowfall",
90
+ category: "realistic",
91
+ description: "Snow falling against a dark sky",
92
+ promptHint: "soft snow falling steadily against a deep night sky, scattered evergreen silhouettes at the bottom edge, even ambient blue moonlight, balanced composition, no foreground subject",
93
+ },
94
+ {
95
+ id: "lightning-storm",
96
+ label: "Lightning Storm",
97
+ category: "realistic",
98
+ description: "Distant lightning over storm clouds",
99
+ promptHint: "distant heat lightning flickering through massive storm clouds over a flat horizon, deep cinematic atmosphere, wide centered framing, dramatic backlit cloud structure, no foreground subject",
100
+ },
101
+ {
102
+ id: "galaxy",
103
+ label: "Galaxy",
104
+ category: "realistic",
105
+ description: "Spiral galaxy in deep space",
106
+ promptHint: "spiral galaxy with swirling arms of stars and luminous gas, dust lanes radiating from a bright core, centered radial composition, deep cosmic black background, no perspective depth",
107
+ },
108
+ {
109
+ id: "nebula",
110
+ label: "Nebula",
111
+ category: "realistic",
112
+ description: "Colorful cosmic gas cloud",
113
+ promptHint: "vivid cosmic nebula of red and blue gas with embedded stars, billowing fractal cloud structure, centered composition, deep space backdrop, no foreground subject",
114
+ },
115
+ {
116
+ id: "sunbeams",
117
+ label: "Sunbeams",
118
+ category: "realistic",
119
+ description: "Light rays through forest canopy",
120
+ promptHint: "golden sunbeams cutting through a tall forest canopy with floating dust motes catching the light, centered vertical composition, deep green ambient tone, no human figures, no foreground subject",
121
+ },
122
+ {
123
+ id: "cherry-blossoms",
124
+ label: "Cherry Blossoms",
125
+ category: "realistic",
126
+ description: "Falling pink petals",
127
+ promptHint: "soft pink cherry blossom petals drifting through the air against a pale sky, scattered petals at every depth, balanced centered composition, even ambient daylight, no foreground subject",
128
+ },
129
+ {
130
+ id: "underwater",
131
+ label: "Underwater Light",
132
+ category: "realistic",
133
+ description: "Sunbeams and bubbles below water",
134
+ promptHint: "sunbeams cutting down through deep blue ocean water with rising silver air bubbles and floating particulate, centered cathedral-light composition, no surface horizon visible, no foreground subject",
135
+ },
136
+ {
137
+ id: "embers",
138
+ label: "Floating Embers",
139
+ category: "realistic",
140
+ description: "Glowing embers rising from fire",
141
+ promptHint: "glowing orange embers floating slowly upward through dark smoky air, scattered at every depth, centered composition, deep warm ambient glow, no foreground subject",
142
+ },
143
+
144
+ // -------------------- Abstract / VJ --------------------
145
+ {
146
+ id: "tunnel",
147
+ label: "Tunnel — Exciting",
148
+ category: "abstract",
149
+ description: "Fractal neon tunnel with depth",
150
+ promptHint: "infinite fractal tunnel of glowing neon ribbons receding into a vanishing point, centered symmetric composition, deep volumetric light, no foreground subject",
151
+ },
152
+ {
153
+ id: "tunnel-clean",
154
+ label: "Tunnel — Clean",
155
+ category: "abstract",
156
+ description: "Periodic tunnel of geometric filaments",
157
+ promptHint: "infinite fractal tunnel of glowing neon geometric filaments, self-similar repeating depth that recurses to infinity, centered symmetric composition, deep volumetric light, dark void background, no foreground subject",
158
+ },
159
+ {
160
+ id: "kaleidoscope",
161
+ label: "Kaleidoscope",
162
+ category: "abstract",
163
+ description: "Fractal mandala with radial symmetry",
164
+ promptHint: "fractal kaleidoscope mandala in jewel tones, eight-fold radial symmetry, centered composition, even illumination, intricate self-similar geometric detail, no focal subject",
165
+ },
166
+ {
167
+ id: "plasma",
168
+ label: "Plasma Field",
169
+ category: "abstract",
170
+ description: "Swirling fractal energy",
171
+ promptHint: "abstract plasma energy field of swirling fractal patterns, electric cyan and magenta, centered radial composition, dark void background, no foreground subject",
172
+ },
173
+ {
174
+ id: "particle-swirl",
175
+ label: "Particle Swirl",
176
+ category: "abstract",
177
+ description: "Glowing motes in fractal flow",
178
+ promptHint: "abstract swirling particle field with thousands of glowing motes, fractal flow lines, centered radial composition, deep dark backdrop, no foreground subject",
179
+ },
180
+ {
181
+ id: "fractal-zoom",
182
+ label: "Fractal Zoom",
183
+ category: "abstract",
184
+ description: "Recursive Mandelbrot-style depth",
185
+ promptHint: "infinite recursive fractal zoom into a kaleidoscopic mandala, self-similar at every scale, identical pattern repeats with each zoom level, glowing neon geometry, dark cosmic background, no foreground subject",
186
+ },
187
+ {
188
+ id: "synthwave",
189
+ label: "Synthwave Grid",
190
+ category: "abstract",
191
+ description: "Retro neon grid with sun",
192
+ promptHint: "retro 1980s synthwave neon grid floor stretching to the horizon with a glowing pink and magenta sun, centered symmetric composition, deep purple sky with starfield, fractal repeating grid lines, no foreground subject",
193
+ },
194
+ {
195
+ id: "wireframe",
196
+ label: "Wireframe Sphere",
197
+ category: "abstract",
198
+ description: "Rotating wireframe geometry",
199
+ promptHint: "glowing neon wireframe sphere with intricate self-similar geometric subdivisions, centered radial symmetry, dark void background, electric cyan rim glow, fractal mesh structure, no foreground subject",
200
+ },
201
+ {
202
+ id: "equalizer",
203
+ label: "Equalizer Bars",
204
+ category: "abstract",
205
+ description: "Pulsing audio visualizer",
206
+ promptHint: "vertical audio equalizer bars in vibrant neon gradient pulsing at different heights, centered symmetric composition, dark backdrop with soft glow, repeating periodic structure, no foreground subject",
207
+ },
208
+ {
209
+ id: "dna-helix",
210
+ label: "DNA Helix",
211
+ category: "abstract",
212
+ description: "Rotating double helix",
213
+ promptHint: "glowing neon DNA double helix structure with periodic identical rungs, centered vertical symmetric composition, electric cyan and magenta strands, dark void background, fractal repeating geometry, no foreground subject",
214
+ },
215
+ {
216
+ id: "liquid-chrome",
217
+ label: "Liquid Chrome",
218
+ category: "abstract",
219
+ description: "Reflective metallic ripples",
220
+ promptHint: "liquid chrome surface with mirror-finish metallic ripples and concentric self-similar wave patterns, centered radial composition, iridescent reflections, deep dark backdrop, no foreground subject",
221
+ },
222
+ {
223
+ id: "hologram",
224
+ label: "Holographic Foil",
225
+ category: "abstract",
226
+ description: "Iridescent shifting colors",
227
+ promptHint: "iridescent holographic foil surface with shifting rainbow gradient, fractal interference patterns, centered symmetric composition, deep dark backdrop, no foreground subject",
228
+ },
229
+ {
230
+ id: "vortex",
231
+ label: "Vortex",
232
+ category: "abstract",
233
+ description: "Swirling energy spiral",
234
+ promptHint: "swirling energy vortex of glowing neon filaments spiraling toward a luminous center, centered radial composition, fractal self-similar spiral arms, dark cosmic backdrop, no foreground subject",
235
+ },
236
+ {
237
+ id: "honeycomb",
238
+ label: "Honeycomb",
239
+ category: "abstract",
240
+ description: "Hexagonal pattern pulsing",
241
+ promptHint: "tessellated hexagonal honeycomb pattern in glowing neon, perfect six-fold radial symmetry, identical repeating cells across the entire frame, centered composition, dark void background, fractal self-similar geometric detail, no foreground subject",
242
+ },
243
+ {
244
+ id: "glitch",
245
+ label: "Glitch Static",
246
+ category: "abstract",
247
+ description: "Digital noise corruption",
248
+ promptHint: "abstract digital glitch field of pixelated RGB color shift, scan lines, and self-similar noise blocks, centered composition, electric cyan and magenta corruption aesthetic, dark backdrop, no foreground subject",
249
+ },
250
+ {
251
+ id: "black-hole",
252
+ label: "Black Hole",
253
+ category: "abstract",
254
+ description: "Event horizon swirl",
255
+ promptHint: "black hole event horizon with luminous accretion disk of glowing plasma swirling around a perfectly dark center, centered radial symmetry, fractal self-similar gas filaments, deep cosmic backdrop, no foreground subject",
256
+ },
257
+ {
258
+ id: "geometric-morph",
259
+ label: "Geometric Morph",
260
+ category: "abstract",
261
+ description: "Shifting platonic solids",
262
+ promptHint: "abstract array of glowing neon platonic solid wireframes morphing through self-similar fractal subdivisions, centered radial symmetric composition, deep dark backdrop, electric rim lighting, no foreground subject",
263
+ },
264
+ ]
265
+
266
+ export function getLoopSubject(id: string): LoopSubject | undefined {
267
+ return LOOP_SUBJECTS.find((s) => s.id === id)
268
+ }
269
+
270
+ export function getLoopSubjectLabel(id: string): string {
271
+ return getLoopSubject(id)?.label ?? id
272
+ }
273
+
274
+ export function getLoopSubjectPromptHint(id: string): string {
275
+ return getLoopSubject(id)?.promptHint ?? ""
276
+ }