@nodaro/prompts 1.7.3 → 1.8.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 +5422 -4269
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1198 -61
- package/dist/index.d.ts +1198 -61
- package/dist/index.js +5336 -4271
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/b7-person-pack-e2e.test.ts +37 -0
- package/src/__tests__/catalog-funnel-ratchet.test.ts +97 -0
- package/src/__tests__/catalog-packs.test.ts +130 -0
- package/src/__tests__/catalog-sidecar-coverage.test.ts +27 -0
- package/src/__tests__/catalog-terms.test.ts +162 -0
- package/src/__tests__/character-default-role.test.ts +3 -2
- package/src/__tests__/content-free-contract.test.ts +45 -0
- package/src/__tests__/dod-replace-pack-acceptance.test.ts +41 -0
- package/src/__tests__/fixtures/parameter-hint-golden.json +2959 -0
- package/src/__tests__/fixtures/person-sector-pack.ts +27 -0
- package/src/__tests__/i18n-entry-completeness.test.ts +5 -2
- package/src/__tests__/parameter-hint-mode.test.ts +385 -0
- package/src/__tests__/parameter-prompt-hint-pack-fallback.test.ts +25 -0
- package/src/__tests__/person-packs.test.ts +166 -0
- package/src/__tests__/project-all-catalogs.test.ts +26 -0
- package/src/__tests__/prompt-builder.test.ts +53 -0
- package/src/__tests__/registered-catalogs-funnel.test.ts +30 -0
- package/src/__tests__/registered-catalogs-guard.test.ts +11 -0
- package/src/__tests__/term.test.ts +104 -0
- package/src/__tests__/transitions.test.ts +8 -5
- package/src/__tests__/upstream-immutability.test.ts +23 -0
- package/src/action-fx.ts +67 -17
- package/src/aesthetic.ts +54 -6
- package/src/atmosphere.ts +64 -23
- package/src/backdrop.ts +44 -30
- package/src/camera-format.ts +33 -11
- package/src/camera-motions.ts +89 -0
- package/src/catalog-packs.ts +125 -0
- package/src/catalog-sidecar-coverage.ts +36 -0
- package/src/character-fx.ts +112 -39
- package/src/color-look.ts +44 -27
- package/src/composition-effects.ts +21 -7
- package/src/era.ts +24 -0
- package/src/exposure-settings.ts +76 -18
- package/src/framing.ts +100 -0
- package/src/held-prop.ts +125 -63
- package/src/identity-lock.ts +12 -5
- package/src/image-reference-doctrine.ts +55 -0
- package/src/index.ts +5 -0
- package/src/instrumentation.ts +148 -57
- package/src/lens.ts +31 -15
- package/src/lighting.ts +120 -59
- package/src/loop-subject.ts +27 -1
- package/src/materials.ts +123 -69
- package/src/mood.ts +123 -51
- package/src/music-genre.ts +171 -67
- package/src/music-mood.ts +85 -17
- package/src/parameter-prompt-hint.ts +168 -56
- package/src/person-packs.ts +182 -0
- package/src/person.ts +506 -408
- package/src/photo-genre.ts +37 -22
- package/src/photographer.ts +138 -1
- package/src/picker-catalogs.ts +86 -40
- package/src/pose.ts +105 -40
- package/src/post-process-effects.ts +51 -8
- package/src/prompt-builder.ts +16 -1
- package/src/render-quality.ts +25 -7
- package/src/setting.ts +30 -14
- package/src/style.ts +33 -15
- package/src/styling.ts +155 -94
- package/src/temporal.ts +80 -18
- package/src/term.ts +155 -0
- package/src/transitions.ts +73 -26
- package/src/voice-character.ts +190 -90
- package/src/voice-delivery.ts +83 -12
package/src/instrumentation.ts
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* Synth / Guitar / Bass / Brass / Woodwinds / Strings / World.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { resolveTerm, type PickerHintMode } from "./term.js"
|
|
15
|
+
|
|
14
16
|
export type InstrumentCategory =
|
|
15
17
|
| "drums"
|
|
16
18
|
| "percussion"
|
|
@@ -48,8 +50,19 @@ export interface InstrumentationEntry {
|
|
|
48
50
|
readonly label: string
|
|
49
51
|
readonly description: string
|
|
50
52
|
readonly promptHint: string
|
|
53
|
+
/**
|
|
54
|
+
* Optional authored compact term (see `term.ts` for the convention).
|
|
55
|
+
* Authored only where the lowercased label is NOT what a producer would
|
|
56
|
+
* write in a prompt — bare modifiers whose trade term carries a category
|
|
57
|
+
* noun ("Stab" → "synth stab", "Polished" → "polished production"), bare
|
|
58
|
+
* genre words in the singing-style slot ("Pop" → "pop singing"), and
|
|
59
|
+
* annotated labels ("Trap (Melodic)" → "melodic trap singing"). Everywhere
|
|
60
|
+
* else the lowercased label IS the term and nothing is authored.
|
|
61
|
+
*/
|
|
62
|
+
readonly term?: string
|
|
51
63
|
}
|
|
52
64
|
|
|
65
|
+
/** Inherits the optional `term` from `InstrumentationEntry`. */
|
|
53
66
|
export interface CategorizedInstrument extends InstrumentationEntry {
|
|
54
67
|
readonly category: InstrumentCategory
|
|
55
68
|
}
|
|
@@ -58,7 +71,7 @@ export const INSTRUMENTS: ReadonlyArray<CategorizedInstrument> = [
|
|
|
58
71
|
// -------- Drums --------
|
|
59
72
|
{ id: "drums", label: "Drums", description: "Acoustic drum kit", promptHint: "drums", category: "drums" },
|
|
60
73
|
{ id: "drum-machine", label: "Drum Machine", description: "Programmed beats", promptHint: "drum machine", category: "drums" },
|
|
61
|
-
{ id: "808", label: "808", description: "Roland TR-808 bass + kit", promptHint: "808 drums", category: "drums" },
|
|
74
|
+
{ id: "808", label: "808", description: "Roland TR-808 bass + kit", promptHint: "808 drums", category: "drums", term: "808 drums" },
|
|
62
75
|
{ id: "live-drums", label: "Live Drums", description: "Recorded acoustic kit", promptHint: "live drums", category: "drums" },
|
|
63
76
|
{ id: "breakbeats", label: "Breakbeats", description: "Sampled drum loops", promptHint: "breakbeats", category: "drums" },
|
|
64
77
|
{ id: "trap-drums", label: "Trap Drums", description: "Hi-hat rolls + 808", promptHint: "trap drums", category: "drums" },
|
|
@@ -86,11 +99,11 @@ export const INSTRUMENTS: ReadonlyArray<CategorizedInstrument> = [
|
|
|
86
99
|
// -------- Keys --------
|
|
87
100
|
{ id: "piano", label: "Piano", description: "Acoustic grand or upright", promptHint: "piano", category: "keys" },
|
|
88
101
|
{ id: "electric-piano", label: "Electric Piano", description: "Rhodes / Wurlitzer-style", promptHint: "electric piano", category: "keys" },
|
|
89
|
-
{ id: "wurlitzer", label: "Wurlitzer", description: "Vintage Wurlitzer EP", promptHint: "Wurlitzer electric piano", category: "keys" },
|
|
90
|
-
{ id: "rhodes", label: "Rhodes", description: "Vintage Rhodes EP", promptHint: "Rhodes electric piano",category: "keys" },
|
|
102
|
+
{ id: "wurlitzer", label: "Wurlitzer", description: "Vintage Wurlitzer EP", promptHint: "Wurlitzer electric piano", category: "keys", term: "wurlitzer electric piano" },
|
|
103
|
+
{ id: "rhodes", label: "Rhodes", description: "Vintage Rhodes EP", promptHint: "Rhodes electric piano",category: "keys", term: "rhodes electric piano" },
|
|
91
104
|
{ id: "clavinet", label: "Clavinet", description: "Funky Hohner D6", promptHint: "clavinet", category: "keys" },
|
|
92
105
|
{ id: "organ", label: "Organ", description: "Hammond or pipe organ", promptHint: "organ", category: "keys" },
|
|
93
|
-
{ id: "hammond-organ", label: "Hammond B3", description: "Soul / rock B3 organ", promptHint: "Hammond B3 organ", category: "keys" },
|
|
106
|
+
{ id: "hammond-organ", label: "Hammond B3", description: "Soul / rock B3 organ", promptHint: "Hammond B3 organ", category: "keys", term: "hammond b3 organ" },
|
|
94
107
|
{ id: "pipe-organ", label: "Pipe Organ", description: "Cathedral / church", promptHint: "pipe organ", category: "keys" },
|
|
95
108
|
{ id: "accordion", label: "Accordion", description: "Bellows-driven reed", promptHint: "accordion", category: "keys" },
|
|
96
109
|
{ id: "mellotron", label: "Mellotron", description: "Tape-loop keyboard", promptHint: "mellotron", category: "keys" },
|
|
@@ -102,19 +115,19 @@ export const INSTRUMENTS: ReadonlyArray<CategorizedInstrument> = [
|
|
|
102
115
|
{ id: "lead-synth", label: "Lead Synth", description: "Cutting melodic synth", promptHint: "lead synth", category: "synth" },
|
|
103
116
|
{ id: "pluck-synth", label: "Pluck Synth", description: "Short, plucked timbre", promptHint: "pluck synth", category: "synth" },
|
|
104
117
|
{ id: "pads", label: "Synth Pads", description: "Sustained synth textures", promptHint: "synth pads", category: "synth" },
|
|
105
|
-
{ id: "arpeggiator", label: "Arpeggiator", description: "Sequenced arp patterns", promptHint: "arpeggiated synth", category: "synth" },
|
|
118
|
+
{ id: "arpeggiator", label: "Arpeggiator", description: "Sequenced arp patterns", promptHint: "arpeggiated synth", category: "synth", term: "arpeggiated synth" },
|
|
106
119
|
{ id: "vocoder", label: "Vocoder", description: "Voice-modulated synth", promptHint: "vocoder", category: "synth" },
|
|
107
120
|
{ id: "talkbox", label: "Talkbox", description: "Mouth-shaped synth", promptHint: "talkbox", category: "synth" },
|
|
108
|
-
{ id: "modular-synth", label: "Modular", description: "Patchable analog synth", promptHint: "modular synth", category: "synth" },
|
|
121
|
+
{ id: "modular-synth", label: "Modular", description: "Patchable analog synth", promptHint: "modular synth", category: "synth", term: "modular synth" },
|
|
109
122
|
{ id: "fm-synth", label: "FM Synth", description: "Frequency-modulation synth",promptHint: "FM synth", category: "synth" },
|
|
110
|
-
{ id: "wavetable-synth", label: "Wavetable", description: "Wavetable-scanning synth", promptHint: "wavetable synth", category: "synth" },
|
|
111
|
-
{ id: "stab-synth", label: "Stab", description: "Short, percussive chord", promptHint: "synth stab", category: "synth" },
|
|
123
|
+
{ id: "wavetable-synth", label: "Wavetable", description: "Wavetable-scanning synth", promptHint: "wavetable synth", category: "synth", term: "wavetable synth" },
|
|
124
|
+
{ id: "stab-synth", label: "Stab", description: "Short, percussive chord", promptHint: "synth stab", category: "synth", term: "synth stab" },
|
|
112
125
|
|
|
113
126
|
// -------- Guitar --------
|
|
114
127
|
{ id: "acoustic-guitar", label: "Acoustic Guitar", description: "Steel- or nylon-strung", promptHint: "acoustic guitar", category: "guitar" },
|
|
115
128
|
{ id: "electric-guitar", label: "Electric Guitar", description: "Solid-body, often distorted", promptHint: "electric guitar", category: "guitar" },
|
|
116
129
|
{ id: "classical-guitar",label: "Classical Guitar",description: "Nylon-string, fingerstyle", promptHint: "classical guitar", category: "guitar" },
|
|
117
|
-
{ id: "12-string-guitar",label: "12-String", description: "Doubled-course strum", promptHint: "12-string guitar", category: "guitar" },
|
|
130
|
+
{ id: "12-string-guitar",label: "12-String", description: "Doubled-course strum", promptHint: "12-string guitar", category: "guitar", term: "12-string guitar" },
|
|
118
131
|
{ id: "pedal-steel", label: "Pedal Steel", description: "Country sliding steel", promptHint: "pedal steel", category: "guitar" },
|
|
119
132
|
{ id: "lap-steel", label: "Lap Steel", description: "Hawaiian lap-played steel", promptHint: "lap steel", category: "guitar" },
|
|
120
133
|
{ id: "dobro", label: "Dobro", description: "Resonator guitar", promptHint: "dobro resonator", category: "guitar" },
|
|
@@ -162,8 +175,8 @@ export const INSTRUMENTS: ReadonlyArray<CategorizedInstrument> = [
|
|
|
162
175
|
{ id: "cello", label: "Cello", description: "Solo cello", promptHint: "cello", category: "strings" },
|
|
163
176
|
{ id: "double-bass", label: "Double Bass", description: "Orchestral upright bass", promptHint: "double bass", category: "strings" },
|
|
164
177
|
{ id: "harp", label: "Harp", description: "Concert or folk harp", promptHint: "harp", category: "strings" },
|
|
165
|
-
{ id: "pizzicato-strings",label: "Pizzicato", description: "Plucked strings", promptHint: "pizzicato strings", category: "strings" },
|
|
166
|
-
{ id: "staccato-strings",label: "Staccato", description: "Short, punchy strings", promptHint: "staccato strings", category: "strings" },
|
|
178
|
+
{ id: "pizzicato-strings",label: "Pizzicato", description: "Plucked strings", promptHint: "pizzicato strings", category: "strings", term: "pizzicato strings" },
|
|
179
|
+
{ id: "staccato-strings",label: "Staccato", description: "Short, punchy strings", promptHint: "staccato strings", category: "strings", term: "staccato strings" },
|
|
167
180
|
{ id: "fiddle", label: "Fiddle", description: "Folk-style violin", promptHint: "fiddle", category: "strings" },
|
|
168
181
|
|
|
169
182
|
// -------- World --------
|
|
@@ -186,40 +199,40 @@ export const INSTRUMENTS: ReadonlyArray<CategorizedInstrument> = [
|
|
|
186
199
|
|
|
187
200
|
// -------- Middle Eastern --------
|
|
188
201
|
{ id: "kamancheh", label: "Kamancheh", description: "Persian bowed spike fiddle", promptHint: "kamancheh", category: "middle-eastern" },
|
|
189
|
-
{ id: "ney", label: "Ney", description: "Persian/Arabic end-blown reed flute", promptHint: "ney flute", category: "middle-eastern" },
|
|
202
|
+
{ id: "ney", label: "Ney", description: "Persian/Arabic end-blown reed flute", promptHint: "ney flute", category: "middle-eastern", term: "ney flute" },
|
|
190
203
|
{ id: "santur", label: "Santur", description: "Persian hammered dulcimer", promptHint: "santur", category: "middle-eastern" },
|
|
191
204
|
] as const
|
|
192
205
|
|
|
193
206
|
export const PRODUCTION_STYLES: ReadonlyArray<InstrumentationEntry> = [
|
|
194
|
-
{ id: "polished", label: "Polished", description: "Pristine, mainstream production", promptHint: "polished production" },
|
|
195
|
-
{ id: "lo-fi", label: "Lo-fi", description: "Warm tape hiss, imperfect", promptHint: "lo-fi production" },
|
|
196
|
-
{ id: "raw", label: "Raw", description: "Unfiltered, live feel", promptHint: "raw production" },
|
|
197
|
-
{ id: "vintage", label: "Vintage", description: "Analog warmth", promptHint: "vintage production" },
|
|
198
|
-
{ id: "modern", label: "Modern", description: "Contemporary digital", promptHint: "modern production" },
|
|
199
|
-
{ id: "minimalist", label: "Minimalist", description: "Sparse, restrained", promptHint: "minimalist production" },
|
|
200
|
-
{ id: "wall-of-sound", label: "Wall of Sound", description: "Dense, layered", promptHint: "wall-of-sound production" },
|
|
201
|
-
{ id: "ambient", label: "Ambient", description: "Atmospheric, reverbed", promptHint: "ambient production" },
|
|
202
|
-
{ id: "garage-band", label: "Garage Band", description: "DIY, basement-tracked", promptHint: "garage band production" },
|
|
203
|
-
{ id: "live-recording", label: "Live", description: "Live concert / room sound", promptHint: "live recording" },
|
|
204
|
-
{ id: "demo-quality", label: "Demo", description: "Rough demo aesthetic", promptHint: "demo quality" },
|
|
205
|
-
{ id: "studio-pristine", label: "Studio Pristine", description: "Hi-fi reference quality", promptHint: "pristine studio production" },
|
|
206
|
-
{ id: "tape-saturated", label: "Tape Saturated", description: "Analog tape warmth + drive", promptHint: "tape-saturated" },
|
|
207
|
-
{ id: "8-bit-retro", label: "8-bit Retro", description: "Lo-bitrate retro game audio", promptHint: "8-bit retro production" },
|
|
207
|
+
{ id: "polished", label: "Polished", description: "Pristine, mainstream production", promptHint: "polished production", term: "polished production" },
|
|
208
|
+
{ id: "lo-fi", label: "Lo-fi", description: "Warm tape hiss, imperfect", promptHint: "lo-fi production", term: "lo-fi production" },
|
|
209
|
+
{ id: "raw", label: "Raw", description: "Unfiltered, live feel", promptHint: "raw production", term: "raw production" },
|
|
210
|
+
{ id: "vintage", label: "Vintage", description: "Analog warmth", promptHint: "vintage production", term: "vintage production" },
|
|
211
|
+
{ id: "modern", label: "Modern", description: "Contemporary digital", promptHint: "modern production", term: "modern production" },
|
|
212
|
+
{ id: "minimalist", label: "Minimalist", description: "Sparse, restrained", promptHint: "minimalist production", term: "minimalist production" },
|
|
213
|
+
{ id: "wall-of-sound", label: "Wall of Sound", description: "Dense, layered", promptHint: "wall-of-sound production", term: "wall-of-sound production" },
|
|
214
|
+
{ id: "ambient", label: "Ambient", description: "Atmospheric, reverbed", promptHint: "ambient production", term: "ambient production" },
|
|
215
|
+
{ id: "garage-band", label: "Garage Band", description: "DIY, basement-tracked", promptHint: "garage band production", term: "garage band production" },
|
|
216
|
+
{ id: "live-recording", label: "Live", description: "Live concert / room sound", promptHint: "live recording", term: "live recording" },
|
|
217
|
+
{ id: "demo-quality", label: "Demo", description: "Rough demo aesthetic", promptHint: "demo quality", term: "demo quality" },
|
|
218
|
+
{ id: "studio-pristine", label: "Studio Pristine", description: "Hi-fi reference quality", promptHint: "pristine studio production", term: "pristine studio production" },
|
|
219
|
+
{ id: "tape-saturated", label: "Tape Saturated", description: "Analog tape warmth + drive", promptHint: "tape-saturated", term: "tape-saturated production" },
|
|
220
|
+
{ id: "8-bit-retro", label: "8-bit Retro", description: "Lo-bitrate retro game audio", promptHint: "8-bit retro production", term: "8-bit retro production" },
|
|
208
221
|
] as const
|
|
209
222
|
|
|
210
223
|
export const VOCAL_PRESENCE: ReadonlyArray<InstrumentationEntry> = [
|
|
211
|
-
{ id: "instrumental", label: "Instrumental", description: "No vocals (exclusive)", promptHint: "instrumental, no vocals" },
|
|
212
|
-
{ id: "male-lead", label: "Male Lead", description: "Male lead vocal", promptHint: "male lead vocals" },
|
|
213
|
-
{ id: "female-lead", label: "Female Lead", description: "Female lead vocal", promptHint: "female lead vocals" },
|
|
214
|
-
{ id: "androgynous-lead",label: "Androgynous", description: "Gender-neutral lead", promptHint: "androgynous lead vocals" },
|
|
215
|
-
{ id: "duet", label: "Duet", description: "Two lead vocalists", promptHint: "duet lead vocals" },
|
|
224
|
+
{ id: "instrumental", label: "Instrumental", description: "No vocals (exclusive)", promptHint: "instrumental, no vocals", term: "instrumental, no vocals" },
|
|
225
|
+
{ id: "male-lead", label: "Male Lead", description: "Male lead vocal", promptHint: "male lead vocals", term: "male lead vocals" },
|
|
226
|
+
{ id: "female-lead", label: "Female Lead", description: "Female lead vocal", promptHint: "female lead vocals", term: "female lead vocals" },
|
|
227
|
+
{ id: "androgynous-lead",label: "Androgynous", description: "Gender-neutral lead", promptHint: "androgynous lead vocals", term: "androgynous lead vocals" },
|
|
228
|
+
{ id: "duet", label: "Duet", description: "Two lead vocalists", promptHint: "duet lead vocals", term: "duet lead vocals" },
|
|
216
229
|
{ id: "choir", label: "Choir", description: "Choral / ensemble vocals", promptHint: "choir vocals" },
|
|
217
230
|
{ id: "gospel-choir", label: "Gospel Choir", description: "Gospel-style choir backing", promptHint: "gospel choir" },
|
|
218
|
-
{ id: "harmony-stack", label: "Harmony Stack", description: "Stacked vocal harmonies", promptHint: "stacked vocal harmonies" },
|
|
219
|
-
{ id: "rapper-lead", label: "Rapper", description: "Rap lead", promptHint: "rap lead vocals" },
|
|
231
|
+
{ id: "harmony-stack", label: "Harmony Stack", description: "Stacked vocal harmonies", promptHint: "stacked vocal harmonies", term: "stacked vocal harmonies" },
|
|
232
|
+
{ id: "rapper-lead", label: "Rapper", description: "Rap lead", promptHint: "rap lead vocals", term: "rap lead vocals" },
|
|
220
233
|
{ id: "spoken-word", label: "Spoken Word", description: "Spoken / poetry over music", promptHint: "spoken word" },
|
|
221
234
|
{ id: "vocal-chops", label: "Vocal Chops", description: "Sliced sample vocals", promptHint: "vocal chops" },
|
|
222
|
-
{ id: "mixed", label: "Mixed", description: "Multiple lead vocalists", promptHint: "mixed lead vocals" },
|
|
235
|
+
{ id: "mixed", label: "Mixed", description: "Multiple lead vocalists", promptHint: "mixed lead vocals", term: "mixed lead vocals" },
|
|
223
236
|
] as const
|
|
224
237
|
|
|
225
238
|
/** "instrumental" is mutually exclusive with any other vocal presence —
|
|
@@ -228,30 +241,30 @@ export const VOCAL_PRESENCE: ReadonlyArray<InstrumentationEntry> = [
|
|
|
228
241
|
export const VOCAL_PRESENCE_INSTRUMENTAL_ID = "instrumental"
|
|
229
242
|
|
|
230
243
|
export const SINGING_STYLES: ReadonlyArray<InstrumentationEntry> = [
|
|
231
|
-
{ id: "pop-singing", label: "Pop", description: "Mainstream lead style", promptHint: "pop singing" },
|
|
232
|
-
{ id: "rock-singing", label: "Rock", description: "Rock-style vocal delivery", promptHint: "rock singing" },
|
|
233
|
-
{ id: "operatic", label: "Operatic", description: "Classical opera technique", promptHint: "operatic singing" },
|
|
234
|
-
{ id: "classical-vocals", label: "Classical", description: "Trained classical voice", promptHint: "classical vocals" },
|
|
244
|
+
{ id: "pop-singing", label: "Pop", description: "Mainstream lead style", promptHint: "pop singing", term: "pop singing" },
|
|
245
|
+
{ id: "rock-singing", label: "Rock", description: "Rock-style vocal delivery", promptHint: "rock singing", term: "rock singing" },
|
|
246
|
+
{ id: "operatic", label: "Operatic", description: "Classical opera technique", promptHint: "operatic singing", term: "operatic singing" },
|
|
247
|
+
{ id: "classical-vocals", label: "Classical", description: "Trained classical voice", promptHint: "classical vocals", term: "classical vocals" },
|
|
235
248
|
{ id: "rap", label: "Rap", description: "Rhythmic spoken delivery", promptHint: "rap" },
|
|
236
|
-
{ id: "trap-melodic", label: "Trap (Melodic)", description: "Melodic trap, autotuned", promptHint: "melodic trap singing" },
|
|
237
|
-
{ id: "growl", label: "Growl", description: "Death-metal-style growl", promptHint: "growled vocals" },
|
|
238
|
-
{ id: "scream", label: "Scream", description: "Hardcore / screamed", promptHint: "screamed vocals" },
|
|
239
|
-
{ id: "shout", label: "Shout", description: "Punk / chanted shout", promptHint: "shouted vocals" },
|
|
249
|
+
{ id: "trap-melodic", label: "Trap (Melodic)", description: "Melodic trap, autotuned", promptHint: "melodic trap singing", term: "melodic trap singing" },
|
|
250
|
+
{ id: "growl", label: "Growl", description: "Death-metal-style growl", promptHint: "growled vocals", term: "growled vocals" },
|
|
251
|
+
{ id: "scream", label: "Scream", description: "Hardcore / screamed", promptHint: "screamed vocals", term: "screamed vocals" },
|
|
252
|
+
{ id: "shout", label: "Shout", description: "Punk / chanted shout", promptHint: "shouted vocals", term: "shouted vocals" },
|
|
240
253
|
{ id: "falsetto", label: "Falsetto", description: "High register, light", promptHint: "falsetto" },
|
|
241
254
|
{ id: "head-voice", label: "Head Voice", description: "Resonant high register", promptHint: "head voice" },
|
|
242
255
|
{ id: "chest-voice", label: "Chest Voice", description: "Strong, low register", promptHint: "chest voice" },
|
|
243
256
|
{ id: "belting", label: "Belting", description: "Powerful sustained high", promptHint: "belting" },
|
|
244
257
|
{ id: "crooning", label: "Crooning", description: "Smooth vintage romantic", promptHint: "crooning" },
|
|
245
|
-
{ id: "scat", label: "Scat", description: "Improvised jazz syllables", promptHint: "scat singing" },
|
|
258
|
+
{ id: "scat", label: "Scat", description: "Improvised jazz syllables", promptHint: "scat singing", term: "scat singing" },
|
|
246
259
|
{ id: "yodeling", label: "Yodeling", description: "Alpine register-flip", promptHint: "yodeling" },
|
|
247
260
|
{ id: "throat-singing", label: "Throat Singing", description: "Mongolian / Tuvan overtone", promptHint: "throat singing" },
|
|
248
261
|
{ id: "melismatic", label: "Melismatic", description: "Multi-note runs per syllable", promptHint: "melismatic" },
|
|
249
|
-
{ id: "autotuned", label: "Autotuned", description: "Pitch-corrected, processed", promptHint: "autotuned vocals" },
|
|
250
|
-
{ id: "vocoded", label: "Vocoded", description: "Vocoder-processed vocals", promptHint: "vocoded vocals" },
|
|
251
|
-
{ id: "whisper-singing", label: "Whisper Sing", description: "Hushed, intimate singing", promptHint: "whisper-singing" },
|
|
252
|
-
{ id: "breathy-singing", label: "Breathy", description: "Airy, intimate singing", promptHint: "breathy singing" },
|
|
253
|
-
{ id: "harmonized", label: "Harmonized", description: "Multi-voice harmonies", promptHint: "harmonized vocals" },
|
|
254
|
-
{ id: "chant", label: "Chant", description: "Liturgical / monk chanting", promptHint: "chanted vocals" },
|
|
262
|
+
{ id: "autotuned", label: "Autotuned", description: "Pitch-corrected, processed", promptHint: "autotuned vocals", term: "autotuned vocals" },
|
|
263
|
+
{ id: "vocoded", label: "Vocoded", description: "Vocoder-processed vocals", promptHint: "vocoded vocals", term: "vocoded vocals" },
|
|
264
|
+
{ id: "whisper-singing", label: "Whisper Sing", description: "Hushed, intimate singing", promptHint: "whisper-singing", term: "whisper-singing" },
|
|
265
|
+
{ id: "breathy-singing", label: "Breathy", description: "Airy, intimate singing", promptHint: "breathy singing", term: "breathy singing" },
|
|
266
|
+
{ id: "harmonized", label: "Harmonized", description: "Multi-voice harmonies", promptHint: "harmonized vocals", term: "harmonized vocals" },
|
|
267
|
+
{ id: "chant", label: "Chant", description: "Liturgical / monk chanting", promptHint: "chanted vocals", term: "chanted vocals" },
|
|
255
268
|
] as const
|
|
256
269
|
|
|
257
270
|
const INSTRUMENT_BY_ID = new Map(INSTRUMENTS.map((x) => [x.id, x]))
|
|
@@ -272,16 +285,40 @@ export function getSingingStyle(id: string | undefined): InstrumentationEntry |
|
|
|
272
285
|
return id ? SINGING_STYLE_BY_ID.get(id) : undefined
|
|
273
286
|
}
|
|
274
287
|
|
|
288
|
+
/**
|
|
289
|
+
* The COMPACT counterparts of the `promptHint` lookups: the short professional
|
|
290
|
+
* term a consumer injects instead of the entry's full fragment ("synth stab",
|
|
291
|
+
* "pristine studio production", "melodic trap singing"). Same lookup as the
|
|
292
|
+
* getters above, same empty-string-on-miss behavior, so hint mode and term
|
|
293
|
+
* mode can never disagree about WHICH entry they are describing.
|
|
294
|
+
*/
|
|
295
|
+
export function getInstrumentTerm(id: string | undefined | null): string {
|
|
296
|
+
return resolveTerm(id ? getInstrument(id) : undefined)
|
|
297
|
+
}
|
|
298
|
+
export function getProductionStyleTerm(id: string | undefined | null): string {
|
|
299
|
+
return resolveTerm(id ? getProductionStyle(id) : undefined)
|
|
300
|
+
}
|
|
301
|
+
export function getVocalPresenceTerm(id: string | undefined | null): string {
|
|
302
|
+
return resolveTerm(id ? getVocalPresence(id) : undefined)
|
|
303
|
+
}
|
|
304
|
+
export function getSingingStyleTerm(id: string | undefined | null): string {
|
|
305
|
+
return resolveTerm(id ? getSingingStyle(id) : undefined)
|
|
306
|
+
}
|
|
307
|
+
|
|
275
308
|
import { pickIds } from "@nodaro/shared"
|
|
276
309
|
|
|
277
|
-
export function buildInstrumentationHints(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
310
|
+
export function buildInstrumentationHints(
|
|
311
|
+
data: {
|
|
312
|
+
readonly preText?: string
|
|
313
|
+
readonly postText?: string
|
|
314
|
+
readonly instruments?: ReadonlyArray<string>
|
|
315
|
+
readonly production?: string
|
|
316
|
+
readonly vocalPresence?: string | ReadonlyArray<string>
|
|
317
|
+
readonly singingStyle?: string | ReadonlyArray<string>
|
|
318
|
+
},
|
|
319
|
+
mode: PickerHintMode = "full",
|
|
320
|
+
): string {
|
|
321
|
+
if (mode === "compact") return buildInstrumentationTerms(data)
|
|
285
322
|
const fragments: string[] = []
|
|
286
323
|
const pre = typeof data.preText === "string" ? data.preText.trim() : ""
|
|
287
324
|
if (pre) fragments.push(pre)
|
|
@@ -323,6 +360,60 @@ export function buildInstrumentationHints(data: {
|
|
|
323
360
|
return fragments.join(", ")
|
|
324
361
|
}
|
|
325
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Compact-mode mirror of `buildInstrumentationHints`: the same preText /
|
|
365
|
+
* [production] [instruments] / "with <vocals>" / "in <style> style" / postText
|
|
366
|
+
* structure, in the same canonical field order, built from each entry's short
|
|
367
|
+
* `term` instead of its full `promptHint`. preText / postText are user free
|
|
368
|
+
* text rather than catalog copy, so they pass through untouched in both modes.
|
|
369
|
+
*/
|
|
370
|
+
export function buildInstrumentationTerms(data: {
|
|
371
|
+
readonly preText?: string
|
|
372
|
+
readonly postText?: string
|
|
373
|
+
readonly instruments?: ReadonlyArray<string>
|
|
374
|
+
readonly production?: string
|
|
375
|
+
readonly vocalPresence?: string | ReadonlyArray<string>
|
|
376
|
+
readonly singingStyle?: string | ReadonlyArray<string>
|
|
377
|
+
}): string {
|
|
378
|
+
const fragments: string[] = []
|
|
379
|
+
const pre = typeof data.preText === "string" ? data.preText.trim() : ""
|
|
380
|
+
if (pre) fragments.push(pre)
|
|
381
|
+
|
|
382
|
+
const segments: string[] = []
|
|
383
|
+
const prod = getProductionStyleTerm(data.production)
|
|
384
|
+
if (prod) segments.push(prod)
|
|
385
|
+
|
|
386
|
+
const instTerms = (data.instruments ?? [])
|
|
387
|
+
.map((id) => getInstrumentTerm(id))
|
|
388
|
+
.filter((t) => t.length > 0)
|
|
389
|
+
if (instTerms.length > 0) segments.push(instTerms.join(", "))
|
|
390
|
+
|
|
391
|
+
const vocalClause = pickIds(data.vocalPresence)
|
|
392
|
+
.map((id) => getVocalPresenceTerm(id))
|
|
393
|
+
.filter((t) => t.length > 0)
|
|
394
|
+
.join(", ")
|
|
395
|
+
|
|
396
|
+
const styleClause = pickIds(data.singingStyle)
|
|
397
|
+
.map((id) => getSingingStyleTerm(id))
|
|
398
|
+
.filter((t) => t.length > 0)
|
|
399
|
+
.join(", ")
|
|
400
|
+
|
|
401
|
+
const result: string[] = []
|
|
402
|
+
if (segments.length > 0) result.push(segments.join(" "))
|
|
403
|
+
if (vocalClause) {
|
|
404
|
+
result.push(result.length > 0 ? `with ${vocalClause}` : vocalClause)
|
|
405
|
+
}
|
|
406
|
+
if (styleClause) {
|
|
407
|
+
result.push(result.length > 0 ? `in ${styleClause} style` : `${styleClause} style`)
|
|
408
|
+
}
|
|
409
|
+
if (result.length > 0) fragments.push(result.join(" "))
|
|
410
|
+
|
|
411
|
+
const post = typeof data.postText === "string" ? data.postText.trim() : ""
|
|
412
|
+
if (post) fragments.push(post)
|
|
413
|
+
|
|
414
|
+
return fragments.join(", ")
|
|
415
|
+
}
|
|
416
|
+
|
|
326
417
|
export function isInstrumentalVocal(value: unknown): boolean {
|
|
327
418
|
return pickIds(value).includes(VOCAL_PRESENCE_INSTRUMENTAL_ID)
|
|
328
419
|
}
|
package/src/lens.ts
CHANGED
|
@@ -10,32 +10,43 @@
|
|
|
10
10
|
* frontend DAG executor and the backend orchestrator.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { resolveTerm } from "./term.js"
|
|
14
|
+
|
|
13
15
|
export interface Lens {
|
|
14
16
|
readonly id: string
|
|
15
17
|
readonly label: string
|
|
16
18
|
readonly description: string
|
|
17
19
|
readonly promptHint: string
|
|
20
|
+
/**
|
|
21
|
+
* Optional authored compact term (see `term.ts`). Authored where the
|
|
22
|
+
* lowercased label is not what a cinematographer would write in a prompt —
|
|
23
|
+
* the focal length lives in a parenthetical the derivation drops
|
|
24
|
+
* ("Wide (24mm)" → "wide"), or the bare optic name needs its category noun
|
|
25
|
+
* ("Macro" → "macro lens", "Shallow DOF" → "shallow depth of field").
|
|
26
|
+
* Everywhere else the label IS the term.
|
|
27
|
+
*/
|
|
28
|
+
readonly term?: string
|
|
18
29
|
}
|
|
19
30
|
|
|
20
31
|
export const LENSES: ReadonlyArray<Lens> = [
|
|
21
|
-
{ id: "ultra-wide-14mm", label: "Ultra-wide (14mm)", description: "Extreme wide angle, exaggerated perspective", promptHint: "shot on ultra-wide 14mm lens, exaggerated perspective with strong barrel distortion" },
|
|
22
|
-
{ id: "wide-24mm", label: "Wide (24mm)", description: "Wide field of view, environmental", promptHint: "shot on wide 24mm lens, expansive environmental field of view" },
|
|
23
|
-
{ id: "standard-35mm", label: "Standard (35mm)", description: "Natural perspective, documentary feel", promptHint: "shot on standard 35mm lens, natural perspective with documentary feel" },
|
|
24
|
-
{ id: "normal-50mm", label: "Normal (50mm)", description: "Closest to human eye perception", promptHint: "shot on normal 50mm lens, perspective closest to natural human vision" },
|
|
25
|
-
{ id: "portrait-85mm", label: "Portrait (85mm)", description: "Flattering compression, creamy bokeh", promptHint: "shot on 85mm portrait lens, flattering facial compression with creamy background bokeh" },
|
|
26
|
-
{ id: "telephoto-135mm", label: "Telephoto (135mm)", description: "Compressed depth, isolated subject", promptHint: "shot on 135mm telephoto lens, compressed depth with subject isolated from background" },
|
|
27
|
-
{ id: "super-telephoto-400mm", label: "Super Telephoto (400mm)", description: "Extreme compression, distant subject", promptHint: "shot on super-telephoto 400mm lens, extreme depth compression with subject pulled forward" },
|
|
28
|
-
{ id: "fisheye", label: "Fisheye", description: "Hemispherical 180° distortion", promptHint: "shot on fisheye lens, extreme 180-degree hemispherical distortion with curved horizon" },
|
|
29
|
-
{ id: "anamorphic", label: "Anamorphic", description: "Cinematic widescreen, oval bokeh", promptHint: "anamorphic lens look, cinematic widescreen feel with characteristic oval bokeh and horizontal lens flares" },
|
|
30
|
-
{ id: "macro", label: "Macro", description: "Extreme close-up of small detail", promptHint: "macro lens, extreme close-up revealing fine detail with shallow depth of field" },
|
|
32
|
+
{ id: "ultra-wide-14mm", label: "Ultra-wide (14mm)", description: "Extreme wide angle, exaggerated perspective", promptHint: "shot on ultra-wide 14mm lens, exaggerated perspective with strong barrel distortion", term: "14mm ultra-wide lens" },
|
|
33
|
+
{ id: "wide-24mm", label: "Wide (24mm)", description: "Wide field of view, environmental", promptHint: "shot on wide 24mm lens, expansive environmental field of view", term: "24mm wide-angle lens" },
|
|
34
|
+
{ id: "standard-35mm", label: "Standard (35mm)", description: "Natural perspective, documentary feel", promptHint: "shot on standard 35mm lens, natural perspective with documentary feel", term: "35mm standard lens" },
|
|
35
|
+
{ id: "normal-50mm", label: "Normal (50mm)", description: "Closest to human eye perception", promptHint: "shot on normal 50mm lens, perspective closest to natural human vision", term: "50mm normal lens" },
|
|
36
|
+
{ id: "portrait-85mm", label: "Portrait (85mm)", description: "Flattering compression, creamy bokeh", promptHint: "shot on 85mm portrait lens, flattering facial compression with creamy background bokeh", term: "85mm portrait lens" },
|
|
37
|
+
{ id: "telephoto-135mm", label: "Telephoto (135mm)", description: "Compressed depth, isolated subject", promptHint: "shot on 135mm telephoto lens, compressed depth with subject isolated from background", term: "135mm telephoto lens" },
|
|
38
|
+
{ id: "super-telephoto-400mm", label: "Super Telephoto (400mm)", description: "Extreme compression, distant subject", promptHint: "shot on super-telephoto 400mm lens, extreme depth compression with subject pulled forward", term: "400mm super-telephoto lens" },
|
|
39
|
+
{ id: "fisheye", label: "Fisheye", description: "Hemispherical 180° distortion", promptHint: "shot on fisheye lens, extreme 180-degree hemispherical distortion with curved horizon", term: "fisheye lens" },
|
|
40
|
+
{ id: "anamorphic", label: "Anamorphic", description: "Cinematic widescreen, oval bokeh", promptHint: "anamorphic lens look, cinematic widescreen feel with characteristic oval bokeh and horizontal lens flares", term: "anamorphic lens" },
|
|
41
|
+
{ id: "macro", label: "Macro", description: "Extreme close-up of small detail", promptHint: "macro lens, extreme close-up revealing fine detail with shallow depth of field", term: "macro lens" },
|
|
31
42
|
{ id: "probe", label: "Probe Lens", description: "Tube macro — through holes and tight spaces", promptHint: "probe lens shot, tube-lens macro perspective gliding through impossibly tight spaces with extreme close-up depth" },
|
|
32
|
-
{ id: "cctv", label: "CCTV", description: "Security-camera surveillance look", promptHint: "CCTV security camera footage look, high-angle fixed surveillance framing, mild wide-angle distortion, washed contrast and visible video grain" },
|
|
33
|
-
{ id: "tilt-shift", label: "Tilt-shift", description: "Selective focus, miniature effect", promptHint: "tilt-shift lens, selective plane of focus producing a miniature-diorama effect" },
|
|
34
|
-
{ id: "shallow-dof", label: "Shallow DOF", description: "Razor-thin focus, dreamy bokeh", promptHint: "extremely shallow depth of field, razor-thin focal plane with dreamy out-of-focus bokeh" },
|
|
43
|
+
{ id: "cctv", label: "CCTV", description: "Security-camera surveillance look", promptHint: "CCTV security camera footage look, high-angle fixed surveillance framing, mild wide-angle distortion, washed contrast and visible video grain", term: "cctv surveillance camera look" },
|
|
44
|
+
{ id: "tilt-shift", label: "Tilt-shift", description: "Selective focus, miniature effect", promptHint: "tilt-shift lens, selective plane of focus producing a miniature-diorama effect", term: "tilt-shift lens" },
|
|
45
|
+
{ id: "shallow-dof", label: "Shallow DOF", description: "Razor-thin focus, dreamy bokeh", promptHint: "extremely shallow depth of field, razor-thin focal plane with dreamy out-of-focus bokeh", term: "shallow depth of field" },
|
|
35
46
|
{ id: "canon-k35", label: "Canon K35 Prime", description: "Vintage cinematic, warm gentle skin", promptHint: "shot on a vintage Canon K35 cinema prime, warm gentle skin rendition, low-contrast highlight roll-off, soft dreamy falloff into the bokeh and the unmistakable seventies-era cinematic patina" },
|
|
36
47
|
{ id: "cooke-s4", label: "Cooke S4 Prime", description: "The Cooke look — creamy painterly skin", promptHint: "shot on a Cooke S4 cinema prime, the signature \"Cooke look\" with creamy painterly bokeh, gently rounded out-of-focus highlights and a flattering filmic skin rendition" },
|
|
37
|
-
{ id: "helios-44", label: "Helios 44", description: "Vintage Soviet swirly bokeh", promptHint: "shot on a vintage Soviet Helios 44 lens, the legendary swirly bokeh circling the subject, soft low contrast in the corners and a dreamlike vintage aberration character" },
|
|
38
|
-
{ id: "petzval", label: "Petzval Portrait", description: "Ultra-vintage swirl, dramatic falloff", promptHint: "shot on an ultra-vintage Petzval portrait lens, sharp central focus that falls off rapidly into a swirling, painterly bokeh, dramatic vignetting and a 19th-century daguerreotype-era softness at the edges" },
|
|
48
|
+
{ id: "helios-44", label: "Helios 44", description: "Vintage Soviet swirly bokeh", promptHint: "shot on a vintage Soviet Helios 44 lens, the legendary swirly bokeh circling the subject, soft low contrast in the corners and a dreamlike vintage aberration character", term: "vintage helios 44 lens, swirly bokeh" },
|
|
49
|
+
{ id: "petzval", label: "Petzval Portrait", description: "Ultra-vintage swirl, dramatic falloff", promptHint: "shot on an ultra-vintage Petzval portrait lens, sharp central focus that falls off rapidly into a swirling, painterly bokeh, dramatic vignetting and a 19th-century daguerreotype-era softness at the edges", term: "petzval portrait lens, swirling bokeh" },
|
|
39
50
|
] as const
|
|
40
51
|
|
|
41
52
|
const lensById = new Map<string, Lens>(LENSES.map((l) => [l.id, l]))
|
|
@@ -56,4 +67,9 @@ export function getLensPromptHint(id: string | undefined | null): string {
|
|
|
56
67
|
return getLens(id)?.promptHint ?? ""
|
|
57
68
|
}
|
|
58
69
|
|
|
70
|
+
/** The compact professional term for a lens (see `term.ts`). */
|
|
71
|
+
export function getLensTerm(id: string | undefined | null): string {
|
|
72
|
+
return resolveTerm(getLens(id))
|
|
73
|
+
}
|
|
74
|
+
|
|
59
75
|
export const LENS_IDS: ReadonlyArray<string> = LENSES.map((l) => l.id)
|