@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.
- package/LICENSE +105 -0
- package/README.md +27 -0
- package/dist/index.cjs +21240 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3599 -0
- package/dist/index.d.ts +3599 -0
- package/dist/index.js +20854 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
- package/src/__tests__/__snapshots__/prompt-builder-segments.test.ts.snap +65 -0
- package/src/__tests__/action-fx.test.ts +155 -0
- package/src/__tests__/apply-picker-json.test.ts +50 -0
- package/src/__tests__/assemble-image-input.test.ts +248 -0
- package/src/__tests__/assemble-suno-input.test.ts +283 -0
- package/src/__tests__/brand-tokens.test.ts +81 -0
- package/src/__tests__/build-image-prompt-element-injection.test.ts +118 -0
- package/src/__tests__/build-image-prompt-hybrid-format.test.ts +131 -0
- package/src/__tests__/build-image-prompt-mentions.test.ts +809 -0
- package/src/__tests__/build-image-prompt-reference-cap.test.ts +57 -0
- package/src/__tests__/build-image-prompt-reference-numbering.test.ts +237 -0
- package/src/__tests__/build-image-prompt-reference-order.test.ts +313 -0
- package/src/__tests__/camera-motions-from-connections.test.ts +71 -0
- package/src/__tests__/catalog-gapfill.test.ts +53 -0
- package/src/__tests__/character-convergence-image.test.ts +217 -0
- package/src/__tests__/character-default-role-image.test.ts +167 -0
- package/src/__tests__/character-default-role-video.test.ts +168 -0
- package/src/__tests__/character-default-role.test.ts +70 -0
- package/src/__tests__/character-fx.test.ts +200 -0
- package/src/__tests__/entity-prompts-location.test.ts +97 -0
- package/src/__tests__/entity-prompts.test.ts +240 -0
- package/src/__tests__/expand-extra-refs-role.test.ts +50 -0
- package/src/__tests__/factory-presets.test.ts +1045 -0
- package/src/__tests__/factory-snippets.test.ts +68 -0
- package/src/__tests__/framing-multi.test.ts +71 -0
- package/src/__tests__/framing-vantage.test.ts +39 -0
- package/src/__tests__/i18n-entry-completeness.test.ts +269 -0
- package/src/__tests__/identity-lock.test.ts +46 -0
- package/src/__tests__/instrumentation.test.ts +68 -0
- package/src/__tests__/lighting-multi.test.ts +68 -0
- package/src/__tests__/location-convergence-image.test.ts +124 -0
- package/src/__tests__/mention-lock-flag.test.ts +499 -0
- package/src/__tests__/multi-picker-spec.test.ts +71 -0
- package/src/__tests__/music-genre.test.ts +103 -0
- package/src/__tests__/music-mood.test.ts +87 -0
- package/src/__tests__/object-creature-convergence-image.test.ts +105 -0
- package/src/__tests__/parameter-prompt-hint.test.ts +270 -0
- package/src/__tests__/parameter-registry-sync.test.ts +243 -0
- package/src/__tests__/person-age.test.ts +80 -0
- package/src/__tests__/person-analyzer-invariants.test.ts +47 -0
- package/src/__tests__/person-body-axes.test.ts +67 -0
- package/src/__tests__/person-facial-geometry.test.ts +156 -0
- package/src/__tests__/person-regional-aesthetic.test.ts +161 -0
- package/src/__tests__/person-sections.test.ts +18 -0
- package/src/__tests__/picker-analyzer-registry.test.ts +108 -0
- package/src/__tests__/picker-catalogs-project.test.ts +85 -0
- package/src/__tests__/picker-catalogs.test.ts +53 -0
- package/src/__tests__/picker-limits.test.ts +20 -0
- package/src/__tests__/prompt-builder-segments.test.ts +183 -0
- package/src/__tests__/prompt-builder-structured-fields.test.ts +40 -0
- package/src/__tests__/prompt-builder.test.ts +1773 -0
- package/src/__tests__/prompt-wizard-categories.test.ts +31 -0
- package/src/__tests__/provider-prompt-doctrine.test.ts +49 -0
- package/src/__tests__/resolve-prompt-append.test.ts +58 -0
- package/src/__tests__/resolve-prompt.test.ts +44 -0
- package/src/__tests__/role-picker-shared.test.ts +208 -0
- package/src/__tests__/seedance-2-inputs.test.ts +173 -0
- package/src/__tests__/seedance-extend.test.ts +44 -0
- package/src/__tests__/sound-aggregator.test.ts +350 -0
- package/src/__tests__/style-presets.test.ts +34 -0
- package/src/__tests__/temporal-multi.test.ts +74 -0
- package/src/__tests__/transitions.test.ts +213 -0
- package/src/__tests__/video-reference-features.test.ts +41 -0
- package/src/__tests__/video-reference-leading-refs.test.ts +90 -0
- package/src/__tests__/video-reference-resolver.test.ts +233 -0
- package/src/__tests__/video-reference-roles.test.ts +96 -0
- package/src/__tests__/voice-character.test.ts +48 -0
- package/src/__tests__/voice-delivery.test.ts +41 -0
- package/src/__tests__/wardrobe.test.ts +25 -0
- package/src/action-fx.ts +255 -0
- package/src/aesthetic.ts +435 -0
- package/src/assemble-image-input.ts +236 -0
- package/src/assemble-suno-input.ts +147 -0
- package/src/atmosphere.ts +104 -0
- package/src/backdrop.ts +131 -0
- package/src/brand-tokens.ts +154 -0
- package/src/camera-format.ts +76 -0
- package/src/camera-motions.ts +615 -0
- package/src/character-fx.ts +274 -0
- package/src/color-look.ts +103 -0
- package/src/composition-effects.ts +67 -0
- package/src/entity-prompts.ts +231 -0
- package/src/era.ts +292 -0
- package/src/exposure-settings.ts +142 -0
- package/src/factory-presets/generate-image.ts +1644 -0
- package/src/factory-presets/generate-video.ts +1116 -0
- package/src/factory-presets/index.ts +46 -0
- package/src/factory-presets/lottie-overlay.ts +166 -0
- package/src/factory-presets/motion-graphics.ts +350 -0
- package/src/factory-presets/music.ts +734 -0
- package/src/factory-presets/sfx.ts +136 -0
- package/src/factory-presets/shared-image.ts +207 -0
- package/src/factory-presets/switchx.ts +65 -0
- package/src/factory-presets/text.ts +292 -0
- package/src/factory-presets/types.ts +51 -0
- package/src/factory-presets/video-edit.ts +136 -0
- package/src/factory-presets/voice.ts +172 -0
- package/src/factory-presets.ts +2 -0
- package/src/factory-snippets/catalog.ts +105 -0
- package/src/factory-snippets/index.ts +16 -0
- package/src/factory-snippets/types.ts +33 -0
- package/src/framing.ts +634 -0
- package/src/held-prop.ts +187 -0
- package/src/identity-lock.ts +213 -0
- package/src/index.ts +66 -0
- package/src/instrumentation.ts +337 -0
- package/src/lens.ts +59 -0
- package/src/lighting.ts +229 -0
- package/src/loop-subject.ts +276 -0
- package/src/materials.ts +184 -0
- package/src/mood.ts +186 -0
- package/src/music-genre.ts +662 -0
- package/src/music-mood.ts +137 -0
- package/src/object-asset-presets.ts +81 -0
- package/src/parameter-prompt-hint.ts +281 -0
- package/src/person.ts +1368 -0
- package/src/photo-genre.ts +151 -0
- package/src/photographer.ts +612 -0
- package/src/picker-analyzer-registry.ts +374 -0
- package/src/picker-catalogs.ts +858 -0
- package/src/pose.ts +246 -0
- package/src/post-process-effects.ts +94 -0
- package/src/prompt-builder-structured-fields.ts +116 -0
- package/src/prompt-builder.ts +2964 -0
- package/src/prompt-templates.ts +50 -0
- package/src/prompt-wizard-categories.ts +334 -0
- package/src/provider-prompt-doctrine.ts +85 -0
- package/src/render-quality.ts +89 -0
- package/src/resolve-prompt.ts +120 -0
- package/src/seedance-2-inputs.ts +100 -0
- package/src/setting.ts +130 -0
- package/src/sound-aggregator.ts +241 -0
- package/src/style-presets.ts +162 -0
- package/src/style.ts +99 -0
- package/src/styling.ts +585 -0
- package/src/temporal.ts +151 -0
- package/src/transitions.ts +333 -0
- package/src/video-reference-resolver.ts +823 -0
- package/src/voice-character.ts +237 -0
- package/src/voice-delivery.ts +142 -0
- package/src/wardrobe.ts +179 -0
package/src/person.ts
ADDED
|
@@ -0,0 +1,1368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical catalog of Person / subject-appearance choices.
|
|
3
|
+
*
|
|
4
|
+
* Multi-dimension parameter node. Unlike Setting / Style / Camera-Motion
|
|
5
|
+
* (single pick) or Framing (5 dims, all shot composition), Person describes
|
|
6
|
+
* *who the subject is*. Each dimension is mutually exclusive within itself
|
|
7
|
+
* (pick one Type, one Age, one Cheekbones, etc.) and all are independently
|
|
8
|
+
* optional. Dimensions are grouped into six sections (PERSON_DIMENSION_SECTIONS):
|
|
9
|
+
* Identity, Body, Face, Hair, Skin & Eyes, Features.
|
|
10
|
+
*
|
|
11
|
+
* The Face section carries a dedicated facial-geometry layer — cheekbones,
|
|
12
|
+
* facial fullness, eyelid type, canthal tilt, eye spacing, eye-to-brow set,
|
|
13
|
+
* nose tip, and split lip fullness / lip shape — so the structured pickers
|
|
14
|
+
* (not a single "supermodel" Type token) drive the feature geometry and ratios
|
|
15
|
+
* that actually define a face's look. Neutral options (Average / Neutral /
|
|
16
|
+
* Standard / Natural) carry an empty promptHint, so enabling a dimension
|
|
17
|
+
* injects nothing until a real value is picked.
|
|
18
|
+
*
|
|
19
|
+
* Non-empty selections from each dimension are joined as a compound hint and
|
|
20
|
+
* injected as cinematography context into downstream image/video prompts:
|
|
21
|
+
* "a beautiful woman, in her 30s, East Asian, slim build, long wavy hair,
|
|
22
|
+
* brown hair, fair skin, green eyes"
|
|
23
|
+
*
|
|
24
|
+
* Applies to BOTH image and video consumers (a person description is not
|
|
25
|
+
* video-specific, unlike camera-motion / temporal). Not in
|
|
26
|
+
* STILL_IMAGE_EXCLUDE_TYPES.
|
|
27
|
+
*
|
|
28
|
+
* Shared between the picker UI, the standalone Person parameter node, and
|
|
29
|
+
* the prompt-hint injection on both the frontend DAG executor and the
|
|
30
|
+
* backend orchestrator.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export type PersonDimension =
|
|
34
|
+
| "type"
|
|
35
|
+
| "age"
|
|
36
|
+
| "ethnicity"
|
|
37
|
+
| "regional-aesthetic"
|
|
38
|
+
| "frame"
|
|
39
|
+
| "body-mass"
|
|
40
|
+
| "bust"
|
|
41
|
+
| "waist"
|
|
42
|
+
| "hips"
|
|
43
|
+
| "silhouette"
|
|
44
|
+
| "face-shape"
|
|
45
|
+
| "jawline"
|
|
46
|
+
| "cheekbones"
|
|
47
|
+
| "facial-fullness"
|
|
48
|
+
| "eye-shape"
|
|
49
|
+
| "eyelid-type"
|
|
50
|
+
| "canthal-tilt"
|
|
51
|
+
| "eye-spacing"
|
|
52
|
+
| "eye-set-brow"
|
|
53
|
+
| "nose"
|
|
54
|
+
| "nose-tip"
|
|
55
|
+
| "lip-fullness"
|
|
56
|
+
| "lip-shape"
|
|
57
|
+
| "lip-state"
|
|
58
|
+
| "hair-color"
|
|
59
|
+
| "hair-base"
|
|
60
|
+
| "eyebrows"
|
|
61
|
+
| "skin-tone"
|
|
62
|
+
| "skin-texture"
|
|
63
|
+
| "eye-color"
|
|
64
|
+
| "eye-state"
|
|
65
|
+
| "facial-hair"
|
|
66
|
+
| "distinctive-features"
|
|
67
|
+
|
|
68
|
+
export interface Person {
|
|
69
|
+
readonly id: string
|
|
70
|
+
readonly label: string
|
|
71
|
+
readonly dimension: PersonDimension
|
|
72
|
+
readonly description: string
|
|
73
|
+
readonly promptHint: string
|
|
74
|
+
/** Optional sub-grouping within a dimension. Used by the picker to render
|
|
75
|
+
* two-level selection (e.g. ethnicity grouped by region → specific). */
|
|
76
|
+
readonly group?: string
|
|
77
|
+
/** Optional shorter label for the picker chip. When a dimension renders
|
|
78
|
+
* with group headers (e.g. ethnicity), the group name already supplies
|
|
79
|
+
* context — so a chip under the "Asian" header shows "East (any)"
|
|
80
|
+
* instead of the redundant "East Asian (any)". Node cards + tooltips
|
|
81
|
+
* keep the full `label` / `description`. */
|
|
82
|
+
readonly shortLabel?: string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const PEOPLE: ReadonlyArray<Person> = [
|
|
86
|
+
// -------------------- Type (primary subject descriptor) --------------------
|
|
87
|
+
// Type captures identity / archetype / species. Pure-age subjects (baby,
|
|
88
|
+
// child, teenager) belong in the Age dimension. Entries are tagged with a
|
|
89
|
+
// `group` so the picker renders Type as collapsible sub-sections — the
|
|
90
|
+
// catalog is large enough that flat scrolling isn't usable.
|
|
91
|
+
//
|
|
92
|
+
// Gender policy:
|
|
93
|
+
// - For species/creatures whose silhouette doesn't depend strongly on
|
|
94
|
+
// gender, entries are gender-neutral. The user can pair Build / Hair /
|
|
95
|
+
// Lips for masculine/feminine cues.
|
|
96
|
+
// - For archetypes with canonical gendered forms (wizard/witch,
|
|
97
|
+
// sorcerer/sorceress, god/goddess, prince/princess, mer-man/mermaid),
|
|
98
|
+
// we ship paired entries.
|
|
99
|
+
|
|
100
|
+
// ----- Realistic — Plain -----
|
|
101
|
+
{ id: "man", label: "Man", group: "Realistic", dimension: "type", description: "Adult man", promptHint: "a man" },
|
|
102
|
+
{ id: "woman", label: "Woman", group: "Realistic", dimension: "type", description: "Adult woman", promptHint: "a woman" },
|
|
103
|
+
// "Not defined" is the open / unspecified slot — distinct from non-binary.
|
|
104
|
+
// Use this when gender shouldn't be pinned in the prompt; the model gets
|
|
105
|
+
// "a person" and ethnicity / build / hair drive the rest.
|
|
106
|
+
{ id: "not-defined", label: "Not Defined", group: "Realistic", dimension: "type", description: "Gender not specified — left open", promptHint: "a person" },
|
|
107
|
+
|
|
108
|
+
// ----- Realistic — Aesthetic / archetype -----
|
|
109
|
+
{ id: "beautiful-woman", label: "Beautiful Woman", group: "Realistic — Style", dimension: "type", description: "Classically beautiful woman", promptHint: "a beautiful woman" },
|
|
110
|
+
{ id: "girl-next-door", label: "Girl Next Door", group: "Realistic — Style", dimension: "type", description: "Friendly, approachable, wholesome", promptHint: "a girl-next-door type — friendly, approachable, natural beauty with wholesome charm" },
|
|
111
|
+
{ id: "handsome-man", label: "Handsome Man", group: "Realistic — Style", dimension: "type", description: "Classically handsome man", promptHint: "a handsome man" },
|
|
112
|
+
{ id: "boy-next-door", label: "Boy Next Door", group: "Realistic — Style", dimension: "type", description: "Friendly, approachable, wholesome", promptHint: "a boy-next-door type — friendly, approachable, with wholesome charm" },
|
|
113
|
+
{ id: "gentleman", label: "Gentleman", group: "Realistic — Style", dimension: "type", description: "Refined, classic, polished man", promptHint: "a refined gentleman with classic elegance and polished presence" },
|
|
114
|
+
{ id: "elegant-woman", label: "Elegant Woman", group: "Realistic — Style", dimension: "type", description: "Poised, refined woman", promptHint: "an elegant, poised woman" },
|
|
115
|
+
{ id: "rugged-man", label: "Rugged Man", group: "Realistic — Style", dimension: "type", description: "Weathered, rugged man", promptHint: "a rugged, weathered man" },
|
|
116
|
+
{ id: "bad-boy", label: "Bad Boy", group: "Realistic — Style", dimension: "type", description: "Rebellious, edgy masculine", promptHint: "a bad boy with rebellious, edgy attitude and a dangerous charm" },
|
|
117
|
+
{ id: "graceful-woman", label: "Graceful Woman", group: "Realistic — Style", dimension: "type", description: "Gentle, graceful woman", promptHint: "a graceful woman" },
|
|
118
|
+
{ id: "baddie", label: "Baddie", group: "Realistic — Style", dimension: "type", description: "Confident, trendy, styled woman", promptHint: "a baddie — a confident, trendy woman with styled makeup and a fashion-forward look" },
|
|
119
|
+
{ id: "stylish-influencer", label: "Stylish Influencer", group: "Realistic — Style", dimension: "type", description: "Polished social-media tastemaker", promptHint: "a stylish social-media influencer — polished personal-brand styling, trend-forward outfit, expressive natural posing for the camera, the curated put-together look of a content creator" },
|
|
120
|
+
{ id: "stunning-model", label: "Stunning Model", group: "Realistic — Style", dimension: "type", description: "Fashion-model aesthetic", promptHint: "a stunning fashion model with editorial poise, refined features, and high-fashion presence" },
|
|
121
|
+
{ id: "supermodel", label: "Supermodel", group: "Realistic — Style", dimension: "type", description: "Iconic, top-tier runway and cover star", promptHint: "a supermodel — iconic top-tier presence with magnetic statement features, a striking silhouette and the unmistakable runway-and-cover aura that commands attention" },
|
|
122
|
+
{ id: "femme-fatale", label: "Femme Fatale", group: "Realistic — Style", dimension: "type", description: "Alluring, dangerous noir seductress", promptHint: "a femme fatale — alluring, mysterious, and dangerous with classic noir seduction" },
|
|
123
|
+
{ id: "tough-guy", label: "Tough Guy", group: "Realistic — Style", dimension: "type", description: "Hardened, tough man", promptHint: "a tough, hardened man" },
|
|
124
|
+
// Aesthetic archetypes that traditionally read older — hints kept gender +
|
|
125
|
+
// look-focused so the Age dimension stays in control of the literal age.
|
|
126
|
+
{ id: "wise-elder", label: "Wise Elder", group: "Realistic — Style", dimension: "type", description: "Knowing, sage presence", promptHint: "a wise, sage figure with a knowing, contemplative presence" },
|
|
127
|
+
{ id: "silver-fox", label: "Silver Fox", group: "Realistic — Style", dimension: "type", description: "Distinguished man with silver-gray hair", promptHint: "a silver fox — a distinguished man with silver-gray hair and a confident, magnetic presence" },
|
|
128
|
+
{ id: "mysterious-figure", label: "Mysterious Figure", group: "Realistic — Style", dimension: "type", description: "Enigmatic figure", promptHint: "a mysterious figure" },
|
|
129
|
+
{ id: "femboy", label: "Femboy", group: "Realistic — Style", dimension: "type", description: "Soft-feminine masculine type, gender-fluid styling", promptHint: "a femboy — soft-feminine masculine presentation with gender-fluid styling" },
|
|
130
|
+
{ id: "twink", label: "Twink", group: "Realistic — Style", dimension: "type", description: "Slim, smooth, soft gay-male type", promptHint: "a twink — slim, smooth, soft masculine presence with a delicate, gender-fluid vibe" },
|
|
131
|
+
{ id: "soft-butch", label: "Soft Butch", group: "Realistic — Style", dimension: "type", description: "Gentle masculine-of-center woman", promptHint: "a soft-butch woman — gentle masculine-of-center styling and energy" },
|
|
132
|
+
{ id: "tomboy", label: "Tomboy", group: "Realistic — Style", dimension: "type", description: "Boyish styling and energy on a feminine body", promptHint: "a tomboy — boyish styling and energy on a feminine body" },
|
|
133
|
+
{ id: "thick", label: "Thick", group: "Realistic — Style", dimension: "type", description: "Voluptuous body-positive figure with curves", promptHint: "a thick, voluptuous body-positive figure with full curves" },
|
|
134
|
+
{ id: "androgynous", label: "Androgynous", group: "Realistic — Style", dimension: "type", description: "Deliberately blended masculine + feminine cues", promptHint: "an androgynous figure with deliberately blended masculine and feminine cues" },
|
|
135
|
+
{ id: "goth", label: "Goth", group: "Realistic — Style", dimension: "type", description: "Goth subculture aesthetic — dark, romantic, dramatic", promptHint: "a goth figure with a dark romantic dramatic subculture aesthetic" },
|
|
136
|
+
{ id: "bear", label: "Bear", group: "Realistic — Style", dimension: "type", description: "Big, bearded, hairy masculine type", promptHint: "a bear — a big, bearded, hairy man with a warm masculine presence" },
|
|
137
|
+
|
|
138
|
+
// ----- Primitive / Wild -----
|
|
139
|
+
{ id: "caveman", label: "Caveman", group: "Primitive / Wild", dimension: "type", description: "Stone Age primitive man", promptHint: "a caveman — Stone Age primitive in animal furs, with shaggy hair, a heavy brow and a sturdy frame" },
|
|
140
|
+
{ id: "cavewoman", label: "Cavewoman", group: "Primitive / Wild", dimension: "type", description: "Stone Age primitive woman", promptHint: "a cavewoman — Stone Age primitive in animal furs, with shaggy hair and a sturdy frame" },
|
|
141
|
+
{ id: "apeman", label: "Apeman", group: "Primitive / Wild", dimension: "type", description: "Half-human half-ape hybrid", promptHint: "an apeman — half-human half-ape hybrid with simian features, broad shoulders and a heavy stooped build" },
|
|
142
|
+
{ id: "feral-wildman", label: "Feral Wild-Human", group: "Primitive / Wild", dimension: "type", description: "Overgrown, primal feral human", promptHint: "a feral wild-human — overgrown matted hair, primal eyes and a near-naked, bestial bearing" },
|
|
143
|
+
{ id: "neanderthal", label: "Neanderthal", group: "Primitive / Wild", dimension: "type", description: "Pre-modern human species", promptHint: "a neanderthal — heavy brow, broad nose, stocky pre-modern human build with weather-toughened skin" },
|
|
144
|
+
|
|
145
|
+
// ----- Fantasy (humanoid) -----
|
|
146
|
+
{ id: "elf-man", label: "Elf (Male)", group: "Fantasy", dimension: "type", description: "Masculine pointed-ear humanoid", promptHint: "a male elf — slender masculine humanoid with delicately pointed ears, fine sharp features and an ethereal poise" },
|
|
147
|
+
{ id: "elf-woman", label: "Elf (Female)", group: "Fantasy", dimension: "type", description: "Feminine pointed-ear humanoid", promptHint: "a female elf — slender feminine humanoid with delicately pointed ears, fine elegant features and an ethereal poise" },
|
|
148
|
+
{ id: "elf", label: "Elf", group: "Fantasy", dimension: "type", description: "Slender humanoid with pointed ears", promptHint: "an elf — slender humanoid with delicately pointed ears, fine features and an ethereal poise" },
|
|
149
|
+
{ id: "half-elf", label: "Half-Elf", group: "Fantasy", dimension: "type", description: "Mixed elven-human heritage", promptHint: "a half-elf with subtly pointed ears and a graceful blend of human and elven features" },
|
|
150
|
+
{ id: "dwarf", label: "Dwarf", group: "Fantasy", dimension: "type", description: "Stocky mountain-folk humanoid", promptHint: "a dwarf — stocky, broad-shouldered humanoid with a thick beard and a hardy mountain-folk look" },
|
|
151
|
+
{ id: "orc", label: "Orc", group: "Fantasy", dimension: "type", description: "Heavyset tusked humanoid", promptHint: "an orc — heavyset humanoid with greenish skin, jutting tusks and a brutish, battle-scarred build" },
|
|
152
|
+
{ id: "goblin", label: "Goblin", group: "Fantasy", dimension: "type", description: "Small, wiry humanoid", promptHint: "a goblin — small, wiry humanoid with sharp features, oversized ears and a mischievous look" },
|
|
153
|
+
{ id: "tiefling", label: "Tiefling", group: "Fantasy", dimension: "type", description: "Horned humanoid with infernal heritage", promptHint: "a tiefling — humanoid with curling horns, a tail and faintly otherworldly skin tones" },
|
|
154
|
+
{ id: "faun", label: "Faun / Satyr", group: "Fantasy", dimension: "type", description: "Goat-legged horned humanoid", promptHint: "a faun — humanoid with goat legs, small curling horns and a woodland presence" },
|
|
155
|
+
{ id: "centaur", label: "Centaur", group: "Fantasy", dimension: "type", description: "Human torso on a horse body", promptHint: "a centaur — human torso fused to the body of a horse" },
|
|
156
|
+
{ id: "vampire-man", label: "Vampire (Male)", group: "Fantasy", dimension: "type", description: "Masculine fanged immortal", promptHint: "a male vampire — pale, fanged immortal man in formal evening wear with gothic elegance and a quiet predatory poise" },
|
|
157
|
+
{ id: "vampire-woman", label: "Vampire (Female)", group: "Fantasy", dimension: "type", description: "Feminine fanged immortal", promptHint: "a female vampire — pale, fanged immortal woman in flowing gothic attire with seductive elegance and a quiet predatory poise" },
|
|
158
|
+
{ id: "vampire", label: "Vampire", group: "Fantasy", dimension: "type", description: "Pale, fanged immortal", promptHint: "a vampire — pale, fanged immortal with a pristine gothic elegance and a quiet predatory poise" },
|
|
159
|
+
{ id: "werewolf", label: "Werewolf", group: "Fantasy", dimension: "type", description: "Partially transformed lycanthrope", promptHint: "a werewolf — partially transformed humanoid with wolfish snout, fur, claws and a feral musculature" },
|
|
160
|
+
{ id: "mermaid", label: "Mermaid", group: "Fantasy", dimension: "type", description: "Female humanoid with a fish tail", promptHint: "a mermaid — humanoid woman from the waist up with a long iridescent fish tail below" },
|
|
161
|
+
{ id: "merman", label: "Merman", group: "Fantasy", dimension: "type", description: "Male humanoid with a fish tail", promptHint: "a merman — humanoid man from the waist up with a long iridescent fish tail below" },
|
|
162
|
+
{ id: "dragonkin", label: "Dragonkin", group: "Fantasy", dimension: "type", description: "Scaled draconic humanoid", promptHint: "dragonkin — humanoid with scaled skin, ridged horns and reptilian draconic features" },
|
|
163
|
+
{ id: "wizard", label: "Wizard", group: "Fantasy", dimension: "type", description: "Robed male spellcaster", promptHint: "a wizard — robed male spellcaster with arcane symbols, a long staff and a contemplative bearing" },
|
|
164
|
+
{ id: "witch", label: "Witch", group: "Fantasy", dimension: "type", description: "Robed female spellcaster", promptHint: "a witch — robed female spellcaster with arcane sigils, a knowing gaze and a quietly powerful air" },
|
|
165
|
+
{ id: "sorcerer", label: "Sorcerer", group: "Fantasy", dimension: "type", description: "Magical man in flowing robes", promptHint: "a sorcerer — magical man in flowing robes wreathed in arcane glow" },
|
|
166
|
+
{ id: "sorceress", label: "Sorceress", group: "Fantasy", dimension: "type", description: "Magical woman in flowing robes", promptHint: "a sorceress — magical woman in flowing robes wreathed in arcane glow" },
|
|
167
|
+
{ id: "knight-man", label: "Knight (Male)", group: "Fantasy", dimension: "type", description: "Plate-armoured male warrior", promptHint: "a male knight in full plate armour with a heraldic surcoat, broad-shouldered build and a steady warrior's stance" },
|
|
168
|
+
{ id: "knight-woman", label: "Knight (Female)", group: "Fantasy", dimension: "type", description: "Plate-armoured female warrior", promptHint: "a female knight — warrior maiden in full plate armour with a heraldic surcoat, a poised stance and steady warrior's bearing" },
|
|
169
|
+
{ id: "knight", label: "Knight", group: "Fantasy", dimension: "type", description: "Plate-armoured warrior", promptHint: "a knight in full plate armour with a heraldic surcoat and a steady warrior's stance" },
|
|
170
|
+
{ id: "barbarian-man", label: "Barbarian (Male)", group: "Fantasy", dimension: "type", description: "Brawny male fur-clad warrior", promptHint: "a male barbarian — brawny masculine warrior in furs and tribal markings, weathered and battle-ready" },
|
|
171
|
+
{ id: "barbarian-woman", label: "Barbarian (Female)", group: "Fantasy", dimension: "type", description: "Strong female fur-clad warrior", promptHint: "a female barbarian — strong woman warrior in furs and tribal markings, weathered and battle-ready" },
|
|
172
|
+
{ id: "barbarian", label: "Barbarian", group: "Fantasy", dimension: "type", description: "Brawny fur-clad warrior", promptHint: "a barbarian — brawny warrior in furs and tribal markings, weathered and battle-ready" },
|
|
173
|
+
{ id: "druid", label: "Druid", group: "Fantasy", dimension: "type", description: "Nature-bound robed mystic", promptHint: "a druid — robed nature-binder wreathed in foliage, antler accents and earthy ornaments" },
|
|
174
|
+
{ id: "ranger", label: "Ranger", group: "Fantasy", dimension: "type", description: "Cloaked archer / wilderness scout", promptHint: "a ranger — cloaked archer in muted leather and forest greens, alert wilderness bearing" },
|
|
175
|
+
{ id: "necromancer", label: "Necromancer", group: "Fantasy", dimension: "type", description: "Pale dark-arts spellcaster", promptHint: "a necromancer — pale robed figure with dark sigils and a corpse-cold aura" },
|
|
176
|
+
{ id: "prince", label: "Prince", group: "Fantasy", dimension: "type", description: "Royal young man", promptHint: "a prince — royal young man in tailored regalia with a poised, courtly bearing" },
|
|
177
|
+
{ id: "princess", label: "Princess", group: "Fantasy", dimension: "type", description: "Royal young woman", promptHint: "a princess — royal young woman in flowing regalia with a poised, courtly bearing" },
|
|
178
|
+
|
|
179
|
+
// ----- Warriors / Martial -----
|
|
180
|
+
{ id: "ninja", label: "Ninja", group: "Warriors / Martial", dimension: "type", description: "Stealth-clad shinobi assassin", promptHint: "a ninja — masked shinobi in a black hooded shozoku with face wrap, lean athletic build, twin sword on the back and a stealthy combat-ready stance" },
|
|
181
|
+
{ id: "kunoichi", label: "Kunoichi", group: "Warriors / Martial", dimension: "type", description: "Female ninja", promptHint: "a kunoichi — female ninja in a fitted black hooded shozoku with face wrap, lean athletic build and a stealthy combat-ready stance" },
|
|
182
|
+
{ id: "samurai-man", label: "Samurai (Male)", group: "Warriors / Martial", dimension: "type", description: "Male Japanese armoured warrior", promptHint: "a male samurai — Japanese warrior in lacquered lamellar yoroi armour with shoulder guards, kabuto helmet, top-knot under the helmet, katana at the hip and a disciplined battle-ready stance" },
|
|
183
|
+
{ id: "samurai-woman", label: "Samurai (Female)", group: "Warriors / Martial", dimension: "type", description: "Female samurai (onna-musha)", promptHint: "a female samurai — onna-musha warrior in lacquered lamellar yoroi armour with shoulder guards, hair tied up, naginata or katana in hand and a disciplined battle-ready stance" },
|
|
184
|
+
{ id: "samurai", label: "Samurai", group: "Warriors / Martial", dimension: "type", description: "Japanese armoured warrior", promptHint: "a samurai — Japanese warrior in lacquered lamellar yoroi armour with shoulder guards, kabuto helmet, katana at the hip and a disciplined battle-ready stance" },
|
|
185
|
+
{ id: "karate-fighter", label: "Karate Fighter", group: "Warriors / Martial", dimension: "type", description: "Karate practitioner in gi", promptHint: "a karate practitioner in a crisp white karate gi tied with a black belt, barefoot, in a focused fighting stance" },
|
|
186
|
+
{ id: "kung-fu-master", label: "Kung Fu Master", group: "Warriors / Martial", dimension: "type", description: "Chinese martial-arts adept", promptHint: "a kung fu master in a flowing silk changshan tunic with frog-button closures, slippers and a poised martial stance, decades-of-training discipline" },
|
|
187
|
+
{ id: "shaolin-monk", label: "Shaolin Monk", group: "Warriors / Martial", dimension: "type", description: "Buddhist warrior monk", promptHint: "a Shaolin warrior monk with a shaved head, ochre and saffron robes wrapped over the shoulder, prayer beads, calloused hands and a centred meditative martial bearing" },
|
|
188
|
+
{ id: "sumo-wrestler", label: "Sumo Wrestler", group: "Warriors / Martial", dimension: "type", description: "Japanese sumo competitor", promptHint: "a sumo wrestler — heavy-set Japanese fighter in a thick mawashi loincloth, top-knot chonmage hairstyle, broad imposing build planted in a low ready stance" },
|
|
189
|
+
{ id: "gladiator", label: "Gladiator", group: "Warriors / Martial", dimension: "type", description: "Roman arena fighter", promptHint: "a gladiator — Roman arena fighter in a leather subligaculum, manica arm guard, greaves and a galea helmet, gladius and scutum shield, scarred and battle-hardened" },
|
|
190
|
+
{ id: "spartan-warrior", label: "Spartan Warrior", group: "Warriors / Martial", dimension: "type", description: "Ancient Greek hoplite", promptHint: "a Spartan hoplite — bronze Corinthian helmet with horsehair crest, crimson cloak, round aspis shield, dory spear, leather kilt and greaves, disciplined warrior bearing" },
|
|
191
|
+
{ id: "viking-warrior", label: "Viking Warrior", group: "Warriors / Martial", dimension: "type", description: "Norse raider", promptHint: "a Viking warrior — bearded Norse raider in a chainmail hauberk over a leather tunic, fur cloak, round wooden shield with iron boss, axe in hand and a horned-or-conical iron helm" },
|
|
192
|
+
{ id: "mongol-warrior", label: "Mongol Warrior", group: "Warriors / Martial", dimension: "type", description: "Steppe horse archer", promptHint: "a Mongol warrior — steppe horse archer in lamellar leather armour over a quilted coat, fur-trimmed conical helmet, recurve bow and quiver, weathered nomadic bearing" },
|
|
193
|
+
{ id: "centurion", label: "Centurion", group: "Warriors / Martial", dimension: "type", description: "Roman legionary officer", promptHint: "a Roman centurion in segmented lorica armour, transverse-crested galea helmet, red tunic and cape, gladius at the hip, vine staff in hand and a commanding bearing" },
|
|
194
|
+
{ id: "crusader", label: "Crusader Knight", group: "Warriors / Martial", dimension: "type", description: "Medieval crusader", promptHint: "a Crusader knight — chainmail hauberk under a white surcoat with a red cross emblem, conical helm, longsword and kite shield, devout warrior bearing" },
|
|
195
|
+
{ id: "pirate", label: "Pirate", group: "Warriors / Martial", dimension: "type", description: "Golden-age pirate / buccaneer", promptHint: "a pirate — Golden-Age buccaneer in a tricorn hat, weathered long coat, cutlass at the hip, flintlock pistol tucked in the belt, eyepatch or beaded beard, salty seafarer's swagger" },
|
|
196
|
+
{ id: "musketeer", label: "Musketeer", group: "Warriors / Martial", dimension: "type", description: "17th-century French swordsman", promptHint: "a 17th-century French musketeer in a wide-brimmed feathered hat, blue tabard with white cross, leather doublet, rapier at the hip, dashing swordsman's bearing" },
|
|
197
|
+
|
|
198
|
+
// ----- Mythic / Divine -----
|
|
199
|
+
{ id: "angel", label: "Angel", group: "Mythic / Divine", dimension: "type", description: "Winged divine humanoid", promptHint: "an angel — humanoid with luminous feathered wings, a soft halo of light and a serene divine presence" },
|
|
200
|
+
{ id: "demon", label: "Demon", group: "Mythic / Divine", dimension: "type", description: "Horned infernal humanoid", promptHint: "a demon — horned humanoid with red or charcoal skin, infernal eyes and a smouldering aura" },
|
|
201
|
+
{ id: "ghost", label: "Ghost", group: "Mythic / Divine", dimension: "type", description: "Translucent spectral figure", promptHint: "a ghost — translucent humanoid figure with a faint pale glow and a haunting drifting silhouette" },
|
|
202
|
+
{ id: "valkyrie", label: "Valkyrie", group: "Mythic / Divine", dimension: "type", description: "Norse warrior-spirit woman", promptHint: "a valkyrie — armoured warrior woman with metal-feathered wings and a shield-maiden's bearing" },
|
|
203
|
+
{ id: "god", label: "God", group: "Mythic / Divine", dimension: "type", description: "Male deity", promptHint: "a god — male deity with a radiant divine aura, idealised features and an aura of immense power" },
|
|
204
|
+
{ id: "goddess", label: "Goddess", group: "Mythic / Divine", dimension: "type", description: "Female deity", promptHint: "a goddess — female deity with a radiant divine aura, idealised features and an aura of immense power" },
|
|
205
|
+
|
|
206
|
+
// ----- Sci-Fi -----
|
|
207
|
+
{ id: "android-man", label: "Android (Male)", group: "Sci-Fi", dimension: "type", description: "Masculine humanoid robot", promptHint: "an android — humanoid robot with a masculine sculpt, smooth synthetic skin and visible mechanical seams" },
|
|
208
|
+
{ id: "android-woman", label: "Android (Female)", group: "Sci-Fi", dimension: "type", description: "Feminine humanoid robot", promptHint: "an android — humanoid robot with a feminine sculpt, smooth synthetic skin and visible mechanical seams" },
|
|
209
|
+
{ id: "android-neutral", label: "Android", group: "Sci-Fi", dimension: "type", description: "Humanoid robot, gender unspecified", promptHint: "an androgynous android — humanoid robot with a neutral sculpt and exposed precision servos" },
|
|
210
|
+
{ id: "cyborg-man", label: "Cyborg (Male)", group: "Sci-Fi", dimension: "type", description: "Masculine human-machine hybrid", promptHint: "a male cyborg — masculine human body fused with mechanical augmentations, cybernetic limbs and partial exposed circuitry" },
|
|
211
|
+
{ id: "cyborg-woman", label: "Cyborg (Female)", group: "Sci-Fi", dimension: "type", description: "Feminine human-machine hybrid", promptHint: "a female cyborg — feminine human body fused with mechanical augmentations, cybernetic limbs and partial exposed circuitry" },
|
|
212
|
+
{ id: "cyborg", label: "Cyborg", group: "Sci-Fi", dimension: "type", description: "Human-machine hybrid", promptHint: "a cyborg — human body fused with mechanical augmentations, cybernetic limbs and partial exposed circuitry" },
|
|
213
|
+
{ id: "robot", label: "Robot", group: "Sci-Fi", dimension: "type", description: "Fully mechanical humanoid", promptHint: "a robot — fully mechanical humanoid with metal plating, articulated joints and glowing optical sensors" },
|
|
214
|
+
{ id: "alien", label: "Alien", group: "Sci-Fi", dimension: "type", description: "Non-human humanoid", promptHint: "an alien — non-human humanoid with otherworldly facial features, unusual skin tone and unfamiliar proportions" },
|
|
215
|
+
{ id: "astronaut", label: "Astronaut", group: "Sci-Fi", dimension: "type", description: "Suited space explorer", promptHint: "an astronaut in a sealed pressure suit and reflective helmet visor with mission patches across the chest" },
|
|
216
|
+
{ id: "space-marine", label: "Space Marine", group: "Sci-Fi", dimension: "type", description: "Power-armoured soldier", promptHint: "a space marine in heavy power armour with a sealed helmet, reinforced shoulder pauldrons and a slung weapon harness" },
|
|
217
|
+
{ id: "ai-hologram", label: "AI Hologram", group: "Sci-Fi", dimension: "type", description: "Volumetric light figure", promptHint: "an AI hologram — humanoid figure rendered as translucent volumetric light with subtle scanline shimmer" },
|
|
218
|
+
|
|
219
|
+
// ----- Heroes & Villains -----
|
|
220
|
+
{ id: "superhero", label: "Superhero", group: "Heroes & Villains", dimension: "type", description: "Masked caped male hero", promptHint: "a superhero — masked, caped, in a fitted spandex suit with a chest emblem and a heroic ready stance" },
|
|
221
|
+
{ id: "superheroine", label: "Superheroine", group: "Heroes & Villains", dimension: "type", description: "Masked caped female hero", promptHint: "a superheroine — masked, caped, in a fitted spandex suit with a chest emblem and a heroic ready stance" },
|
|
222
|
+
{ id: "masked-vigilante", label: "Masked Vigilante", group: "Heroes & Villains", dimension: "type", description: "Dark-suited masked hero", promptHint: "a masked vigilante — dark tactical bodysuit with a half-mask covering the eyes, urban gritty aesthetic" },
|
|
223
|
+
{ id: "supervillain", label: "Supervillain", group: "Heroes & Villains", dimension: "type", description: "Theatrical evil archetype", promptHint: "a supervillain — theatrical menacing costume with a sinister emblem, dramatic cape and a commanding villain bearing" },
|
|
224
|
+
{ id: "caped-crusader-man", label: "Caped Crusader (Male)", group: "Heroes & Villains", dimension: "type", description: "Armored cowled male dark hero", promptHint: "a male caped crusader — armoured cowled masculine hero in dark colors with reinforced gauntlets, utility belt and a heavy cape" },
|
|
225
|
+
{ id: "caped-crusader-woman", label: "Caped Crusader (Female)", group: "Heroes & Villains", dimension: "type", description: "Armored cowled female dark hero", promptHint: "a female caped crusader — armoured cowled feminine hero in dark colors with reinforced gauntlets, utility belt and a heavy cape" },
|
|
226
|
+
{ id: "caped-crusader", label: "Caped Crusader", group: "Heroes & Villains", dimension: "type", description: "Armored cowled dark hero", promptHint: "a caped crusader — armoured cowled hero in dark colors with reinforced gauntlets, utility belt and a heavy cape" },
|
|
227
|
+
{ id: "super-soldier", label: "Super-Soldier", group: "Heroes & Villains", dimension: "type", description: "Patriotic exo-armoured warrior", promptHint: "a super-soldier — military-grade exo-armour with patriotic markings, reinforced helm and a star-spangled emblem" },
|
|
228
|
+
{ id: "mutant", label: "Mutant", group: "Heroes & Villains", dimension: "type", description: "Human with one or two visible mutations", promptHint: "a mutant — human-baseline body with one or two visible mutations such as unusual skin pigmentation, glowing eyes, claws or gills" },
|
|
229
|
+
{ id: "magical-girl", label: "Magical Girl", group: "Heroes & Villains", dimension: "type", description: "Transforming-heroine archetype", promptHint: "a magical girl — transforming heroine in a frilled costume with sparkles, ribbons, ornamented accessories and a transformation aura" },
|
|
230
|
+
|
|
231
|
+
// ----- Professions / Roles -----
|
|
232
|
+
{ id: "police-officer", label: "Police Officer", group: "Professions / Roles", dimension: "type", description: "Uniformed officer of the law", promptHint: "a police officer in a navy-blue uniform with badge, duty belt with holstered sidearm and radio, peaked cap or service hat, professional law-enforcement bearing" },
|
|
233
|
+
{ id: "policeman", label: "Policeman", group: "Professions / Roles", dimension: "type", description: "Male police officer", promptHint: "a male police officer in a navy-blue uniform with badge, duty belt with holstered sidearm, peaked cap, professional law-enforcement bearing" },
|
|
234
|
+
{ id: "policewoman", label: "Policewoman", group: "Professions / Roles", dimension: "type", description: "Female police officer", promptHint: "a female police officer in a navy-blue uniform with badge, duty belt with holstered sidearm, hair tucked under a service cap, professional law-enforcement bearing" },
|
|
235
|
+
{ id: "firefighter", label: "Firefighter", group: "Professions / Roles", dimension: "type", description: "Bunker-gear emergency responder", promptHint: "a firefighter in heavy yellow-and-tan turnout gear with reflective stripes, helmet with visor, suspenders and rubber boots, soot-streaked and ready for action" },
|
|
236
|
+
{ id: "doctor", label: "Doctor", group: "Professions / Roles", dimension: "type", description: "Physician in white coat", promptHint: "a doctor in a white medical coat over scrubs with a stethoscope around the neck, professional clinical bearing" },
|
|
237
|
+
{ id: "surgeon", label: "Surgeon", group: "Professions / Roles", dimension: "type", description: "Operating-room surgeon", promptHint: "a surgeon in teal or blue surgical scrubs with a surgical mask pulled down to the neck, scrub cap and gloved hands, focused operating-room bearing" },
|
|
238
|
+
{ id: "nurse", label: "Nurse", group: "Professions / Roles", dimension: "type", description: "Hospital nurse in scrubs", promptHint: "a nurse in coloured medical scrubs with an ID badge, stethoscope around the neck and a warm professional caregiver bearing" },
|
|
239
|
+
{ id: "chef", label: "Chef", group: "Professions / Roles", dimension: "type", description: "Kitchen-uniformed cook", promptHint: "a chef in a crisp white double-breasted chef's coat with a tall white toque hat, knotted neckerchief and an apron, focused culinary bearing" },
|
|
240
|
+
{ id: "teacher", label: "Teacher", group: "Professions / Roles", dimension: "type", description: "Classroom educator", promptHint: "a teacher in smart-casual professional attire — a button-down or blouse with a cardigan or blazer, holding a book or marker, warm patient educator bearing" },
|
|
241
|
+
{ id: "priest", label: "Priest", group: "Professions / Roles", dimension: "type", description: "Catholic clergyman", promptHint: "a priest in a black cassock with a white Roman collar, hands folded, calm devout clerical bearing" },
|
|
242
|
+
{ id: "nun", label: "Nun", group: "Professions / Roles", dimension: "type", description: "Catholic woman religious", promptHint: "a nun in a black habit and white wimple framing the face, scapular and rosary at the waist, hands folded in serene devout bearing" },
|
|
243
|
+
{ id: "soldier", label: "Soldier", group: "Professions / Roles", dimension: "type", description: "Modern military service member", promptHint: "a soldier in modern camouflage combat fatigues with body armour, helmet, slung rifle and tactical gear, disciplined military bearing" },
|
|
244
|
+
{ id: "lawyer", label: "Lawyer", group: "Professions / Roles", dimension: "type", description: "Suited attorney", promptHint: "a lawyer in a sharp tailored business suit with a dress shirt and tie or blouse, holding a leather portfolio, polished professional courtroom bearing" },
|
|
245
|
+
{ id: "judge", label: "Judge", group: "Professions / Roles", dimension: "type", description: "Robed judicial officer", promptHint: "a judge in a long black judicial robe over a shirt and collar, gavel in hand, an authoritative impartial courtroom bearing" },
|
|
246
|
+
{ id: "scientist", label: "Scientist", group: "Professions / Roles", dimension: "type", description: "Lab-coated researcher", promptHint: "a scientist in a white lab coat with safety goggles around the neck, holding a clipboard or beaker, focused inquisitive research bearing" },
|
|
247
|
+
{ id: "pilot", label: "Pilot", group: "Professions / Roles", dimension: "type", description: "Commercial-airline pilot", promptHint: "a commercial airline pilot in a navy-blue uniform with gold stripes on the sleeves, peaked cap with airline insignia, white shirt and black tie, confident captain's bearing" },
|
|
248
|
+
{ id: "flight-attendant", label: "Flight Attendant", group: "Professions / Roles", dimension: "type", description: "Cabin-crew member", promptHint: "a flight attendant in a tailored airline uniform with neck scarf or tie, name tag and wings pin, polished hospitable cabin-crew bearing" },
|
|
249
|
+
{ id: "construction-worker", label: "Construction Worker", group: "Professions / Roles", dimension: "type", description: "Hard-hatted builder", promptHint: "a construction worker in a high-visibility safety vest over a work shirt, hard hat, work jeans and steel-toed boots, tool belt at the waist, hands-on builder bearing" },
|
|
250
|
+
{ id: "businessman", label: "Businessman", group: "Professions / Roles", dimension: "type", description: "Suited corporate man", promptHint: "a businessman in a sharp tailored two-piece suit with a crisp dress shirt and tie, polished leather shoes, briefcase in hand, confident corporate bearing" },
|
|
251
|
+
{ id: "businesswoman", label: "Businesswoman", group: "Professions / Roles", dimension: "type", description: "Suited corporate woman", promptHint: "a businesswoman in a tailored skirt or trouser suit with a blouse, heels or smart flats, holding a portfolio, confident corporate bearing" },
|
|
252
|
+
{ id: "farmer", label: "Farmer", group: "Professions / Roles", dimension: "type", description: "Agricultural worker", promptHint: "a farmer in worn denim overalls over a flannel shirt, a wide-brimmed straw or trucker hat, work boots and weathered hands, rustic agricultural bearing" },
|
|
253
|
+
{ id: "mechanic", label: "Mechanic", group: "Professions / Roles", dimension: "type", description: "Auto / industrial mechanic", promptHint: "a mechanic in oil-stained navy coveralls or work shirt with rolled sleeves, grease-streaked hands, a wrench in hand and a name patch on the chest" },
|
|
254
|
+
{ id: "artist", label: "Artist", group: "Professions / Roles", dimension: "type", description: "Painter / visual artist", promptHint: "an artist in paint-spattered casual clothing with rolled-up sleeves, brush or palette in hand, an inquisitive creative bearing" },
|
|
255
|
+
{ id: "musician", label: "Musician", group: "Professions / Roles", dimension: "type", description: "Performer with instrument", promptHint: "a musician holding their instrument in a stylish performance outfit, expressive performer's bearing" },
|
|
256
|
+
{ id: "athlete", label: "Athlete", group: "Professions / Roles", dimension: "type", description: "Pro-sport competitor", promptHint: "an athlete in technical performance sportswear with a defined athletic build, focused competitive bearing" },
|
|
257
|
+
{ id: "detective", label: "Detective", group: "Professions / Roles", dimension: "type", description: "Plain-clothes investigator", promptHint: "a plain-clothes detective in a rumpled trench coat or sport jacket over a dress shirt, badge clipped to the belt, sharp investigative bearing" },
|
|
258
|
+
{ id: "journalist", label: "Journalist", group: "Professions / Roles", dimension: "type", description: "Reporter with notebook / camera", promptHint: "a journalist with a press lanyard, notebook and pen or microphone in hand, smart-casual professional attire, alert reporter's bearing" },
|
|
259
|
+
{ id: "barista", label: "Barista", group: "Professions / Roles", dimension: "type", description: "Coffee-shop server", promptHint: "a barista in a dark apron over a casual t-shirt, name tag, behind-the-counter bearing with a friendly café-staff vibe" },
|
|
260
|
+
|
|
261
|
+
// ----- Hybrid / Anthro -----
|
|
262
|
+
// Person archetypes — visual style (anime, realistic, painterly…) is
|
|
263
|
+
// chosen separately via the Style node, so prompts here describe the
|
|
264
|
+
// creature without baking in any specific art style.
|
|
265
|
+
{ id: "catgirl", label: "Catgirl", group: "Hybrid / Anthro", dimension: "type", description: "Woman with cat ears and tail", promptHint: "a catgirl — humanoid woman with cat ears on her head and a soft cat tail" },
|
|
266
|
+
{ id: "catboy", label: "Catboy", group: "Hybrid / Anthro", dimension: "type", description: "Man with cat ears and tail", promptHint: "a catboy — humanoid man with cat ears on his head and a soft cat tail" },
|
|
267
|
+
{ id: "anthro", label: "Anthropomorphic", group: "Hybrid / Anthro", dimension: "type", description: "Bipedal animal-headed humanoid", promptHint: "an anthropomorphic animal humanoid — bipedal body in human stance with an animal head, fur and animal tail" },
|
|
268
|
+
|
|
269
|
+
// ----- Iconic / Public Domain -----
|
|
270
|
+
// Only public-domain figures — modern trademarked characters (Superman,
|
|
271
|
+
// Spider-Man, Yoda, Mickey, etc.) are deliberately NOT shipped as named
|
|
272
|
+
// entries. Users wanting them can type the name into the prompt; we don't
|
|
273
|
+
// ship a button.
|
|
274
|
+
{ id: "sherlock-holmes", label: "Sherlock Holmes", group: "Iconic / Public Domain", dimension: "type", description: "Victorian consulting detective", promptHint: "Sherlock Holmes — Victorian consulting detective in a tweed deerstalker hat and Inverness cape, pipe in hand, sharp analytical gaze" },
|
|
275
|
+
{ id: "dracula", label: "Count Dracula", group: "Iconic / Public Domain", dimension: "type", description: "Stoker's gothic vampire count", promptHint: "Count Dracula — gothic vampire count in formal black evening wear with a long opera cape, pale aristocratic features and pronounced fangs" },
|
|
276
|
+
{ id: "frankenstein-monster", label: "Frankenstein's Monster", group: "Iconic / Public Domain", dimension: "type", description: "Shelley's reanimated creature", promptHint: "Frankenstein's Monster — tall reanimated humanoid with a flat-topped head, bolts in the neck, sutured greenish skin and a heavy stooped frame" },
|
|
277
|
+
{ id: "phantom-opera", label: "Phantom of the Opera", group: "Iconic / Public Domain", dimension: "type", description: "Leroux's masked operatic recluse", promptHint: "the Phantom of the Opera — masked operatic figure in formal evening wear with a stark white half-mask covering one side of the face" },
|
|
278
|
+
{ id: "mr-hyde", label: "Mr Hyde", group: "Iconic / Public Domain", dimension: "type", description: "Stevenson's monstrous alter ego", promptHint: "Mr Hyde — bestial transformed alter ego with a hunched ape-like frame, brutish features and a sneer of malevolent glee" },
|
|
279
|
+
{ id: "king-arthur", label: "King Arthur", group: "Iconic / Public Domain", dimension: "type", description: "Legendary medieval king", promptHint: "King Arthur — armoured medieval king in plate and chainmail with a regal crown, the legendary sword Excalibur at his side" },
|
|
280
|
+
{ id: "merlin", label: "Merlin", group: "Iconic / Public Domain", dimension: "type", description: "Arthurian wizard advisor", promptHint: "Merlin — Arthurian wizard with a long flowing white beard, deep blue robes embroidered with stars, a tall pointed hat and a gnarled wooden staff" },
|
|
281
|
+
{ id: "robin-hood", label: "Robin Hood", group: "Iconic / Public Domain", dimension: "type", description: "Forest outlaw archer", promptHint: "Robin Hood — forest outlaw archer in green tunic and hood with a longbow and quiver of arrows, leather boots and a roguish grin" },
|
|
282
|
+
{ id: "joan-of-arc", label: "Joan of Arc", group: "Iconic / Public Domain", dimension: "type", description: "Medieval warrior-maid in armour", promptHint: "Joan of Arc — medieval warrior-maid in shining plate armour over a white surcoat, holding a banner, short cropped hair and a determined holy expression" },
|
|
283
|
+
{ id: "cleopatra", label: "Cleopatra", group: "Iconic / Public Domain", dimension: "type", description: "Last pharaoh of Egypt", promptHint: "Cleopatra — Egyptian pharaoh-queen in pleated white linen and a broad gold collar, kohl-lined almond eyes, a black bobbed wig and a golden uraeus diadem" },
|
|
284
|
+
{ id: "captain-nemo", label: "Captain Nemo", group: "Iconic / Public Domain", dimension: "type", description: "Verne's submarine captain", promptHint: "Captain Nemo — 19th-century submarine captain in a dark double-breasted naval coat, peaked cap and a stern mariner's bearing" },
|
|
285
|
+
{ id: "alice-wonderland", label: "Alice (Wonderland)", group: "Iconic / Public Domain", dimension: "type", description: "Carroll's Wonderland girl", promptHint: "Alice from Wonderland — young girl in a blue dress with a white pinafore apron, long blonde hair tied back with a black bow and white knee-high stockings" },
|
|
286
|
+
{ id: "peter-pan", label: "Peter Pan", group: "Iconic / Public Domain", dimension: "type", description: "Boy who never grew up", promptHint: "Peter Pan — boyish figure in a green tunic and tights with a pointed feathered cap, mischievous grin and a youthful adventurous bearing" },
|
|
287
|
+
{ id: "dorothy-oz", label: "Dorothy (Oz)", group: "Iconic / Public Domain", dimension: "type", description: "Oz girl in blue gingham", promptHint: "Dorothy from Oz — young girl in a blue and white gingham dress with brown pigtail braids tied with ribbons and ruby slippers" },
|
|
288
|
+
{ id: "tin-man", label: "Tin Man", group: "Iconic / Public Domain", dimension: "type", description: "Oz tin woodsman", promptHint: "the Tin Man from Oz — humanoid figure made of riveted tin plates with a funnel-shaped hat, an axe in hand and an oil-can at the belt" },
|
|
289
|
+
{ id: "scarecrow-oz", label: "Scarecrow (Oz)", group: "Iconic / Public Domain", dimension: "type", description: "Oz straw scarecrow", promptHint: "the Scarecrow from Oz — straw-stuffed humanoid in a ragged farmer's outfit and floppy pointed hat with a painted burlap face and patched clothes" },
|
|
290
|
+
{ id: "cowardly-lion", label: "Cowardly Lion", group: "Iconic / Public Domain", dimension: "type", description: "Oz anthropomorphic lion", promptHint: "the Cowardly Lion from Oz — anthropomorphic bipedal lion with a thick mane, expressive humanlike face and a fretful, timid posture" },
|
|
291
|
+
{ id: "santa-claus", label: "Santa Claus", group: "Iconic / Public Domain", dimension: "type", description: "Christmas gift-giver", promptHint: "Santa Claus — jolly bearded old man in a red coat trimmed with white fur, a wide black belt, red trousers and tall black boots, carrying a sack of toys" },
|
|
292
|
+
{ id: "grim-reaper", label: "Grim Reaper", group: "Iconic / Public Domain", dimension: "type", description: "Personification of Death", promptHint: "the Grim Reaper — tall hooded skeletal figure in a tattered black robe, bony hands gripping a long wooden scythe, face hidden in shadow" },
|
|
293
|
+
|
|
294
|
+
// -------------------- Age --------------------
|
|
295
|
+
// Young ages get finer splits — a 2yo, 5yo, and 8yo look very different;
|
|
296
|
+
// 14yo vs 18yo vs 22yo vs 28yo all read distinct on screen. From 30 onward
|
|
297
|
+
// a single decade bucket reads close enough. Custom age lets the user
|
|
298
|
+
// dial in any specific number for full control.
|
|
299
|
+
{ id: "age-baby", label: "Baby", dimension: "age", description: "Newborn / infant under 1", promptHint: "a baby under 1 year old" },
|
|
300
|
+
{ id: "age-toddler", label: "Toddler", dimension: "age", description: "Around 2-3 years old", promptHint: "a toddler around 2-3 years old" },
|
|
301
|
+
{ id: "age-young-child", label: "Young Child", dimension: "age", description: "Around 4-6 years old", promptHint: "a young child around 5 years old" },
|
|
302
|
+
{ id: "age-child", label: "Child", dimension: "age", description: "Around 7-9 years old", promptHint: "a child around 8 years old" },
|
|
303
|
+
{ id: "age-pre-teen", label: "Pre-Teen", dimension: "age", description: "Around 10-12 years old", promptHint: "a pre-teen around 11 years old" },
|
|
304
|
+
{ id: "age-early-teen", label: "Early Teen", dimension: "age", description: "Around 13-15 years old", promptHint: "in their early teens, around 14 years old" },
|
|
305
|
+
{ id: "age-late-teen", label: "Late Teen", dimension: "age", description: "Around 16-19 years old", promptHint: "in their late teens, around 17 years old" },
|
|
306
|
+
{ id: "age-teen", label: "Teenager (any)", dimension: "age", description: "Anywhere in 13-19", promptHint: "a teenager, somewhere in their teens" },
|
|
307
|
+
{ id: "age-early-20s", label: "Early 20s", dimension: "age", description: "Around 20-23 years old", promptHint: "in their early 20s, around 21 years old" },
|
|
308
|
+
{ id: "age-late-20s", label: "Late 20s", dimension: "age", description: "Around 24-29 years old", promptHint: "in their late 20s, around 27 years old" },
|
|
309
|
+
{ id: "age-20s", label: "20s (any)", dimension: "age", description: "Anywhere in their twenties", promptHint: "in their 20s" },
|
|
310
|
+
{ id: "age-30s", label: "30s", dimension: "age", description: "Thirties", promptHint: "in their 30s" },
|
|
311
|
+
{ id: "age-40s", label: "40s", dimension: "age", description: "Forties", promptHint: "in their 40s" },
|
|
312
|
+
{ id: "age-50s", label: "50s", dimension: "age", description: "Fifties", promptHint: "in their 50s" },
|
|
313
|
+
{ id: "age-60s", label: "60s", dimension: "age", description: "Sixties", promptHint: "in their 60s" },
|
|
314
|
+
{ id: "age-elderly", label: "Elderly", dimension: "age", description: "70 and older", promptHint: "elderly, in their 70s or older" },
|
|
315
|
+
{ id: "age-custom", label: "Custom age...", dimension: "age", description: "Specify an exact age in years", promptHint: "" },
|
|
316
|
+
|
|
317
|
+
// -------------------- Ethnicity --------------------
|
|
318
|
+
// Asian (generic + East / South / Southeast breakouts + national-level splits)
|
|
319
|
+
{ id: "asian-any", label: "Asian (any)", shortLabel: "any", group: "Asian", dimension: "ethnicity", description: "Any Asian — unspecified region", promptHint: "of Asian descent" },
|
|
320
|
+
{ id: "east-asian", label: "East Asian (any)", shortLabel: "East (any)", group: "Asian", dimension: "ethnicity", description: "East Asian features (broad)", promptHint: "East Asian" },
|
|
321
|
+
{ id: "chinese", label: "Chinese", group: "Asian", dimension: "ethnicity", description: "Han Chinese features", promptHint: "of Chinese descent" },
|
|
322
|
+
{ id: "japanese", label: "Japanese", group: "Asian", dimension: "ethnicity", description: "Japanese features", promptHint: "of Japanese descent" },
|
|
323
|
+
{ id: "korean", label: "Korean", group: "Asian", dimension: "ethnicity", description: "Korean features", promptHint: "of Korean descent" },
|
|
324
|
+
{ id: "mongolian-tibetan", label: "Mongolian / Tibetan", shortLabel: "Mongol / Tibetan", group: "Asian", dimension: "ethnicity", description: "Mongolian / Tibetan features", promptHint: "of Mongolian or Tibetan descent" },
|
|
325
|
+
{ id: "south-asian", label: "South Asian / Indian", shortLabel: "South / Indian", group: "Asian", dimension: "ethnicity", description: "Indian / Pakistani / Bangladeshi / Sri Lankan", promptHint: "South Asian" },
|
|
326
|
+
{ id: "southeast-asian", label: "Southeast Asian (any)", shortLabel: "Southeast (any)", group: "Asian", dimension: "ethnicity", description: "Southeast Asian (broad)", promptHint: "Southeast Asian" },
|
|
327
|
+
{ id: "thai", label: "Thai", group: "Asian", dimension: "ethnicity", description: "Thai features", promptHint: "of Thai descent" },
|
|
328
|
+
{ id: "vietnamese", label: "Vietnamese", group: "Asian", dimension: "ethnicity", description: "Vietnamese features", promptHint: "of Vietnamese descent" },
|
|
329
|
+
{ id: "indonesian-malay", label: "Indonesian / Malay", shortLabel: "Indo / Malay", group: "Asian", dimension: "ethnicity", description: "Indonesian / Malay features", promptHint: "of Indonesian or Malay descent" },
|
|
330
|
+
{ id: "khmer", label: "Khmer / Cambodian", shortLabel: "Khmer", group: "Asian", dimension: "ethnicity", description: "Khmer / Cambodian features", promptHint: "of Khmer Cambodian descent" },
|
|
331
|
+
{ id: "filipino", label: "Filipino", group: "Asian", dimension: "ethnicity", description: "Filipino features (Asian + Spanish heritage)", promptHint: "Filipino" },
|
|
332
|
+
// Middle East & North Africa
|
|
333
|
+
{ id: "mena-any", label: "Middle Eastern / N. African (any)", shortLabel: "any", group: "Middle East & N. Africa", dimension: "ethnicity", description: "Any MENA — unspecified", promptHint: "of Middle Eastern or North African descent" },
|
|
334
|
+
{ id: "middle-eastern", label: "Middle Eastern (Arab)", shortLabel: "Arab", group: "Middle East & N. Africa", dimension: "ethnicity", description: "Arab / Levantine / Gulf features", promptHint: "Middle Eastern" },
|
|
335
|
+
{ id: "turkish", label: "Turkish", group: "Middle East & N. Africa", dimension: "ethnicity", description: "Anatolian Turkish features", promptHint: "of Turkish descent" },
|
|
336
|
+
{ id: "persian", label: "Persian / Iranian", shortLabel: "Persian", group: "Middle East & N. Africa", dimension: "ethnicity", description: "Indo-Iranian features", promptHint: "of Persian Iranian descent" },
|
|
337
|
+
{ id: "israeli", label: "Israeli", group: "Middle East & N. Africa", dimension: "ethnicity", description: "Israeli features (Mediterranean Levantine, broad)", promptHint: "of Israeli descent" },
|
|
338
|
+
// African
|
|
339
|
+
{ id: "african", label: "African (any)", shortLabel: "any", group: "African", dimension: "ethnicity", description: "Any Sub-Saharan African — unspecified region", promptHint: "of African descent" },
|
|
340
|
+
{ id: "west-african", label: "West African", shortLabel: "West", group: "African", dimension: "ethnicity", description: "Yoruba / Igbo / Akan / Wolof", promptHint: "of West African descent" },
|
|
341
|
+
{ id: "east-african", label: "East African", shortLabel: "East", group: "African", dimension: "ethnicity", description: "Somali / Ethiopian / Swahili", promptHint: "of East African descent" },
|
|
342
|
+
{ id: "central-african", label: "Central African", shortLabel: "Central", group: "African", dimension: "ethnicity", description: "Bantu / Congolese / Angolan", promptHint: "of Central African descent" },
|
|
343
|
+
{ id: "southern-african", label: "Southern African", shortLabel: "Southern", group: "African", dimension: "ethnicity", description: "Zulu / Xhosa / Sotho", promptHint: "of Southern African descent" },
|
|
344
|
+
{ id: "afro-caribbean", label: "Afro-Caribbean", group: "African", dimension: "ethnicity", description: "Anglophone Caribbean (Jamaica / Trinidad / Barbados)", promptHint: "of Afro-Caribbean descent" },
|
|
345
|
+
// European
|
|
346
|
+
{ id: "european-any", label: "European (any)", shortLabel: "any", group: "European", dimension: "ethnicity", description: "Any European — unspecified region", promptHint: "of European descent" },
|
|
347
|
+
{ id: "nordic", label: "Nordic (any)", shortLabel: "Nordic", group: "European", dimension: "ethnicity", description: "Any Scandinavian / Northern European — unspecified region", promptHint: "of Nordic Scandinavian descent" },
|
|
348
|
+
{ id: "swedish", label: "Swedish", group: "European", dimension: "ethnicity", description: "Sweden — tall, often blonde, fine features", promptHint: "of Swedish descent" },
|
|
349
|
+
{ id: "norwegian", label: "Norwegian", group: "European", dimension: "ethnicity", description: "Norway — fair-skinned, often light-eyed", promptHint: "of Norwegian descent" },
|
|
350
|
+
{ id: "danish", label: "Danish", group: "European", dimension: "ethnicity", description: "Denmark — soft features, ash-blonde common", promptHint: "of Danish descent" },
|
|
351
|
+
{ id: "finnish", label: "Finnish", group: "European", dimension: "ethnicity", description: "Finland — Uralic features, high cheekbones", promptHint: "of Finnish descent" },
|
|
352
|
+
{ id: "icelandic", label: "Icelandic", group: "European", dimension: "ethnicity", description: "Iceland — Norse + Celtic blend, sharp angular features", promptHint: "of Icelandic descent" },
|
|
353
|
+
{ id: "celtic", label: "Celtic", group: "European", dimension: "ethnicity", description: "British / Irish features", promptHint: "of Celtic British descent" },
|
|
354
|
+
{ id: "mediterranean", label: "Mediterranean", shortLabel: "Mediter.", group: "European", dimension: "ethnicity", description: "Southern European (broad)", promptHint: "of Mediterranean descent" },
|
|
355
|
+
{ id: "slavic", label: "Slavic", group: "European", dimension: "ethnicity", description: "Eastern European features", promptHint: "of Slavic Eastern European descent" },
|
|
356
|
+
{ id: "czech", label: "Czech", group: "European", dimension: "ethnicity", description: "Czech features (West Slavic, Central European)", promptHint: "of Czech descent" },
|
|
357
|
+
{ id: "russian", label: "Russian", group: "European", dimension: "ethnicity", description: "Russian features (East Slavic)", promptHint: "of Russian descent" },
|
|
358
|
+
{ id: "ukrainian", label: "Ukrainian", group: "European", dimension: "ethnicity", description: "Ukrainian features (East Slavic)", promptHint: "of Ukrainian descent" },
|
|
359
|
+
{ id: "italian", label: "Italian", group: "European", dimension: "ethnicity", description: "Italian features", promptHint: "of Italian descent" },
|
|
360
|
+
{ id: "iberian", label: "Iberian (Spanish / Portuguese)", shortLabel: "Iberian", group: "European", dimension: "ethnicity", description: "Spanish / Portuguese with Celtiberian + Moorish heritage", promptHint: "of Iberian Spanish or Portuguese descent" },
|
|
361
|
+
{ id: "french", label: "French", group: "European", dimension: "ethnicity", description: "French features (varies north to south)", promptHint: "of French descent" },
|
|
362
|
+
{ id: "germanic", label: "Germanic", group: "European", dimension: "ethnicity", description: "German / Austrian / Dutch / Swiss-German", promptHint: "of Germanic descent" },
|
|
363
|
+
{ id: "greek", label: "Greek", group: "European", dimension: "ethnicity", description: "Greek features", promptHint: "of Greek descent" },
|
|
364
|
+
{ id: "baltic", label: "Baltic", group: "European", dimension: "ethnicity", description: "Lithuanian / Latvian / Estonian", promptHint: "of Baltic descent" },
|
|
365
|
+
// Latin American
|
|
366
|
+
{ id: "latin-american", label: "Latin American (any)", shortLabel: "any", group: "Latin American", dimension: "ethnicity", description: "Any Latin American — unspecified region", promptHint: "Latin American" },
|
|
367
|
+
{ id: "mexican-mesoamerican", label: "Mexican / Mesoamerican", shortLabel: "Mexican", group: "Latin American", dimension: "ethnicity", description: "Mexican / Guatemalan / Central American Mestizo", promptHint: "of Mexican or Mesoamerican heritage" },
|
|
368
|
+
{ id: "caribbean-latino", label: "Caribbean Latin", shortLabel: "Caribbean", group: "Latin American", dimension: "ethnicity", description: "Cuban / Dominican / Puerto Rican", promptHint: "of Caribbean Latin heritage" },
|
|
369
|
+
{ id: "brazilian", label: "Brazilian", group: "Latin American", dimension: "ethnicity", description: "Brazilian (Portuguese + African + Indigenous)", promptHint: "of Brazilian heritage" },
|
|
370
|
+
{ id: "andean-indigenous", label: "Andean / Indigenous Latin", shortLabel: "Andean", group: "Latin American", dimension: "ethnicity", description: "Quechua / Aymara / Maya / Nahua", promptHint: "of Andean or Indigenous Latin American heritage" },
|
|
371
|
+
{ id: "southern-cone", label: "Southern Cone European", shortLabel: "Southern Cone", group: "Latin American", dimension: "ethnicity", description: "Argentinian / Uruguayan / Chilean (largely European descent)", promptHint: "of Southern Cone European descent" },
|
|
372
|
+
// Indigenous
|
|
373
|
+
{ id: "indigenous-any", label: "Indigenous (any)", shortLabel: "any", group: "Indigenous", dimension: "ethnicity", description: "Any Indigenous — unspecified group", promptHint: "of Indigenous descent" },
|
|
374
|
+
{ id: "native-american", label: "Native American", shortLabel: "Native American", group: "Indigenous", dimension: "ethnicity", description: "Native American / First Nations", promptHint: "Native American" },
|
|
375
|
+
{ id: "aboriginal-australian", label: "Aboriginal Australian", shortLabel: "Aboriginal", group: "Indigenous", dimension: "ethnicity", description: "Aboriginal Australian / Torres Strait", promptHint: "Aboriginal Australian" },
|
|
376
|
+
{ id: "pacific-islander", label: "Pacific Islander", shortLabel: "Pacific Isl.", group: "Indigenous", dimension: "ethnicity", description: "Polynesian / Maori / Samoan / Hawaiian", promptHint: "Pacific Islander" },
|
|
377
|
+
{ id: "inuit", label: "Inuit / Arctic", shortLabel: "Inuit", group: "Indigenous", dimension: "ethnicity", description: "Inuit / Yupik / Aleut", promptHint: "of Inuit Arctic descent" },
|
|
378
|
+
// Other
|
|
379
|
+
{ id: "mixed", label: "Mixed", group: "Other", dimension: "ethnicity", description: "Mixed heritage", promptHint: "of mixed heritage" },
|
|
380
|
+
|
|
381
|
+
// -------------------- Body — six independent proportion axes --------------------
|
|
382
|
+
// Frame / Body Mass / Bust / Waist / Hips / Silhouette replace the old single
|
|
383
|
+
// Build + Body Proportions pickers, so otherwise-inexpressible bodies (e.g.
|
|
384
|
+
// "slim frame + full bust") become representable. Neutral values (Average /
|
|
385
|
+
// Balanced) carry an empty promptHint — selecting them injects nothing.
|
|
386
|
+
// ORDER MATTERS: hints compose by comma-join in PERSON_DIMENSION_ORDER, so
|
|
387
|
+
// Frame:Slim ("slim") + Body Mass:Lean ("lean build") → "slim, lean build".
|
|
388
|
+
|
|
389
|
+
// ----- Frame (skeletal width / height) -----
|
|
390
|
+
{ id: "frame-petite", label: "Petite", dimension: "frame", description: "Small, short-statured frame", promptHint: "petite" },
|
|
391
|
+
{ id: "frame-slim", label: "Slim", dimension: "frame", description: "Narrow, slender frame", promptHint: "slim" },
|
|
392
|
+
{ id: "frame-average", label: "Average", dimension: "frame", description: "Average frame", promptHint: "" },
|
|
393
|
+
{ id: "frame-broad", label: "Broad", dimension: "frame", description: "Wide, broad-shouldered frame", promptHint: "broad-framed" },
|
|
394
|
+
|
|
395
|
+
// ----- Body Mass (leanness / weight, independent of Frame) -----
|
|
396
|
+
{ id: "mass-lean", label: "Lean", dimension: "body-mass", description: "Lean, low body mass", promptHint: "lean build" },
|
|
397
|
+
{ id: "mass-average", label: "Average", dimension: "body-mass", description: "Average body mass", promptHint: "" },
|
|
398
|
+
{ id: "mass-full", label: "Full", dimension: "body-mass", description: "Full-figured", promptHint: "full-figured" },
|
|
399
|
+
{ id: "mass-heavy", label: "Heavy", dimension: "body-mass", description: "Heavy-set, large build", promptHint: "heavy-set build" },
|
|
400
|
+
|
|
401
|
+
// ----- Bust (applicable subjects; ungated like Facial Hair, neutral = no-op) -----
|
|
402
|
+
{ id: "bust-small", label: "Small", dimension: "bust", description: "Small bust", promptHint: "small bust" },
|
|
403
|
+
{ id: "bust-average", label: "Average", dimension: "bust", description: "Average bust", promptHint: "" },
|
|
404
|
+
{ id: "bust-full", label: "Full", dimension: "bust", description: "Full bust", promptHint: "full bust" },
|
|
405
|
+
{ id: "bust-very-full", label: "Very Full", dimension: "bust", description: "Very full bust", promptHint: "very full bust" },
|
|
406
|
+
|
|
407
|
+
// ----- Waist -----
|
|
408
|
+
{ id: "waist-defined", label: "Defined", dimension: "waist", description: "Defined, cinched waist", promptHint: "defined waist" },
|
|
409
|
+
{ id: "waist-average", label: "Average", dimension: "waist", description: "Average waist", promptHint: "" },
|
|
410
|
+
{ id: "waist-straight", label: "Straight", dimension: "waist", description: "Straight, undefined waist", promptHint: "straight waistline" },
|
|
411
|
+
|
|
412
|
+
// ----- Hips -----
|
|
413
|
+
{ id: "hips-narrow", label: "Narrow", dimension: "hips", description: "Narrow hips", promptHint: "narrow hips" },
|
|
414
|
+
{ id: "hips-balanced", label: "Balanced", dimension: "hips", description: "Balanced hips", promptHint: "" },
|
|
415
|
+
{ id: "hips-wide", label: "Wide", dimension: "hips", description: "Wide hips", promptHint: "wide hips" },
|
|
416
|
+
|
|
417
|
+
// ----- Silhouette (overall body shape; optional, no neutral) -----
|
|
418
|
+
{ id: "silhouette-hourglass", label: "Hourglass", dimension: "silhouette", description: "Balanced bust + hips, defined waist", promptHint: "hourglass silhouette" },
|
|
419
|
+
{ id: "silhouette-rectangular", label: "Rectangular", dimension: "silhouette", description: "Straight up-and-down silhouette", promptHint: "rectangular silhouette" },
|
|
420
|
+
{ id: "silhouette-pear", label: "Pear", dimension: "silhouette", description: "Hips wider than shoulders", promptHint: "pear-shaped silhouette" },
|
|
421
|
+
{ id: "silhouette-inverted", label: "Inverted Triangle", dimension: "silhouette", description: "Broad shoulders, narrow hips", promptHint: "inverted-triangle silhouette" },
|
|
422
|
+
{ id: "silhouette-athletic", label: "Athletic", dimension: "silhouette", description: "Lean, toned, V-taper", promptHint: "athletic silhouette" },
|
|
423
|
+
|
|
424
|
+
// -------------------- Face Shape --------------------
|
|
425
|
+
{ id: "face-oval", label: "Oval", dimension: "face-shape", description: "Slightly longer than wide, soft curves", promptHint: "an oval face shape" },
|
|
426
|
+
{ id: "face-round", label: "Round", dimension: "face-shape", description: "Soft circular face", promptHint: "a round face shape" },
|
|
427
|
+
{ id: "face-square", label: "Square", dimension: "face-shape", description: "Strong jaw, equal width forehead and jaw", promptHint: "a square face shape with a strong jaw" },
|
|
428
|
+
{ id: "face-heart", label: "Heart", dimension: "face-shape", description: "Wider forehead, pointed chin", promptHint: "a heart-shaped face with a wide forehead and pointed chin" },
|
|
429
|
+
{ id: "face-diamond", label: "Diamond", dimension: "face-shape", description: "Narrow forehead and chin, wide cheekbones", promptHint: "a diamond face shape with a narrow forehead and chin and wide cheekbones" },
|
|
430
|
+
{ id: "face-oblong", label: "Oblong", dimension: "face-shape", description: "Long and narrow", promptHint: "an oblong face shape, long and narrow" },
|
|
431
|
+
{ id: "face-triangular", label: "Triangular", dimension: "face-shape", description: "Narrow forehead, wide jaw", promptHint: "a triangular face shape with a narrow forehead and wide jaw" },
|
|
432
|
+
|
|
433
|
+
// -------------------- Jawline --------------------
|
|
434
|
+
{ id: "jaw-strong", label: "Strong", dimension: "jawline", description: "Sharp, defined jawline", promptHint: "a strong, sharply defined jawline" },
|
|
435
|
+
{ id: "jaw-soft", label: "Soft", dimension: "jawline", description: "Soft, rounded jaw", promptHint: "a soft rounded jawline" },
|
|
436
|
+
{ id: "jaw-pointed", label: "Pointed", dimension: "jawline", description: "Pointed chin, narrow jaw", promptHint: "a pointed chin with a narrow jawline" },
|
|
437
|
+
{ id: "jaw-wide", label: "Wide", dimension: "jawline", description: "Wide, broad jaw", promptHint: "a wide broad jaw" },
|
|
438
|
+
{ id: "jaw-double", label: "Double Chin", dimension: "jawline", description: "Visible double chin", promptHint: "a visible double chin" },
|
|
439
|
+
|
|
440
|
+
// -------------------- Cheekbones --------------------
|
|
441
|
+
// Neutral-first: "Average" carries an empty promptHint so enabling the
|
|
442
|
+
// dimension injects nothing until a non-neutral value is picked.
|
|
443
|
+
{ id: "cheekbones-average", label: "Average", dimension: "cheekbones", description: "Average cheekbone projection (no emphasis)", promptHint: "" },
|
|
444
|
+
{ id: "cheekbones-low", label: "Low", dimension: "cheekbones", description: "Low, flat cheekbones", promptHint: "low, flat cheekbones" },
|
|
445
|
+
{ id: "cheekbones-high-defined", label: "High & Defined", dimension: "cheekbones", description: "High, clearly defined cheekbones", promptHint: "high, defined cheekbones" },
|
|
446
|
+
{ id: "cheekbones-sculpted", label: "Sculpted-High", dimension: "cheekbones", description: "Sharply sculpted high cheekbones", promptHint: "sharply sculpted high cheekbones with strong cheekbone projection" },
|
|
447
|
+
{ id: "cheekbones-wide", label: "Wide", dimension: "cheekbones", description: "Wide, prominent cheekbones", promptHint: "wide, prominent cheekbones" },
|
|
448
|
+
|
|
449
|
+
// -------------------- Facial Fullness (face leanness, independent of body Build) --------------------
|
|
450
|
+
{ id: "facial-fullness-average", label: "Average", dimension: "facial-fullness", description: "Average facial fullness", promptHint: "" },
|
|
451
|
+
{ id: "facial-fullness-gaunt", label: "Gaunt", dimension: "facial-fullness", description: "Hollow cheeks, very low facial fat", promptHint: "a gaunt face with hollow cheeks and very low facial fat" },
|
|
452
|
+
{ id: "facial-fullness-lean", label: "Lean", dimension: "facial-fullness", description: "Low facial fat, subtle hollowing under cheekbones", promptHint: "a lean face with low facial fat and subtle hollowing beneath the cheekbones" },
|
|
453
|
+
{ id: "facial-fullness-full", label: "Full", dimension: "facial-fullness", description: "Soft, well-padded cheeks", promptHint: "a full face with soft, well-padded cheeks" },
|
|
454
|
+
{ id: "facial-fullness-round", label: "Round", dimension: "facial-fullness", description: "Round face, soft rounded cheeks", promptHint: "a round face with soft, rounded cheeks" },
|
|
455
|
+
|
|
456
|
+
// -------------------- Eye Shape (anatomical shape only — lid/tilt/spacing/set split out below) --------------------
|
|
457
|
+
{ id: "eye-almond", label: "Almond", dimension: "eye-shape", description: "Almond-shaped eyes", promptHint: "almond-shaped eyes" },
|
|
458
|
+
{ id: "eye-round", label: "Round", dimension: "eye-shape", description: "Wide, round eyes", promptHint: "wide round eyes" },
|
|
459
|
+
{ id: "eye-monolid", label: "Monolid", dimension: "eye-shape", description: "No visible crease, smooth lid", promptHint: "monolid eyes with no visible crease" },
|
|
460
|
+
{ id: "eye-double-eyelid", label: "Double Eyelid", dimension: "eye-shape", description: "Defined upper-eyelid crease", promptHint: "double-eyelid eyes with a clearly defined upper crease" },
|
|
461
|
+
{ id: "eye-wide", label: "Wide", dimension: "eye-shape", description: "Wide, large eyes", promptHint: "wide, large eyes" },
|
|
462
|
+
{ id: "eye-narrow", label: "Narrow", dimension: "eye-shape", description: "Narrow, elongated eyes", promptHint: "narrow, elongated eyes" },
|
|
463
|
+
|
|
464
|
+
// -------------------- Eyelid Type (upper-lid trait) --------------------
|
|
465
|
+
// `eye-hooded` / `eye-droopy` / `eye-deep-set` keep their original ids
|
|
466
|
+
// (moved out of eye-shape) so legacy data still resolves the same hint.
|
|
467
|
+
{ id: "eyelid-standard", label: "Standard", dimension: "eyelid-type", description: "Standard upper eyelid", promptHint: "" },
|
|
468
|
+
{ id: "eye-hooded", label: "Hooded", dimension: "eyelid-type", description: "Upper lid partially covers the crease", promptHint: "hooded eyes with the upper lid partially covering the crease" },
|
|
469
|
+
{ id: "eye-droopy", label: "Droopy", dimension: "eyelid-type", description: "Upper lids sit low — soft, sleepy gaze", promptHint: "droopy eyes with the upper lids sitting low for a soft, sleepy gaze" },
|
|
470
|
+
{ id: "eye-deep-set", label: "Deep-set", dimension: "eyelid-type", description: "Set deeper into the socket", promptHint: "deep-set eyes" },
|
|
471
|
+
|
|
472
|
+
// -------------------- Canthal Tilt (outer-corner angle) --------------------
|
|
473
|
+
{ id: "canthal-neutral", label: "Neutral", dimension: "canthal-tilt", description: "Neutral canthal tilt", promptHint: "" },
|
|
474
|
+
{ id: "eye-upturned", label: "Upturned", dimension: "canthal-tilt", description: "Outer corners lift up (positive tilt)", promptHint: "upturned eyes with the outer corners lifted, positive canthal tilt" },
|
|
475
|
+
{ id: "eye-downturned", label: "Downturned", dimension: "canthal-tilt", description: "Outer corners angle down (negative tilt)", promptHint: "downturned eyes with the outer corners angled down, negative canthal tilt" },
|
|
476
|
+
|
|
477
|
+
// -------------------- Eye Spacing (horizontal distance between eyes) --------------------
|
|
478
|
+
{ id: "eye-spacing-average", label: "Average", dimension: "eye-spacing", description: "Average eye spacing", promptHint: "" },
|
|
479
|
+
{ id: "eye-close-set", label: "Close-set", dimension: "eye-spacing", description: "Eyes set close together", promptHint: "close-set eyes set close together" },
|
|
480
|
+
{ id: "eye-wide-set", label: "Wide-set", dimension: "eye-spacing", description: "Eyes spaced widely apart", promptHint: "wide-set eyes spaced widely apart" },
|
|
481
|
+
|
|
482
|
+
// -------------------- Eye Set / Brow Distance (vertical brow-to-eye gap) --------------------
|
|
483
|
+
{ id: "eyeset-average", label: "Average", dimension: "eye-set-brow", description: "Average brow-to-eye distance", promptHint: "" },
|
|
484
|
+
{ id: "eyeset-low", label: "Low-set", dimension: "eye-set-brow", description: "Little brow-to-eye distance (hunter eyes)", promptHint: "eyes set close beneath the brow with little brow-to-eye distance, hunter eyes" },
|
|
485
|
+
{ id: "eyeset-high", label: "High-set", dimension: "eye-set-brow", description: "Generous brow-to-eye distance (doe eyes)", promptHint: "eyes set well below the brow with generous brow-to-eye distance, doe eyes" },
|
|
486
|
+
|
|
487
|
+
// -------------------- Nose (bridge) --------------------
|
|
488
|
+
{ id: "nose-straight", label: "Straight", dimension: "nose", description: "Straight bridge", promptHint: "a straight nose" },
|
|
489
|
+
{ id: "nose-aquiline", label: "Aquiline", dimension: "nose", description: "Curved hooked bridge", promptHint: "an aquiline nose with a curved bridge" },
|
|
490
|
+
{ id: "nose-roman", label: "Roman", dimension: "nose", description: "Prominent bridge with slight bump", promptHint: "a Roman nose with a prominent bridge" },
|
|
491
|
+
{ id: "nose-snub", label: "Snub", dimension: "nose", description: "Short, slightly upturned", promptHint: "a snub nose, short and slightly upturned" },
|
|
492
|
+
{ id: "nose-button", label: "Button", dimension: "nose", description: "Small, rounded tip", promptHint: "a small button nose with a rounded tip" },
|
|
493
|
+
{ id: "nose-broad", label: "Broad", dimension: "nose", description: "Wide nostrils, broad bridge", promptHint: "a broad nose with wide nostrils" },
|
|
494
|
+
{ id: "nose-narrow", label: "Narrow", dimension: "nose", description: "Thin, narrow", promptHint: "a narrow thin nose" },
|
|
495
|
+
{ id: "nose-hooked", label: "Hooked", dimension: "nose", description: "Strongly curved hooked tip", promptHint: "a hooked nose with a strongly curved tip" },
|
|
496
|
+
|
|
497
|
+
// -------------------- Nose Tip (rotation / shape of the tip) --------------------
|
|
498
|
+
{ id: "nose-tip-natural", label: "Natural", dimension: "nose-tip", description: "Natural nose tip (no emphasis)", promptHint: "" },
|
|
499
|
+
{ id: "nose-tip-refined", label: "Refined", dimension: "nose-tip", description: "Refined, narrow tip", promptHint: "a refined, narrow nose tip" },
|
|
500
|
+
{ id: "nose-tip-upturned", label: "Upturned", dimension: "nose-tip", description: "Slightly upturned tip", promptHint: "a slightly upturned nose tip" },
|
|
501
|
+
{ id: "nose-tip-rounded", label: "Rounded", dimension: "nose-tip", description: "Soft, rounded tip", promptHint: "a soft, rounded nose tip" },
|
|
502
|
+
{ id: "nose-tip-drooping", label: "Drooping", dimension: "nose-tip", description: "Slightly drooping, downward tip", promptHint: "a slightly drooping, downward-pointing nose tip" },
|
|
503
|
+
|
|
504
|
+
// -------------------- Lip Fullness --------------------
|
|
505
|
+
// `lips-thin` / `lips-medium` / `lips-full` keep their original ids (moved
|
|
506
|
+
// out of the old combined `lips` dimension) so legacy data resolves the same.
|
|
507
|
+
{ id: "lips-thin", label: "Thin", dimension: "lip-fullness", description: "Thin lips", promptHint: "thin lips" },
|
|
508
|
+
{ id: "lips-medium", label: "Medium", dimension: "lip-fullness", description: "Average fullness", promptHint: "medium lips of average fullness" },
|
|
509
|
+
{ id: "lips-full", label: "Full", dimension: "lip-fullness", description: "Full plump lips", promptHint: "full plump lips" },
|
|
510
|
+
{ id: "lips-full-lower", label: "Full Lower Lip", dimension: "lip-fullness", description: "Full lips with a fuller lower lip", promptHint: "full lips with a noticeably fuller lower lip" },
|
|
511
|
+
|
|
512
|
+
// -------------------- Lip Shape --------------------
|
|
513
|
+
{ id: "lips-natural", label: "Natural", dimension: "lip-shape", description: "Natural lip shape", promptHint: "" },
|
|
514
|
+
{ id: "lips-cupids-bow", label: "Cupid's Bow", dimension: "lip-shape", description: "Pronounced cupid's bow on the upper lip", promptHint: "a pronounced cupid's bow on the upper lip" },
|
|
515
|
+
{ id: "lips-wide", label: "Wide", dimension: "lip-shape", description: "Wide mouth shape", promptHint: "a wide mouth" },
|
|
516
|
+
{ id: "lips-heart", label: "Heart", dimension: "lip-shape", description: "Heart-shaped lips with a defined cupid's bow", promptHint: "heart-shaped lips with a defined cupid's bow" },
|
|
517
|
+
{ id: "lips-small", label: "Small", dimension: "lip-shape", description: "Petite mouth", promptHint: "a small petite mouth" },
|
|
518
|
+
|
|
519
|
+
// -------------------- Lip State (what the lips are doing / wearing) --------------------
|
|
520
|
+
{ id: "lip-state-chapped", label: "Chapped", dimension: "lip-state", description: "Cracked, dry, weather-worn lips", promptHint: "with chapped, cracked, weather-worn dry lips" },
|
|
521
|
+
{ id: "lip-state-glossy", label: "Glossy", dimension: "lip-state", description: "High-shine, wet-look lips", promptHint: "with high-shine glossy wet-look lips" },
|
|
522
|
+
{ id: "lip-state-bare", label: "Bare", dimension: "lip-state", description: "Natural, untreated, no makeup", promptHint: "with bare, natural, untreated lips" },
|
|
523
|
+
{ id: "lip-state-bold-red", label: "Bold Red", dimension: "lip-state", description: "Saturated red lipstick statement", promptHint: "with a bold, saturated red lipstick statement" },
|
|
524
|
+
{ id: "lip-state-bitten", label: "Bitten", dimension: "lip-state", description: "Slight playful lip-bite, lower lip caught", promptHint: "playfully biting the lower lip" },
|
|
525
|
+
{ id: "lip-state-parted", label: "Parted", dimension: "lip-state", description: "Lips slightly parted, breath of air", promptHint: "with lips slightly parted, taking a soft breath" },
|
|
526
|
+
{ id: "lip-state-pursed", label: "Pursed", dimension: "lip-state", description: "Lips pressed and pushed forward", promptHint: "with lips pursed, pressed and pushed forward" },
|
|
527
|
+
{ id: "lip-state-bold-black", label: "Bold Black", dimension: "lip-state", description: "Saturated black lipstick statement (goth / avant-garde)", promptHint: "with a bold, saturated black lipstick statement, goth / avant-garde" },
|
|
528
|
+
{ id: "lip-state-burgundy", label: "Burgundy", dimension: "lip-state", description: "Deep wine-red lipstick", promptHint: "with deep wine-red burgundy lipstick" },
|
|
529
|
+
{ id: "lip-state-plum", label: "Plum", dimension: "lip-state", description: "Dark purple-toned lipstick", promptHint: "with dark purple-toned plum lipstick" },
|
|
530
|
+
{ id: "lip-state-mauve", label: "Mauve", dimension: "lip-state", description: "Muted mauve / dusty-pink lipstick", promptHint: "with muted mauve, dusty-pink lipstick" },
|
|
531
|
+
{ id: "lip-state-pouting", label: "Pouting", dimension: "lip-state", description: "Full pout", promptHint: "with a full pouting expression" },
|
|
532
|
+
|
|
533
|
+
// -------------------- Hair Color --------------------
|
|
534
|
+
// Blonde family (light → warm → cool)
|
|
535
|
+
{ id: "hair-platinum", label: "Platinum", dimension: "hair-color", description: "Very pale, icy platinum", promptHint: "platinum blonde hair, very pale and icy" },
|
|
536
|
+
{ id: "hair-creamy", label: "Creamy", dimension: "hair-color", description: "Warm creamy / vanilla blonde", promptHint: "creamy vanilla blonde hair" },
|
|
537
|
+
{ id: "hair-blonde", label: "Blonde", dimension: "hair-color", description: "Classic blonde hair", promptHint: "blonde hair" },
|
|
538
|
+
{ id: "hair-honey", label: "Honey", dimension: "hair-color", description: "Warm honey-gold blonde", promptHint: "honey-gold blonde hair with warm golden tones" },
|
|
539
|
+
{ id: "hair-strawberry", label: "Strawberry Blonde", dimension: "hair-color", description: "Blonde with pinkish-red tone", promptHint: "strawberry blonde hair with a pinkish-red tint" },
|
|
540
|
+
{ id: "hair-ash-blonde", label: "Ash Blonde", dimension: "hair-color", description: "Cool, ashy blonde", promptHint: "ash blonde hair with cool, muted tones" },
|
|
541
|
+
// Red / warm family
|
|
542
|
+
{ id: "hair-ginger", label: "Ginger", dimension: "hair-color", description: "Bright ginger (reddish-orange)", promptHint: "bright ginger hair, reddish-orange" },
|
|
543
|
+
{ id: "hair-copper", label: "Copper", dimension: "hair-color", description: "Vibrant copper orange-red", promptHint: "vibrant copper hair with orange-red metallic shine" },
|
|
544
|
+
{ id: "hair-red", label: "Red", dimension: "hair-color", description: "Classic red hair", promptHint: "red hair" },
|
|
545
|
+
{ id: "hair-auburn", label: "Auburn", dimension: "hair-color", description: "Rich reddish-brown", promptHint: "auburn hair, rich reddish-brown" },
|
|
546
|
+
{ id: "hair-burgundy", label: "Burgundy", dimension: "hair-color", description: "Dark wine-red", promptHint: "burgundy hair, deep wine-red" },
|
|
547
|
+
// Brown family (light → dark)
|
|
548
|
+
{ id: "hair-light-brown", label: "Light Brown", dimension: "hair-color", description: "Light brown hair", promptHint: "light brown hair" },
|
|
549
|
+
{ id: "hair-caramel", label: "Caramel", dimension: "hair-color", description: "Warm caramel brown", promptHint: "caramel brown hair with warm golden undertones" },
|
|
550
|
+
{ id: "hair-brown", label: "Brown", dimension: "hair-color", description: "Medium brown hair", promptHint: "brown hair" },
|
|
551
|
+
{ id: "hair-chestnut", label: "Chestnut", dimension: "hair-color", description: "Warm reddish-brown chestnut", promptHint: "chestnut brown hair with warm reddish undertones" },
|
|
552
|
+
{ id: "hair-chocolate", label: "Chocolate", dimension: "hair-color", description: "Rich dark chocolate brown", promptHint: "rich chocolate brown hair" },
|
|
553
|
+
{ id: "hair-dark-brown", label: "Dark Brown", dimension: "hair-color", description: "Dark brown hair", promptHint: "dark brown hair" },
|
|
554
|
+
// Black family
|
|
555
|
+
{ id: "hair-black", label: "Black", dimension: "hair-color", description: "Classic black hair", promptHint: "black hair" },
|
|
556
|
+
{ id: "hair-jet-black", label: "Jet Black", dimension: "hair-color", description: "Deep black with blue undertones", promptHint: "jet black hair with deep blue-black undertones and glossy shine" },
|
|
557
|
+
// Gray / neutral
|
|
558
|
+
{ id: "hair-gray", label: "Gray", dimension: "hair-color", description: "Gray hair", promptHint: "gray hair" },
|
|
559
|
+
{ id: "hair-salt-pepper", label: "Salt & Pepper", dimension: "hair-color", description: "Mixed black and gray hair", promptHint: "salt-and-pepper hair" },
|
|
560
|
+
{ id: "hair-white", label: "White", dimension: "hair-color", description: "White hair", promptHint: "white hair" },
|
|
561
|
+
// Silver / Metallic
|
|
562
|
+
{ id: "hair-silver", label: "Silver", dimension: "hair-color", description: "Bright metallic silver", promptHint: "bright metallic silver hair with a clean reflective sheen" },
|
|
563
|
+
{ id: "hair-rose-gold", label: "Rose Gold", dimension: "hair-color", description: "Soft pink-gold metallic", promptHint: "rose gold hair, soft pink-gold metallic warmth" },
|
|
564
|
+
// Dyed (catch-all + specific fantasy / cosplay colors)
|
|
565
|
+
{ id: "hair-dyed", label: "Colorful (any)", dimension: "hair-color", description: "Dyed vibrant colors — unspecified", promptHint: "vibrantly dyed colorful hair" },
|
|
566
|
+
{ id: "hair-blue", label: "Blue", dimension: "hair-color", description: "Bold blue dyed hair", promptHint: "bold blue dyed hair" },
|
|
567
|
+
{ id: "hair-pastel-blue", label: "Pastel Blue", dimension: "hair-color", description: "Soft pastel sky-blue", promptHint: "soft pastel sky-blue hair" },
|
|
568
|
+
{ id: "hair-teal", label: "Teal", dimension: "hair-color", description: "Saturated teal blue-green", promptHint: "saturated teal blue-green hair" },
|
|
569
|
+
{ id: "hair-mint", label: "Mint", dimension: "hair-color", description: "Pale mint green", promptHint: "pale mint-green hair" },
|
|
570
|
+
{ id: "hair-green", label: "Green", dimension: "hair-color", description: "Bold green dyed hair", promptHint: "bold green dyed hair" },
|
|
571
|
+
{ id: "hair-lavender", label: "Lavender", dimension: "hair-color", description: "Soft lavender violet", promptHint: "soft lavender violet hair" },
|
|
572
|
+
{ id: "hair-purple", label: "Purple", dimension: "hair-color", description: "Bold deep purple", promptHint: "bold deep purple dyed hair" },
|
|
573
|
+
{ id: "hair-magenta", label: "Magenta", dimension: "hair-color", description: "Vivid magenta pink-purple", promptHint: "vivid magenta pink-purple hair" },
|
|
574
|
+
{ id: "hair-pink", label: "Pink", dimension: "hair-color", description: "Bold pink dyed hair", promptHint: "bold pink dyed hair" },
|
|
575
|
+
{ id: "hair-pastel-pink", label: "Pastel Pink", dimension: "hair-color", description: "Soft pastel cotton-candy pink", promptHint: "soft pastel cotton-candy pink hair" },
|
|
576
|
+
{ id: "hair-peach", label: "Peach", dimension: "hair-color", description: "Warm peach blonde-pink", promptHint: "warm peach hair, blonde-pink with soft warmth" },
|
|
577
|
+
{ id: "hair-mermaid", label: "Mermaid", dimension: "hair-color", description: "Blue-green-purple ocean blend", promptHint: "mermaid hair blending blue, green, and purple in flowing waves" },
|
|
578
|
+
{ id: "hair-rainbow", label: "Rainbow", dimension: "hair-color", description: "Multi-color rainbow streaks", promptHint: "rainbow hair with streaks of multiple bright dyed colors" },
|
|
579
|
+
|
|
580
|
+
// -------------------- Hair Style (length + texture) --------------------
|
|
581
|
+
// -------------------- Hair Base (natural texture + length) --------------------
|
|
582
|
+
// Cut/styling choices live in Styling.hair-cut (separate dimension); this is
|
|
583
|
+
// about what hair the character has, not what was done to it today.
|
|
584
|
+
{ id: "base-bald", label: "Bald / Shaved", dimension: "hair-base", description: "Bald or fully shaved head", promptHint: "a shaved bald head" },
|
|
585
|
+
{ id: "base-buzz", label: "Buzz", dimension: "hair-base", description: "Very short cropped hair", promptHint: "very short buzzed hair" },
|
|
586
|
+
{ id: "base-short-straight", label: "Short Straight", dimension: "hair-base", description: "Short, straight texture", promptHint: "short straight hair" },
|
|
587
|
+
{ id: "base-short-wavy", label: "Short Wavy", dimension: "hair-base", description: "Short, wavy texture", promptHint: "short wavy hair" },
|
|
588
|
+
{ id: "base-short-curly", label: "Short Curly", dimension: "hair-base", description: "Short, curly texture", promptHint: "short curly hair" },
|
|
589
|
+
{ id: "base-short-coily", label: "Short Coily", dimension: "hair-base", description: "Short, tightly coiled texture", promptHint: "short tightly coiled hair" },
|
|
590
|
+
{ id: "base-medium-straight", label: "Medium Straight", dimension: "hair-base", description: "Shoulder-length, straight", promptHint: "medium-length straight hair" },
|
|
591
|
+
{ id: "base-medium-wavy", label: "Medium Wavy", dimension: "hair-base", description: "Shoulder-length, wavy", promptHint: "medium-length wavy hair" },
|
|
592
|
+
{ id: "base-medium-curly", label: "Medium Curly", dimension: "hair-base", description: "Shoulder-length, curly", promptHint: "medium-length curly hair" },
|
|
593
|
+
{ id: "base-medium-coily", label: "Medium Coily", dimension: "hair-base", description: "Shoulder-length, tightly coiled", promptHint: "medium-length tightly coiled hair" },
|
|
594
|
+
{ id: "base-long-straight", label: "Long Straight", dimension: "hair-base", description: "Mid-back, straight", promptHint: "long straight hair" },
|
|
595
|
+
{ id: "base-long-wavy", label: "Long Wavy", dimension: "hair-base", description: "Mid-back, wavy", promptHint: "long wavy hair" },
|
|
596
|
+
{ id: "base-long-curly", label: "Long Curly", dimension: "hair-base", description: "Mid-back, curly", promptHint: "long curly hair" },
|
|
597
|
+
{ id: "base-long-coily", label: "Long Coily", dimension: "hair-base", description: "Mid-back, tightly coiled", promptHint: "long tightly coiled hair" },
|
|
598
|
+
{ id: "base-very-long-straight", label: "Very Long Straight", dimension: "hair-base", description: "Waist-length, straight", promptHint: "very long straight hair down to the waist" },
|
|
599
|
+
{ id: "base-very-long-wavy", label: "Very Long Wavy", dimension: "hair-base", description: "Waist-length, wavy", promptHint: "very long wavy hair down to the waist" },
|
|
600
|
+
{ id: "base-very-long-curly", label: "Very Long Curly", dimension: "hair-base", description: "Waist-length, curly", promptHint: "very long curly hair down to the waist" },
|
|
601
|
+
{ id: "base-afro", label: "Afro", dimension: "hair-base", description: "Voluminous rounded afro", promptHint: "voluminous afro hair" },
|
|
602
|
+
{ id: "base-thinning", label: "Thinning / Receding", dimension: "hair-base", description: "Visibly thinning or receding hairline", promptHint: "thinning hair with a receding hairline" },
|
|
603
|
+
{ id: "base-wet", label: "Wet", dimension: "hair-base", description: "Wet, slick hair", promptHint: "wet, slick hair" },
|
|
604
|
+
|
|
605
|
+
// -------------------- Eyebrows --------------------
|
|
606
|
+
{ id: "brows-natural", label: "Natural", dimension: "eyebrows", description: "Natural, untouched brows", promptHint: "natural eyebrows" },
|
|
607
|
+
{ id: "brows-thick", label: "Thick / Bushy", dimension: "eyebrows", description: "Thick, bushy brows", promptHint: "thick bushy eyebrows" },
|
|
608
|
+
{ id: "brows-thin", label: "Thin / Pencil", dimension: "eyebrows", description: "Thin, pencil-shaped brows", promptHint: "thin pencil-shaped eyebrows" },
|
|
609
|
+
{ id: "brows-arched", label: "Arched", dimension: "eyebrows", description: "Strongly arched, dramatic curve", promptHint: "strongly arched eyebrows with a dramatic curve" },
|
|
610
|
+
{ id: "brows-straight", label: "Straight", dimension: "eyebrows", description: "Flat, horizontal brows", promptHint: "flat straight eyebrows" },
|
|
611
|
+
{ id: "brows-bold", label: "Bold", dimension: "eyebrows", description: "Defined, dark, full brows", promptHint: "bold defined dark full eyebrows" },
|
|
612
|
+
{ id: "brows-soft", label: "Soft / Faded", dimension: "eyebrows", description: "Soft, light, faded brows", promptHint: "soft, light, faded eyebrows" },
|
|
613
|
+
{ id: "brows-microbladed", label: "Microbladed", dimension: "eyebrows", description: "Drawn / microbladed crisp shape", promptHint: "microbladed crisply drawn eyebrows" },
|
|
614
|
+
{ id: "brows-tinted", label: "Tinted", dimension: "eyebrows", description: "Tinted a different color than hair", promptHint: "eyebrows tinted a different color from the hair" },
|
|
615
|
+
{ id: "brows-unibrow", label: "Unibrow", dimension: "eyebrows", description: "Connected unibrow", promptHint: "a connected unibrow" },
|
|
616
|
+
|
|
617
|
+
// -------------------- Skin Tone --------------------
|
|
618
|
+
{ id: "skin-very-fair", label: "Very Fair", dimension: "skin-tone", description: "Very fair / pale skin", promptHint: "very fair pale skin" },
|
|
619
|
+
{ id: "skin-fair", label: "Fair", dimension: "skin-tone", description: "Fair skin", promptHint: "fair skin" },
|
|
620
|
+
{ id: "skin-medium", label: "Medium", dimension: "skin-tone", description: "Medium skin tone", promptHint: "medium skin tone" },
|
|
621
|
+
{ id: "skin-olive", label: "Olive", dimension: "skin-tone", description: "Olive skin", promptHint: "olive skin" },
|
|
622
|
+
{ id: "skin-tan", label: "Tan", dimension: "skin-tone", description: "Tan skin", promptHint: "tan skin" },
|
|
623
|
+
{ id: "skin-brown", label: "Brown", dimension: "skin-tone", description: "Brown skin", promptHint: "brown skin" },
|
|
624
|
+
{ id: "skin-dark", label: "Dark", dimension: "skin-tone", description: "Dark skin", promptHint: "dark skin" },
|
|
625
|
+
{ id: "skin-very-dark", label: "Very Dark / Ebony", dimension: "skin-tone", description: "Very dark, ebony skin tone (deepest end of the scale)", promptHint: "very dark ebony skin, the deepest skin tone" },
|
|
626
|
+
|
|
627
|
+
// -------------------- Eye Color --------------------
|
|
628
|
+
{ id: "eyes-brown", label: "Brown", dimension: "eye-color", description: "Brown eyes", promptHint: "brown eyes" },
|
|
629
|
+
{ id: "eyes-blue", label: "Blue", dimension: "eye-color", description: "Blue eyes", promptHint: "blue eyes" },
|
|
630
|
+
{ id: "eyes-green", label: "Green", dimension: "eye-color", description: "Green eyes", promptHint: "green eyes" },
|
|
631
|
+
{ id: "eyes-hazel", label: "Hazel", dimension: "eye-color", description: "Hazel eyes", promptHint: "hazel eyes" },
|
|
632
|
+
{ id: "eyes-gray", label: "Gray", dimension: "eye-color", description: "Gray eyes", promptHint: "gray eyes" },
|
|
633
|
+
{ id: "eyes-amber", label: "Amber", dimension: "eye-color", description: "Amber eyes", promptHint: "amber eyes" },
|
|
634
|
+
{ id: "eyes-violet", label: "Violet", dimension: "eye-color", description: "Rare violet eyes (Liz Taylor)", promptHint: "rare violet eyes with a deep purple iris" },
|
|
635
|
+
{ id: "eyes-gold", label: "Gold", dimension: "eye-color", description: "Bright gold iris", promptHint: "bright gold eyes with luminous metallic warmth" },
|
|
636
|
+
{ id: "eyes-silver", label: "Silver", dimension: "eye-color", description: "Pale silver-gray iris", promptHint: "pale silver eyes with a luminous reflective sheen" },
|
|
637
|
+
{ id: "eyes-red", label: "Red", dimension: "eye-color", description: "Red iris (albinism / fantasy)", promptHint: "red eyes, albinism or fantasy red iris" },
|
|
638
|
+
{ id: "eyes-pink", label: "Pink", dimension: "eye-color", description: "Soft pink iris (albino / fantasy)", promptHint: "soft pink eyes, albino or fantasy pink iris" },
|
|
639
|
+
{ id: "eyes-turquoise", label: "Turquoise", dimension: "eye-color", description: "Vivid turquoise blue-green iris", promptHint: "vivid turquoise blue-green eyes" },
|
|
640
|
+
{ id: "eyes-black", label: "Black", dimension: "eye-color", description: "Truly black iris (deeper than brown)", promptHint: "jet-black eyes with a truly black iris, deeper than brown" },
|
|
641
|
+
|
|
642
|
+
// -------------------- Eye State (what the eyes are doing / where they look) --------------------
|
|
643
|
+
{ id: "eye-state-closed", label: "Closed", dimension: "eye-state", description: "Eyes fully closed, peaceful", promptHint: "with eyes fully closed in a peaceful expression" },
|
|
644
|
+
{ id: "eye-state-half-lidded", label: "Half-lidded", dimension: "eye-state", description: "Heavy-lidded sleepy gaze", promptHint: "with heavy half-lidded sleepy eyes" },
|
|
645
|
+
{ id: "eye-state-wide-eyed", label: "Wide-eyed", dimension: "eye-state", description: "Eyes wide open, alert / surprised", promptHint: "with eyes wide open, alert and surprised" },
|
|
646
|
+
{ id: "eye-state-staring-camera", label: "Staring at Camera", dimension: "eye-state", description: "Direct unbroken eye contact with the lens", promptHint: "staring directly at the camera with unbroken eye contact" },
|
|
647
|
+
{ id: "eye-state-gazing-away", label: "Gazing Away", dimension: "eye-state", description: "Looking off-camera, contemplative", promptHint: "gazing off-camera with a contemplative expression" },
|
|
648
|
+
{ id: "eye-state-gazing-up", label: "Gazing Up", dimension: "eye-state", description: "Eyes raised toward something above", promptHint: "with eyes gazing upward toward something above" },
|
|
649
|
+
{ id: "eye-state-gazing-down", label: "Gazing Down", dimension: "eye-state", description: "Eyes downcast", promptHint: "with eyes downcast, gazing softly downward" },
|
|
650
|
+
{ id: "eye-state-glassy", label: "Glassy", dimension: "eye-state", description: "Eyes wet / tear-glazed", promptHint: "with glassy, tear-glazed wet eyes" },
|
|
651
|
+
|
|
652
|
+
// -------------------- Facial Hair --------------------
|
|
653
|
+
{ id: "face-clean-shaven", label: "Clean-shaven", dimension: "facial-hair", description: "Clean-shaven face", promptHint: "clean-shaven" },
|
|
654
|
+
{ id: "face-stubble", label: "Stubble", dimension: "facial-hair", description: "Light stubble", promptHint: "light stubble" },
|
|
655
|
+
{ id: "face-mustache", label: "Mustache", dimension: "facial-hair", description: "Mustache", promptHint: "a mustache" },
|
|
656
|
+
{ id: "face-goatee", label: "Goatee", dimension: "facial-hair", description: "Goatee", promptHint: "a goatee" },
|
|
657
|
+
{ id: "face-short-beard", label: "Short Beard", dimension: "facial-hair", description: "Short trimmed beard", promptHint: "a short trimmed beard" },
|
|
658
|
+
{ id: "face-full-beard", label: "Full Beard", dimension: "facial-hair", description: "Thick full beard", promptHint: "a full beard" },
|
|
659
|
+
{ id: "face-five-oclock-shadow", label: "Five-O'Clock Shadow", dimension: "facial-hair", description: "Heavier daily growth — denser than light stubble", promptHint: "a five-o'clock shadow, daily growth heavier than light stubble" },
|
|
660
|
+
|
|
661
|
+
// -------------------- Skin Texture --------------------
|
|
662
|
+
{ id: "texture-smooth", label: "Smooth", dimension: "skin-texture", description: "Flawless, silky smooth skin", promptHint: "with flawless, silky smooth skin" },
|
|
663
|
+
{ id: "texture-wrinkled", label: "Wrinkled", dimension: "skin-texture", description: "Aged, deeply lined skin", promptHint: "with deep wrinkles and aged skin texture" },
|
|
664
|
+
{ id: "texture-goosebumps", label: "Goosebumps", dimension: "skin-texture", description: "Raised goosebumps on skin", promptHint: "with goosebumps raised on the skin" },
|
|
665
|
+
{ id: "texture-dewy", label: "Dewy", dimension: "skin-texture", description: "Glowing, dewy fresh skin", promptHint: "with dewy, glowing skin and a fresh sheen" },
|
|
666
|
+
{ id: "texture-glistening", label: "Glistening", dimension: "skin-texture", description: "Sweat or oil sheen", promptHint: "with glistening skin, sweat or oil catching the light" },
|
|
667
|
+
{ id: "texture-weathered", label: "Weathered", dimension: "skin-texture", description: "Sun-aged rough skin", promptHint: "with weathered, sun-worn rough skin" },
|
|
668
|
+
{ id: "texture-porcelain", label: "Porcelain", dimension: "skin-texture", description: "Flawless near-translucent porcelain", promptHint: "with flawless, near-translucent porcelain skin" },
|
|
669
|
+
{ id: "texture-sun-kissed", label: "Sun-kissed", dimension: "skin-texture", description: "Warm tan with healthy glow", promptHint: "with sun-kissed skin, warmly tanned with a healthy glow" },
|
|
670
|
+
{ id: "texture-tanned", label: "Tanned", dimension: "skin-texture", description: "Deeply, evenly tanned skin", promptHint: "with deeply, evenly bronzed tanned skin" },
|
|
671
|
+
{ id: "texture-tan-lines", label: "Tan Lines", dimension: "skin-texture", description: "Visible tan lines from swimwear", promptHint: "with visible swimwear tan lines on the skin" },
|
|
672
|
+
{ id: "texture-freckled", label: "Freckled", dimension: "skin-texture", description: "Freckles across cheeks and nose", promptHint: "with visible freckles scattered across cheeks and the bridge of the nose" },
|
|
673
|
+
{ id: "texture-ruddy", label: "Ruddy", dimension: "skin-texture", description: "Pink-flushed cheeks and nose", promptHint: "with ruddy, warm pink-flushed cheeks and nose" },
|
|
674
|
+
{ id: "texture-ashen", label: "Ashen / Pale", dimension: "skin-texture", description: "Cool pale, near-bloodless undertone", promptHint: "with ashen, cool pale, near-bloodless skin" },
|
|
675
|
+
{ id: "texture-oily", label: "Oily / Shiny", dimension: "skin-texture", description: "Slight oil sheen on T-zone", promptHint: "with a slight oily sheen across the T-zone" },
|
|
676
|
+
{ id: "texture-matte", label: "Matte", dimension: "skin-texture", description: "Poreless matte finish", promptHint: "with a poreless, matte skin finish" },
|
|
677
|
+
{ id: "texture-blemished", label: "Blemished", dimension: "skin-texture", description: "Visible blemishes, real-skin imperfections", promptHint: "with visible blemishes and natural real-skin imperfections" },
|
|
678
|
+
{ id: "texture-baby-soft", label: "Baby-soft", dimension: "skin-texture", description: "Smooth, fine-pored youthful skin", promptHint: "with baby-soft, fine-pored, youthful smooth skin" },
|
|
679
|
+
{ id: "texture-shower-fresh-wet", label: "Shower-Fresh Wet", dimension: "skin-texture", description: "Just-out-of-shower wet skin with water beads", promptHint: "with just-out-of-the-shower wet skin, water beading on the surface and rolling in slow droplets down the curves of the body" },
|
|
680
|
+
{ id: "texture-acne-scarred", label: "Acne-scarred", dimension: "skin-texture", description: "Visible acne scarring (distinct from blemished — healed scar pattern)", promptHint: "with visible acne scarring, healed pitted-skin texture and uneven surface from past breakouts" },
|
|
681
|
+
|
|
682
|
+
// -------------------- Distinctive Features --------------------
|
|
683
|
+
{ id: "feature-glasses", label: "Glasses", dimension: "distinctive-features", description: "Wears glasses", promptHint: "wearing glasses" },
|
|
684
|
+
{ id: "feature-freckles", label: "Freckles", dimension: "distinctive-features", description: "Visible freckles", promptHint: "with freckles" },
|
|
685
|
+
{ id: "feature-tattoos", label: "Tattoos", dimension: "distinctive-features", description: "Visible tattoos", promptHint: "with visible tattoos" },
|
|
686
|
+
{ id: "feature-scar", label: "Scar", dimension: "distinctive-features", description: "Facial scar", promptHint: "with a facial scar" },
|
|
687
|
+
{ id: "feature-dimples", label: "Dimples", dimension: "distinctive-features", description: "Dimpled cheeks", promptHint: "with dimples" },
|
|
688
|
+
{ id: "feature-piercing", label: "Piercing", dimension: "distinctive-features", description: "Visible piercing", promptHint: "with a visible piercing" },
|
|
689
|
+
{ id: "feature-birthmark", label: "Birthmark", dimension: "distinctive-features", description: "Visible birthmark on face or shoulder", promptHint: "with a visible birthmark on the face or shoulder" },
|
|
690
|
+
{ id: "feature-mole", label: "Mole / Beauty Mark", dimension: "distinctive-features", description: "One prominent mole, often near upper lip", promptHint: "with a prominent beauty mark mole near the upper lip" },
|
|
691
|
+
{ id: "feature-gap-teeth", label: "Gap Teeth", dimension: "distinctive-features", description: "Gap between front teeth", promptHint: "with a gap between the front teeth" },
|
|
692
|
+
{ id: "feature-aegyo-sal", label: "Aegyo Sal", dimension: "distinctive-features", description: "Puffy under-eye fold prized in K-beauty", promptHint: "with aegyo sal, the soft puffy under-eye fold" },
|
|
693
|
+
{ id: "feature-eye-bags", label: "Eye Bags", dimension: "distinctive-features", description: "Mild dark under-eye circles", promptHint: "with mild dark circles and under-eye bags" },
|
|
694
|
+
{ id: "feature-dimpled-chin", label: "Dimpled Chin", dimension: "distinctive-features", description: "Soft dimple in the chin", promptHint: "with a soft dimple in the chin" },
|
|
695
|
+
{ id: "feature-cleft-chin", label: "Cleft Chin", dimension: "distinctive-features", description: "Defined vertical chin cleft", promptHint: "with a defined vertical cleft chin" },
|
|
696
|
+
{ id: "feature-heterochromia", label: "Heterochromia", dimension: "distinctive-features", description: "Each iris a different color", promptHint: "with heterochromia, each iris a different color" },
|
|
697
|
+
{ id: "feature-vitiligo", label: "Vitiligo", dimension: "distinctive-features", description: "Patches of depigmented skin", promptHint: "with vitiligo, visible patches of depigmented skin" },
|
|
698
|
+
{ id: "feature-sleeve-tattoo", label: "Sleeve Tattoo", dimension: "distinctive-features", description: "Full one-arm tattoo sleeve", promptHint: "with a full tattoo sleeve covering one arm in intricate ink" },
|
|
699
|
+
{ id: "feature-face-tattoo", label: "Face Tattoo", dimension: "distinctive-features", description: "Small face tattoo near eye or cheekbone", promptHint: "with a small face tattoo near the eye or cheekbone" },
|
|
700
|
+
{ id: "feature-hand-tattoos", label: "Hand Tattoos", dimension: "distinctive-features", description: "Knuckle / finger tattoos", promptHint: "with hand tattoos across knuckles and fingers" },
|
|
701
|
+
{ id: "feature-back-tattoo", label: "Back Tattoo", dimension: "distinctive-features", description: "Large piece across the back", promptHint: "with a large back-piece tattoo across the upper back" },
|
|
702
|
+
{ id: "feature-chest-tattoo", label: "Chest Tattoo", dimension: "distinctive-features", description: "Tattoo across chest / décolletage", promptHint: "with a tattoo across the chest and décolletage" },
|
|
703
|
+
{ id: "feature-leg-tattoo", label: "Leg Tattoo", dimension: "distinctive-features", description: "Visible tattoo on leg / calf / thigh", promptHint: "with a visible tattoo on the leg, running across the calf or thigh" },
|
|
704
|
+
{ id: "feature-forearm-tattoo", label: "Forearm Tattoo", dimension: "distinctive-features", description: "Tattoo on inner or outer forearm", promptHint: "with a tattoo on the forearm, visible on the inner or outer side" },
|
|
705
|
+
{ id: "feature-visible-piercings", label: "Visible Piercings", dimension: "distinctive-features", description: "Multiple visible piercings (septum, nose, lip, multi-ear)", promptHint: "with multiple visible piercings — septum, nose, lip, and multi-ear" },
|
|
706
|
+
{ id: "feature-ear-piercings", label: "Ear Piercings", dimension: "distinctive-features", description: "Multiple stacked ear piercings (cartilage, helix, conch)", promptHint: "with multiple stacked ear piercings — cartilage, helix, and conch" },
|
|
707
|
+
{ id: "feature-lip-piercing", label: "Lip Piercing", dimension: "distinctive-features", description: "Lip ring / labret stud", promptHint: "with a lip piercing, a small ring or labret stud at the lip" },
|
|
708
|
+
{ id: "feature-nostril-piercing", label: "Nostril Piercing", dimension: "distinctive-features", description: "Single nostril stud or ring", promptHint: "with a single nostril piercing, a small stud or ring at the nostril" },
|
|
709
|
+
{ id: "feature-bare-shoulders", label: "Bare Shoulders", dimension: "distinctive-features", description: "Bare shoulders exposed", promptHint: "with bare shoulders exposed, the line of the collarbone and shoulder muscles uncovered" },
|
|
710
|
+
{ id: "feature-collarbone-visible", label: "Collarbone Visible", dimension: "distinctive-features", description: "Prominent collarbone catching light", promptHint: "with a prominent collarbone clearly defined and catching the light" },
|
|
711
|
+
{ id: "feature-midriff-visible", label: "Midriff Visible", dimension: "distinctive-features", description: "Exposed midriff between top and bottom", promptHint: "with the midriff exposed, a strip of bare stomach visible between the top and the bottom" },
|
|
712
|
+
{ id: "feature-navel-visible", label: "Navel Visible", dimension: "distinctive-features", description: "Visible navel on bare stomach", promptHint: "with a visible navel on a bare stomach" },
|
|
713
|
+
{ id: "feature-elongated-neck", label: "Elongated Neck", dimension: "distinctive-features", description: "Long swan-like neck", promptHint: "with an elongated, swan-like neck, long and gracefully extended" },
|
|
714
|
+
{ id: "feature-under-eye-circles", label: "Under-Eye Circles", dimension: "distinctive-features", description: "Subtle dark circles under the eyes (distinct from puffy eye-bags)", promptHint: "with subtle dark circles under the eyes" },
|
|
715
|
+
{ id: "feature-fangs", label: "Fangs", dimension: "distinctive-features", description: "Visible fangs (vampire / character archetype)", promptHint: "with visible fangs at the canines, character / vampire archetype" },
|
|
716
|
+
|
|
717
|
+
// -------------------- Regional Aesthetic --------------------
|
|
718
|
+
// Vibe layer (composes with Ethnicity, Skin Tone, Hair, Styling — never
|
|
719
|
+
// hard-codes them). Sub-grouped via `group` so the picker can render
|
|
720
|
+
// collapsible sections by region. Multi-pick supported (up to 2).
|
|
721
|
+
//
|
|
722
|
+
// promptHint invariant: VIBE-ONLY. Do not mention skin tone, hair color,
|
|
723
|
+
// hair style, or specific wardrobe — those compete with the user's
|
|
724
|
+
// explicit picks in other dimensions.
|
|
725
|
+
//
|
|
726
|
+
// Sub-Saharan African entries use 🇬🇧 / 🇫🇷 / 🇵🇹 in the description to
|
|
727
|
+
// call out the dominant colonial-cultural overlay (Anglophone /
|
|
728
|
+
// Francophone / Lusophone) — this drives sartorial + media-diet
|
|
729
|
+
// differences. Ethiopia is intentionally untagged (never colonized).
|
|
730
|
+
|
|
731
|
+
// ----- USA — Mainstream -----
|
|
732
|
+
{ id: "cali-beach", label: "California Beach", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Laid-back beach-town California aesthetic", promptHint: "a California beach aesthetic — laid-back beach-town vibe, golden-hour sun-warmed energy, easy carefree mood" },
|
|
733
|
+
{ id: "valley-girl", label: "Valley Girl", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Southern California valley aesthetic (feminine)", promptHint: "a Valley Girl aesthetic — Southern California suburban-chic vibe, polished and sun-kissed" },
|
|
734
|
+
{ id: "norcal-hippie", label: "NorCal Hippie", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Northern California Berkeley / Bay-Area hippie aesthetic", promptHint: "a Northern California hippie aesthetic — laid-back Berkeley / Bay-Area mood, gentle counter-culture vibe" },
|
|
735
|
+
{ id: "boho-la", label: "Boho LA", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Los Angeles canyon-bohemian aesthetic (feminine)", promptHint: "a Boho LA aesthetic — Los Angeles canyon-bohemian vibe, free-spirited Laurel Canyon mood" },
|
|
736
|
+
{ id: "texas-cowgirl", label: "Texas Cowgirl", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Texas Western ranch-country aesthetic (feminine)", promptHint: "a Texas cowgirl aesthetic — Western ranch-country vibe, warm Lone-Star confidence" },
|
|
737
|
+
{ id: "texas-cowboy", label: "Texas Cowboy", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Texas Western ranch-country aesthetic (masculine)", promptHint: "a Texas cowboy aesthetic — Western ranch-country vibe, weathered Lone-Star presence" },
|
|
738
|
+
{ id: "southern-belle", label: "Southern Belle", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Charleston / Savannah antebellum-gentility aesthetic (feminine)", promptHint: "a Southern Belle aesthetic — Charleston / Savannah gentility, gracious antebellum charm" },
|
|
739
|
+
{ id: "southern-gentleman", label: "Southern Gentleman", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Old-South gentility aesthetic (masculine)", promptHint: "a Southern Gentleman aesthetic — old-South gentility, gracious Charleston / Savannah charm" },
|
|
740
|
+
{ id: "miami-latina", label: "Miami Latina", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "South Beach Caribbean-Latin aesthetic (feminine)", promptHint: "a Miami Latina aesthetic — South Beach Caribbean-Latin energy, sun-and-neon vibrance" },
|
|
741
|
+
{ id: "miami-latino", label: "Miami Latino", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "South Beach Caribbean-Latin aesthetic (masculine)", promptHint: "a Miami Latino aesthetic — South Beach Caribbean-Latin energy, sun-and-neon confidence" },
|
|
742
|
+
{ id: "hawaii-island", label: "Hawaii Island", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Polynesian-Pacific Hawaiian aesthetic", promptHint: "a Hawaiian island aesthetic — Polynesian-Pacific vibe, gentle ocean-warmed presence" },
|
|
743
|
+
{ id: "nyc-fashion", label: "NYC Fashion", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Manhattan editorial-fashion aesthetic (feminine)", promptHint: "a New York fashion-girl aesthetic — Manhattan editorial vibe, urban-sharp confident energy" },
|
|
744
|
+
{ id: "nyc-sharp", label: "NYC Sharp", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Manhattan power-tailored aesthetic (masculine)", promptHint: "a New York sharp-dressed aesthetic — Manhattan power-and-pace vibe, urban-tailored presence" },
|
|
745
|
+
{ id: "brooklyn-hipster", label: "Brooklyn Hipster", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Williamsburg creative-class aesthetic", promptHint: "a Brooklyn hipster aesthetic — Williamsburg creative-class vibe, considered bohemian-urban energy" },
|
|
746
|
+
{ id: "new-england-prep", label: "New England Prep", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Cape Cod / Nantucket old-money preppy aesthetic", promptHint: "a New England prep aesthetic — Cape Cod / Nantucket old-money vibe, classic Ivy-coast polish" },
|
|
747
|
+
{ id: "pnw-granola", label: "PNW Granola", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Portland / Seattle outdoorsy aesthetic", promptHint: "a Pacific Northwest granola aesthetic — Portland / Seattle outdoorsy vibe, mossy mountain-coffee mood" },
|
|
748
|
+
{ id: "nashville-country", label: "Nashville Country", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Music-City modern country-pop aesthetic (feminine)", promptHint: "a Nashville country-pop aesthetic — Music-City warmth, Southern modern-country charm" },
|
|
749
|
+
{ id: "nashville-cowboy", label: "Nashville Cowboy", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Music-City modern-cowboy aesthetic (masculine)", promptHint: "a Nashville modern-cowboy aesthetic — Music-City warmth, modern-Western charm" },
|
|
750
|
+
{ id: "vegas-glam", label: "Vegas Glam", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Las Vegas Strip-glamour aesthetic (feminine)", promptHint: "a Las Vegas glam aesthetic — Strip-glamour vibe, high-shine showroom presence" },
|
|
751
|
+
{ id: "vegas-suit", label: "Vegas Suit", group: "USA — Mainstream", dimension: "regional-aesthetic", description: "Las Vegas Strip suited aesthetic (masculine)", promptHint: "a Las Vegas suit-and-shine aesthetic — Strip-glamour vibe, high-roller charisma" },
|
|
752
|
+
|
|
753
|
+
// ----- USA — African-American -----
|
|
754
|
+
{ id: "harlem-classic", label: "Harlem Classic", group: "USA — African-American", dimension: "regional-aesthetic", description: "Black-New-York heritage aesthetic, jazz-age elegance", promptHint: "a Harlem classic aesthetic — Black-New-York heritage vibe, jazz-age elegance and modern-uptown polish" },
|
|
755
|
+
{ id: "atl-southern", label: "ATL Southern", group: "USA — African-American", dimension: "regional-aesthetic", description: "Atlanta Black-South hip-hop / R&B aesthetic", promptHint: "an Atlanta Black-South aesthetic — Southern hip-hop / R&B capital vibe, peach-tree confident-and-warm mood" },
|
|
756
|
+
{ id: "nola-creole", label: "NOLA Creole", group: "USA — African-American", dimension: "regional-aesthetic", description: "New Orleans Louisiana French-Caribbean-African aesthetic", promptHint: "a New Orleans Creole aesthetic — Louisiana French-Caribbean-African vibe, jazz-and-second-line warmth" },
|
|
757
|
+
{ id: "dmv-go-go", label: "DMV Go-Go", group: "USA — African-American", dimension: "regional-aesthetic", description: "DC / Maryland / Virginia Black-DMV go-go aesthetic", promptHint: "a DC / Maryland / Virginia Black-DMV aesthetic — go-go-music capital vibe, polished East-Coast confidence" },
|
|
758
|
+
{ id: "west-coast-hip-hop", label: "West Coast", group: "USA — African-American", dimension: "regional-aesthetic", description: "South Central / Compton West Coast hip-hop aesthetic", promptHint: "a West Coast Black-LA aesthetic — South Central / Compton hip-hop vibe, sun-warmed laid-back swagger" },
|
|
759
|
+
{ id: "chicago-south-side", label: "Chicago South Side", group: "USA — African-American", dimension: "regional-aesthetic", description: "Midwest Black-urban Chicago aesthetic", promptHint: "a Chicago South Side aesthetic — Midwest Black-urban vibe, drill-era street-sharp presence" },
|
|
760
|
+
|
|
761
|
+
// ----- Europe -----
|
|
762
|
+
{ id: "parisienne", label: "Parisienne", group: "Europe", dimension: "regional-aesthetic", description: "Left-Bank Paris French-chic aesthetic (feminine)", promptHint: "a Parisienne aesthetic — Left-Bank Paris vibe, effortless French-chic mood, je-ne-sais-quoi confidence" },
|
|
763
|
+
{ id: "parisian-flaneur", label: "Parisian Flâneur", group: "Europe", dimension: "regional-aesthetic", description: "Left-Bank Paris French-chic aesthetic (masculine)", promptHint: "a Parisian flâneur aesthetic — Left-Bank Paris vibe, contemplative French-chic mood, refined city-walker air" },
|
|
764
|
+
{ id: "milanese-fashion", label: "Milanese Fashion", group: "Europe", dimension: "regional-aesthetic", description: "Italian Milanese high-fashion aesthetic", promptHint: "a Milanese fashion aesthetic — Italian high-fashion polish, Quadrilatero confident sleekness" },
|
|
765
|
+
{ id: "sicilian-beach", label: "Sicilian Beach", group: "Europe", dimension: "regional-aesthetic", description: "Mediterranean southern-Italian beach aesthetic", promptHint: "a Sicilian beach aesthetic — Mediterranean sun-and-salt vibe, warm southern-Italian ease" },
|
|
766
|
+
{ id: "madrileña", label: "Madrileña", group: "Europe", dimension: "regional-aesthetic", description: "Madrid Spanish aesthetic (feminine)", promptHint: "a Madrileña aesthetic — Madrid Spanish vibe, sun-and-confidence Iberian energy" },
|
|
767
|
+
{ id: "madrileño", label: "Madrileño", group: "Europe", dimension: "regional-aesthetic", description: "Madrid Spanish aesthetic (masculine)", promptHint: "a Madrileño aesthetic — Madrid Spanish vibe, sun-and-confidence Iberian presence" },
|
|
768
|
+
{ id: "catalan-modern", label: "Catalan Modern", group: "Europe", dimension: "regional-aesthetic", description: "Barcelona Mediterranean-design aesthetic", promptHint: "a Catalan modern aesthetic — Barcelona Mediterranean-design vibe, easygoing creative coast" },
|
|
769
|
+
{ id: "greek-mediterranean", label: "Greek Mediterranean", group: "Europe", dimension: "regional-aesthetic", description: "Aegean island Greek aesthetic", promptHint: "a Greek Mediterranean aesthetic — Aegean island vibe, sun-bleached limestone-and-sea mood" },
|
|
770
|
+
{ id: "scandi-minimal", label: "Scandi Minimal", group: "Europe", dimension: "regional-aesthetic", description: "Nordic clean-line minimal aesthetic", promptHint: "a Scandinavian minimal aesthetic — Nordic clean-line vibe, hygge-and-quiet-confidence mood" },
|
|
771
|
+
{ id: "english-rose", label: "English Rose", group: "Europe", dimension: "regional-aesthetic", description: "Countryside English aesthetic (feminine)", promptHint: "an English Rose aesthetic — countryside English vibe, gentle rose-garden refinement" },
|
|
772
|
+
{ id: "london-prep", label: "London Prep", group: "Europe", dimension: "regional-aesthetic", description: "Mayfair / Chelsea refined British aesthetic (masculine)", promptHint: "a London prep aesthetic — Mayfair / Chelsea vibe, refined British polish" },
|
|
773
|
+
{ id: "manchester-indie", label: "Manchester Indie", group: "Europe", dimension: "regional-aesthetic", description: "North-England gig-and-rain working-class-cool aesthetic", promptHint: "a Manchester indie aesthetic — North-England gig-and-rain vibe, working-class cool" },
|
|
774
|
+
{ id: "berlin-alt", label: "Berlin Alt", group: "Europe", dimension: "regional-aesthetic", description: "Kreuzberg techno-and-art counterculture aesthetic", promptHint: "a Berlin alt aesthetic — Kreuzberg techno-and-art vibe, edgy considered counterculture" },
|
|
775
|
+
{ id: "munich-traditional", label: "Munich Traditional", group: "Europe", dimension: "regional-aesthetic", description: "Bavarian Oktoberfest-traditional aesthetic", promptHint: "a Munich Bavarian-traditional aesthetic — Oktoberfest warmth, alpine-folk pride" },
|
|
776
|
+
{ id: "eastern-european-glam", label: "Eastern European Glam", group: "Europe", dimension: "regional-aesthetic", description: "Moscow / Kyiv winter-glamour aesthetic (feminine)", promptHint: "an Eastern European glam aesthetic — Moscow / Kyiv vibe, statement-confident winter-glamour" },
|
|
777
|
+
{ id: "eastern-european-stoic", label: "Eastern European Stoic", group: "Europe", dimension: "regional-aesthetic", description: "Moscow / Kyiv weathered-confidence aesthetic (masculine)", promptHint: "an Eastern European stoic aesthetic — Moscow / Kyiv vibe, weathered confidence, winter-hardened presence" },
|
|
778
|
+
|
|
779
|
+
// ----- Asia -----
|
|
780
|
+
{ id: "tokyo-harajuku", label: "Tokyo Harajuku", group: "Asia", dimension: "regional-aesthetic", description: "Tokyo playful expressive Japanese street aesthetic", promptHint: "a Tokyo Harajuku aesthetic — playful expressive Japanese street vibe, color-and-character mood" },
|
|
781
|
+
{ id: "ginza-office", label: "Ginza Office", group: "Asia", dimension: "regional-aesthetic", description: "Tokyo Ginza Japanese corporate-elegant aesthetic", promptHint: "a Ginza Tokyo office aesthetic — buttoned-up Japanese corporate-elegant vibe" },
|
|
782
|
+
{ id: "shibuya-streetwear", label: "Shibuya Streetwear", group: "Asia", dimension: "regional-aesthetic", description: "Tokyo Shibuya street-fashion aesthetic", promptHint: "a Shibuya streetwear aesthetic — Tokyo street-fashion vibe, layered post-modern mood" },
|
|
783
|
+
{ id: "osaka-everyday", label: "Osaka Everyday", group: "Asia", dimension: "regional-aesthetic", description: "Osaka warm casual Kansai aesthetic", promptHint: "an Osaka everyday aesthetic — warm casual Kansai vibe, easygoing humor-and-warmth" },
|
|
784
|
+
{ id: "seoul-k-pop", label: "Seoul K-Pop", group: "Asia", dimension: "regional-aesthetic", description: "Seoul Korean-pop polished aesthetic", promptHint: "a Seoul K-pop aesthetic — Korean-pop polish vibe, idol-grade brightness" },
|
|
785
|
+
{ id: "gangnam-glam", label: "Gangnam Glam", group: "Asia", dimension: "regional-aesthetic", description: "Seoul Gangnam Korean luxury-district aesthetic", promptHint: "a Gangnam Seoul glam aesthetic — Korean luxury-district vibe, polished beauty-statement" },
|
|
786
|
+
{ id: "shanghai-modern", label: "Shanghai Modern", group: "Asia", dimension: "regional-aesthetic", description: "Shanghai Chinese metropolitan aesthetic", promptHint: "a Shanghai modern aesthetic — Chinese metropolitan vibe, Bund-skyline cosmopolitan polish" },
|
|
787
|
+
{ id: "beijing-hutong", label: "Beijing Hutong", group: "Asia", dimension: "regional-aesthetic", description: "Beijing old-courtyard traditional-meets-modern aesthetic", promptHint: "a Beijing hutong aesthetic — old-Beijing courtyard vibe, traditional-meets-modern mood" },
|
|
788
|
+
{ id: "hong-kong-cinematic", label: "Hong Kong Cinematic", group: "Asia", dimension: "regional-aesthetic", description: "Hong Kong neon-noir Wong-Kar-Wai aesthetic", promptHint: "a Hong Kong cinematic aesthetic — neon-mid-century vibe, Wong-Kar-Wai mood" },
|
|
789
|
+
{ id: "mumbai-bollywood", label: "Mumbai Bollywood", group: "Asia", dimension: "regional-aesthetic", description: "Mumbai Indian film-industry aesthetic", promptHint: "a Mumbai Bollywood aesthetic — Indian film-industry vibe, vibrant statement-glamour" },
|
|
790
|
+
{ id: "south-india-traditional", label: "South India Traditional", group: "Asia", dimension: "regional-aesthetic", description: "Tamil / Kerala temple-town classical aesthetic", promptHint: "a South Indian traditional aesthetic — Tamil / Kerala temple-town vibe, classical refinement" },
|
|
791
|
+
{ id: "bangkok-street", label: "Bangkok Street", group: "Asia", dimension: "regional-aesthetic", description: "Bangkok Thai night-market neon-urban aesthetic", promptHint: "a Bangkok street aesthetic — Thai night-market energy, neon-and-warmth urban vibe" },
|
|
792
|
+
|
|
793
|
+
// ----- Latin America -----
|
|
794
|
+
{ id: "carioca-rio", label: "Carioca (Rio)", group: "Latin America", dimension: "regional-aesthetic", description: "Rio de Janeiro beach-and-favela-music Brazilian aesthetic", promptHint: "a Carioca aesthetic — Rio de Janeiro beach-and-favela-music vibe, sun-warmed Brazilian energy" },
|
|
795
|
+
{ id: "paulista", label: "Paulista (São Paulo)", group: "Latin America", dimension: "regional-aesthetic", description: "São Paulo metropolitan Brazilian creative-class aesthetic", promptHint: "a Paulista aesthetic — São Paulo metropolitan vibe, urban-Brazilian creative-class polish" },
|
|
796
|
+
{ id: "buenos-aires-tango", label: "Buenos Aires Tango", group: "Latin America", dimension: "regional-aesthetic", description: "Buenos Aires Recoleta / San Telmo tango aesthetic (feminine)", promptHint: "a Buenos Aires tango aesthetic — Recoleta / San Telmo vibe, sultry porteña confidence" },
|
|
797
|
+
{ id: "porteño", label: "Porteño", group: "Latin America", dimension: "regional-aesthetic", description: "Buenos Aires refined milonguero aesthetic (masculine)", promptHint: "a Porteño aesthetic — Buenos Aires vibe, refined milonguero presence" },
|
|
798
|
+
{ id: "mexico-city-chic", label: "Mexico City Chic", group: "Latin America", dimension: "regional-aesthetic", description: "CDMX Roma / Polanco modern-Mexican aesthetic", promptHint: "a Mexico City chic aesthetic — CDMX Roma / Polanco vibe, modern-Mexican polish" },
|
|
799
|
+
{ id: "chilango-traditional", label: "Chilango Traditional", group: "Latin America", dimension: "regional-aesthetic", description: "Mexico City warm folk-and-feast aesthetic", promptHint: "a Chilango traditional aesthetic — Mexico City vibe, warm folk-and-feast mood" },
|
|
800
|
+
{ id: "bogota-cosmopolitan", label: "Bogotá Cosmopolitan", group: "Latin America", dimension: "regional-aesthetic", description: "Bogotá Colombian capital refined Andean aesthetic", promptHint: "a Bogotá cosmopolitan aesthetic — Colombian capital vibe, refined Andean polish" },
|
|
801
|
+
|
|
802
|
+
// ----- Middle East -----
|
|
803
|
+
{ id: "beirut-riviera", label: "Beirut Riviera", group: "Middle East", dimension: "regional-aesthetic", description: "Lebanese Mediterranean cosmopolitan aesthetic", promptHint: "a Beirut Riviera aesthetic — Lebanese Mediterranean vibe, cosmopolitan-glamour mood" },
|
|
804
|
+
{ id: "tel-aviv-beach", label: "Tel Aviv Beach", group: "Middle East", dimension: "regional-aesthetic", description: "Tel Aviv Mediterranean coastal-city startup-energy aesthetic", promptHint: "a Tel Aviv beach aesthetic — Mediterranean coastal-city vibe, easygoing sun-and-sea startup-energy mood" },
|
|
805
|
+
{ id: "jerusalem-stone", label: "Jerusalem Stone", group: "Middle East", dimension: "regional-aesthetic", description: "Jerusalem old-city limestone-heritage aesthetic", promptHint: "a Jerusalem old-city aesthetic — limestone-warmth heritage vibe, layered ancient-and-modern presence" },
|
|
806
|
+
{ id: "sabra", label: "Sabra", group: "Middle East", dimension: "regional-aesthetic", description: "Native-Israeli direct-and-warm Sabra aesthetic", promptHint: "a Sabra aesthetic — native-Israeli vibe, sun-warmed direct-and-warm energy, prickly-outside-sweet-inside mood" },
|
|
807
|
+
{ id: "dubai-modern", label: "Dubai Modern", group: "Middle East", dimension: "regional-aesthetic", description: "Gulf-luxury Dubai cosmopolitan-emirate aesthetic", promptHint: "a Dubai modern aesthetic — Gulf-luxury vibe, polished cosmopolitan-emirate presence" },
|
|
808
|
+
{ id: "persian-glam", label: "Persian Glam", group: "Middle East", dimension: "regional-aesthetic", description: "Iranian heritage-and-glamour Persian aesthetic", promptHint: "a Persian glam aesthetic — Iranian heritage-and-glamour vibe, refined statement mood" },
|
|
809
|
+
{ id: "istanbul-bosphorus", label: "Istanbul Bosphorus", group: "Middle East", dimension: "regional-aesthetic", description: "Istanbul east-meets-west Bosphorus Ottoman-and-modern aesthetic", promptHint: "an Istanbul Bosphorus aesthetic — east-meets-west Turkish vibe, layered Ottoman-and-modern strait-city presence" },
|
|
810
|
+
|
|
811
|
+
// ----- North Africa -----
|
|
812
|
+
{ id: "cairene-cosmopolitan", label: "Cairene Cosmopolitan", group: "North Africa", dimension: "regional-aesthetic", description: "Cairo Egyptian-Arab metropolitan aesthetic", promptHint: "a Cairene cosmopolitan aesthetic — Cairo metropolitan vibe, layered Egyptian-Arab modern-and-historical mood" },
|
|
813
|
+
{ id: "maghreb-coastal", label: "Maghreb Coastal", group: "North Africa", dimension: "regional-aesthetic", description: "Tunis / Algiers French-Mediterranean-North-African colonial-port aesthetic", promptHint: "a Maghreb coastal aesthetic — Tunis / Algiers French-Mediterranean-North-African vibe, sun-bleached colonial-port mood" },
|
|
814
|
+
{ id: "marrakech-bohemian", label: "Marrakech Bohemian", group: "North Africa", dimension: "regional-aesthetic", description: "Marrakech Moroccan medina warm-spice aesthetic", promptHint: "a Marrakech bohemian aesthetic — Moroccan medina vibe, warm-spice and -textile mood" },
|
|
815
|
+
|
|
816
|
+
// ----- Sub-Saharan Africa -----
|
|
817
|
+
// 🇬🇧 Anglophone, 🇫🇷 Francophone, 🇵🇹 Lusophone — colonial-cultural overlay
|
|
818
|
+
// drives sartorial / media-diet differences. Ethiopia untagged (never colonized).
|
|
819
|
+
{ id: "lagos-afro-glam", label: "Lagos Afro-Glam", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇬🇧 Lagos Nigerian Anglophone-West-African metropolitan aesthetic", promptHint: "a Lagos Afro-glam aesthetic — Nigerian Anglophone-West-African metropolitan vibe, vibrant statement-fashion mood" },
|
|
820
|
+
{ id: "accra-afro-fashion", label: "Accra Afro-Fashion", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇬🇧 Accra Ghanaian Anglophone-West-African kente-meets-modern aesthetic", promptHint: "an Accra Afro-fashion aesthetic — Ghanaian Anglophone-West-African vibe, kente-meets-modern statement" },
|
|
821
|
+
{ id: "dakar-francophone", label: "Dakar Francophone", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇫🇷 Dakar Senegalese Francophone-West-African coastal-cosmopolitan aesthetic", promptHint: "a Dakar aesthetic — Senegalese Francophone-West-African coastal-cosmopolitan vibe, refined boubou-and-Paris-pull mood" },
|
|
822
|
+
{ id: "abidjan-cosmopolitan", label: "Abidjan Cosmopolitan", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇫🇷 Abidjan Ivoirian Francophone-West-African urban aesthetic", promptHint: "an Abidjan aesthetic — Ivoirian Francophone-West-African urban vibe, lagoon-city polish" },
|
|
823
|
+
{ id: "kinshasa-sape", label: "Kinshasa SAPE", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇫🇷 Kinshasa Congolese Francophone-Central-African SAPE dandy aesthetic", promptHint: "a Kinshasa SAPE aesthetic — Congolese Francophone-Central-African dandy vibe, Society-of-Ambianceurs hyper-tailored statement" },
|
|
824
|
+
{ id: "nairobi-cosmopolitan", label: "Nairobi Cosmopolitan", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇬🇧 Nairobi Kenyan Anglophone-East-African modern-urban aesthetic", promptHint: "a Nairobi cosmopolitan aesthetic — Kenyan Anglophone-East-African modern-urban vibe, considered creative-class polish" },
|
|
825
|
+
{ id: "addis-habesha", label: "Addis Habesha", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "Addis Ababa Ethiopian Habesha highland-heritage aesthetic (Ethiopia was never colonized)", promptHint: "an Addis Ababa Habesha aesthetic — Ethiopian highland heritage vibe, refined coffee-ceremony-and-modern-Addis polish" },
|
|
826
|
+
{ id: "swahili-coast", label: "Swahili Coast", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇬🇧 Zanzibar / Mombasa Anglophone-East-African coastal-Swahili aesthetic", promptHint: "a Swahili-Coast aesthetic — Zanzibar / Mombasa Anglophone-East-African coastal vibe, Indian-Ocean-trade-route warmth" },
|
|
827
|
+
{ id: "johannesburg-urban", label: "Johannesburg Urban", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇬🇧 Johannesburg South-African Anglophone-Southern-African Black-metropolitan aesthetic", promptHint: "a Johannesburg urban aesthetic — South-African Anglophone-Southern-African Black-metropolitan vibe, post-Soweto urban-creative confidence" },
|
|
828
|
+
{ id: "cape-town-cosmopolitan", label: "Cape Town Cosmopolitan", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇬🇧 Cape Town South-African Anglophone-Southern-African coastal-city aesthetic", promptHint: "a Cape Town cosmopolitan aesthetic — South-African Anglophone-Southern-African coastal-city vibe, easygoing creative polish" },
|
|
829
|
+
{ id: "luanda-lusophone", label: "Luanda Lusophone", group: "Sub-Saharan Africa", dimension: "regional-aesthetic", description: "🇵🇹 Luanda Angolan Lusophone-Southern-African kizomba-and-Atlantic-coast aesthetic", promptHint: "a Luanda aesthetic — Angolan Lusophone-Southern-African vibe, kizomba-and-Atlantic-coast statement-presence" },
|
|
830
|
+
|
|
831
|
+
// ----- Oceania -----
|
|
832
|
+
{ id: "bondi-beach", label: "Bondi Beach", group: "Oceania", dimension: "regional-aesthetic", description: "Bondi Australian coastal salt-and-sun aesthetic", promptHint: "a Bondi Beach aesthetic — Australian coastal vibe, salt-and-sun easygoing presence" },
|
|
833
|
+
{ id: "australian-outback", label: "Outback", group: "Oceania", dimension: "regional-aesthetic", description: "Rural Australian Outback aesthetic (feminine)", promptHint: "an Australian Outback aesthetic — rural Australian vibe, weathered sun-burnt confidence" },
|
|
834
|
+
{ id: "outback-stockman", label: "Outback Stockman", group: "Oceania", dimension: "regional-aesthetic", description: "Rural Australian Outback aesthetic (masculine)", promptHint: "an Outback Stockman aesthetic — rural Australian vibe, weathered station-hand presence" },
|
|
835
|
+
{ id: "auckland-coastal", label: "Auckland Coastal", group: "Oceania", dimension: "regional-aesthetic", description: "Auckland New Zealand harbor-city Pacific aesthetic", promptHint: "an Auckland coastal aesthetic — New Zealand harbor-city vibe, considered Pacific polish" },
|
|
836
|
+
] as const
|
|
837
|
+
|
|
838
|
+
export const PERSON_DIMENSION_ORDER: ReadonlyArray<PersonDimension> = [
|
|
839
|
+
// Identity
|
|
840
|
+
"type",
|
|
841
|
+
"age",
|
|
842
|
+
"ethnicity",
|
|
843
|
+
"regional-aesthetic",
|
|
844
|
+
// Body
|
|
845
|
+
"frame",
|
|
846
|
+
"body-mass",
|
|
847
|
+
"bust",
|
|
848
|
+
"waist",
|
|
849
|
+
"hips",
|
|
850
|
+
"silhouette",
|
|
851
|
+
// Face structure
|
|
852
|
+
"face-shape",
|
|
853
|
+
"jawline",
|
|
854
|
+
"cheekbones",
|
|
855
|
+
"facial-fullness",
|
|
856
|
+
"eye-shape",
|
|
857
|
+
"eyelid-type",
|
|
858
|
+
"canthal-tilt",
|
|
859
|
+
"eye-spacing",
|
|
860
|
+
"eye-set-brow",
|
|
861
|
+
"nose",
|
|
862
|
+
"nose-tip",
|
|
863
|
+
"lip-fullness",
|
|
864
|
+
"lip-shape",
|
|
865
|
+
"lip-state",
|
|
866
|
+
// Hair
|
|
867
|
+
"hair-base",
|
|
868
|
+
"hair-color",
|
|
869
|
+
"eyebrows",
|
|
870
|
+
// Skin & eyes
|
|
871
|
+
"skin-tone",
|
|
872
|
+
"skin-texture",
|
|
873
|
+
"eye-color",
|
|
874
|
+
"eye-state",
|
|
875
|
+
"facial-hair",
|
|
876
|
+
"distinctive-features",
|
|
877
|
+
]
|
|
878
|
+
|
|
879
|
+
export const PERSON_DIMENSION_LABELS: Readonly<Record<PersonDimension, string>> = {
|
|
880
|
+
type: "Type",
|
|
881
|
+
age: "Age",
|
|
882
|
+
ethnicity: "Ethnicity",
|
|
883
|
+
"regional-aesthetic": "Regional Aesthetic",
|
|
884
|
+
frame: "Frame",
|
|
885
|
+
"body-mass": "Body Mass",
|
|
886
|
+
bust: "Bust",
|
|
887
|
+
waist: "Waist",
|
|
888
|
+
hips: "Hips",
|
|
889
|
+
silhouette: "Silhouette",
|
|
890
|
+
"face-shape": "Face Shape",
|
|
891
|
+
jawline: "Jawline",
|
|
892
|
+
cheekbones: "Cheekbones",
|
|
893
|
+
"facial-fullness": "Facial Fullness",
|
|
894
|
+
"eye-shape": "Eye Shape",
|
|
895
|
+
"eyelid-type": "Eyelid Type",
|
|
896
|
+
"canthal-tilt": "Canthal Tilt",
|
|
897
|
+
"eye-spacing": "Eye Spacing",
|
|
898
|
+
"eye-set-brow": "Eye Set / Brow Distance",
|
|
899
|
+
nose: "Nose",
|
|
900
|
+
"nose-tip": "Nose Tip",
|
|
901
|
+
"lip-fullness": "Lip Fullness",
|
|
902
|
+
"lip-shape": "Lip Shape",
|
|
903
|
+
"lip-state": "Lip State",
|
|
904
|
+
"hair-color": "Hair Color",
|
|
905
|
+
"hair-base": "Hair (Texture & Length)",
|
|
906
|
+
eyebrows: "Eyebrows",
|
|
907
|
+
"skin-tone": "Skin Tone",
|
|
908
|
+
"skin-texture": "Skin Texture",
|
|
909
|
+
"eye-color": "Eye Color",
|
|
910
|
+
"eye-state": "Eye State",
|
|
911
|
+
"facial-hair": "Facial Hair",
|
|
912
|
+
"distinctive-features": "Distinctive Features",
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Cross-dimension grouping for the compact Person picker view. The detailed
|
|
917
|
+
* view renders one row per dimension (PERSON_DIMENSION_ORDER); the compact
|
|
918
|
+
* view collapses those rows into these six labelled sections. This is the
|
|
919
|
+
* machine-readable form of the section comments in PERSON_DIMENSION_ORDER —
|
|
920
|
+
* the guard test (person-sections.test.ts) asserts it partitions every
|
|
921
|
+
* PersonDimension exactly once, so a newly added dimension can't silently
|
|
922
|
+
* drop out of the compact grouping.
|
|
923
|
+
*/
|
|
924
|
+
export interface PersonDimensionSection {
|
|
925
|
+
readonly label: string
|
|
926
|
+
readonly dimensions: ReadonlyArray<PersonDimension>
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
export const PERSON_DIMENSION_SECTIONS: ReadonlyArray<PersonDimensionSection> = [
|
|
930
|
+
{ label: "Identity", dimensions: ["type", "age", "ethnicity", "regional-aesthetic"] },
|
|
931
|
+
{ label: "Body", dimensions: ["frame", "body-mass", "bust", "waist", "hips", "silhouette"] },
|
|
932
|
+
{ label: "Face", dimensions: ["face-shape", "jawline", "cheekbones", "facial-fullness", "eye-shape", "eyelid-type", "canthal-tilt", "eye-spacing", "eye-set-brow", "nose", "nose-tip", "lip-fullness", "lip-shape", "lip-state"] },
|
|
933
|
+
{ label: "Hair", dimensions: ["hair-base", "hair-color", "eyebrows"] },
|
|
934
|
+
{ label: "Skin & Eyes", dimensions: ["skin-tone", "skin-texture", "eye-color", "eye-state"] },
|
|
935
|
+
{ label: "Features", dimensions: ["facial-hair", "distinctive-features"] },
|
|
936
|
+
] as const
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Maps each PersonDimension to the consumer data field name holding the
|
|
940
|
+
* selected entry id. Multi-dimension model: a consumer (PersonData) may
|
|
941
|
+
* independently set a value in each of the dimensions.
|
|
942
|
+
*/
|
|
943
|
+
export const PERSON_FIELD_BY_DIMENSION: Record<
|
|
944
|
+
PersonDimension,
|
|
945
|
+
| "type" | "age" | "ethnicity" | "regionalAesthetic"
|
|
946
|
+
| "frame" | "bodyMass" | "bust" | "waist" | "hips" | "silhouette"
|
|
947
|
+
| "faceShape" | "jawline" | "cheekbones" | "facialFullness"
|
|
948
|
+
| "eyeShape" | "eyelidType" | "canthalTilt" | "eyeSpacing" | "eyeSetBrow"
|
|
949
|
+
| "nose" | "noseTip" | "lipFullness" | "lipShape" | "lipState"
|
|
950
|
+
| "hairColor" | "hairBase" | "eyebrows"
|
|
951
|
+
| "skinTone" | "skinTexture" | "eyeColor" | "eyeState" | "facialHair" | "distinctiveFeature"
|
|
952
|
+
> = {
|
|
953
|
+
type: "type",
|
|
954
|
+
age: "age",
|
|
955
|
+
ethnicity: "ethnicity",
|
|
956
|
+
"regional-aesthetic": "regionalAesthetic",
|
|
957
|
+
frame: "frame",
|
|
958
|
+
"body-mass": "bodyMass",
|
|
959
|
+
bust: "bust",
|
|
960
|
+
waist: "waist",
|
|
961
|
+
hips: "hips",
|
|
962
|
+
silhouette: "silhouette",
|
|
963
|
+
"face-shape": "faceShape",
|
|
964
|
+
jawline: "jawline",
|
|
965
|
+
cheekbones: "cheekbones",
|
|
966
|
+
"facial-fullness": "facialFullness",
|
|
967
|
+
"eye-shape": "eyeShape",
|
|
968
|
+
"eyelid-type": "eyelidType",
|
|
969
|
+
"canthal-tilt": "canthalTilt",
|
|
970
|
+
"eye-spacing": "eyeSpacing",
|
|
971
|
+
"eye-set-brow": "eyeSetBrow",
|
|
972
|
+
nose: "nose",
|
|
973
|
+
"nose-tip": "noseTip",
|
|
974
|
+
"lip-fullness": "lipFullness",
|
|
975
|
+
"lip-shape": "lipShape",
|
|
976
|
+
"lip-state": "lipState",
|
|
977
|
+
"hair-color": "hairColor",
|
|
978
|
+
"hair-base": "hairBase",
|
|
979
|
+
eyebrows: "eyebrows",
|
|
980
|
+
"skin-tone": "skinTone",
|
|
981
|
+
"skin-texture": "skinTexture",
|
|
982
|
+
"eye-color": "eyeColor",
|
|
983
|
+
"eye-state": "eyeState",
|
|
984
|
+
"facial-hair": "facialHair",
|
|
985
|
+
"distinctive-features": "distinctiveFeature",
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/** Per-dimension multi-select cap (single source of truth for the picker UI,
|
|
989
|
+
* the image-analyzer tool schema, and the Zod validator).
|
|
990
|
+
* - ethnicity / regional-aesthetic / hair-color / eye-color / lip-state /
|
|
991
|
+
* eye-state / skin-texture: 2
|
|
992
|
+
* - distinctive-features: 3
|
|
993
|
+
* All other dims are single-select (absent → treated as 1). */
|
|
994
|
+
export const MAX_SELECTED_BY_DIMENSION: Partial<Record<PersonDimension, number>> = {
|
|
995
|
+
ethnicity: 2,
|
|
996
|
+
"regional-aesthetic": 2,
|
|
997
|
+
"hair-color": 2,
|
|
998
|
+
"eye-color": 2,
|
|
999
|
+
"distinctive-features": 3,
|
|
1000
|
+
"lip-state": 2,
|
|
1001
|
+
"eye-state": 2,
|
|
1002
|
+
"skin-texture": 2,
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/** Selection limit for a dimension (1 when not multi-select). */
|
|
1006
|
+
export function getPersonDimensionLimit(dimension: PersonDimension): number {
|
|
1007
|
+
return MAX_SELECTED_BY_DIMENSION[dimension] ?? 1
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Shape of the per-dimension person fields. All optional — user may set
|
|
1012
|
+
* zero, one, or all ten dimensions.
|
|
1013
|
+
*/
|
|
1014
|
+
export interface PersonValue {
|
|
1015
|
+
type?: string
|
|
1016
|
+
/** Selected age preset id (e.g. `"age-30s"`). When set to the special
|
|
1017
|
+
* `"age-custom"` sentinel, the literal age in years is read from
|
|
1018
|
+
* `customAge` and the hint is generated at build time. */
|
|
1019
|
+
age?: string
|
|
1020
|
+
/** Specific age in years. Only consulted when `age === "age-custom"`. */
|
|
1021
|
+
customAge?: number
|
|
1022
|
+
/** Single id, or an array of up to 2 ids for mixed heritage (e.g.
|
|
1023
|
+
* ["slavic","mediterranean"] → "of mixed Slavic and Mediterranean heritage"). */
|
|
1024
|
+
ethnicity?: string | ReadonlyArray<string>
|
|
1025
|
+
/** Regional / cultural aesthetic vibe (e.g. `"cali-beach"`, `"parisienne"`,
|
|
1026
|
+
* `"kinshasa-sape"`). Composes with ethnicity, skin tone, hair, and
|
|
1027
|
+
* styling — the dimension's promptHints are vibe-only and never hard-code
|
|
1028
|
+
* the visuals those other dimensions own. Single id or up to 2 ids for
|
|
1029
|
+
* hybrid looks (e.g. ["nyc-fashion","parisienne"]). */
|
|
1030
|
+
regionalAesthetic?: string | ReadonlyArray<string>
|
|
1031
|
+
/** Skeletal frame width / height (petite, slim, average, broad). Independent
|
|
1032
|
+
* of body mass — "slim frame + full bust" is expressible. */
|
|
1033
|
+
frame?: string
|
|
1034
|
+
/** Body mass / leanness (lean, average, full, heavy). Independent of frame. */
|
|
1035
|
+
bodyMass?: string
|
|
1036
|
+
/** Bust size (small, average, full, very full) — applicable subjects. */
|
|
1037
|
+
bust?: string
|
|
1038
|
+
/** Waist definition (defined, average, straight). */
|
|
1039
|
+
waist?: string
|
|
1040
|
+
/** Hip width (narrow, balanced, wide). */
|
|
1041
|
+
hips?: string
|
|
1042
|
+
/** Overall body silhouette (hourglass, rectangular, pear, inverted-triangle,
|
|
1043
|
+
* athletic). Optional — only emits when picked. */
|
|
1044
|
+
silhouette?: string
|
|
1045
|
+
/** Face silhouette (oval, round, square, heart…). */
|
|
1046
|
+
faceShape?: string
|
|
1047
|
+
/** Jaw shape (strong, soft, pointed, wide). */
|
|
1048
|
+
jawline?: string
|
|
1049
|
+
/** Cheekbone projection / height (low, average, high-defined, sculpted-high, wide). */
|
|
1050
|
+
cheekbones?: string
|
|
1051
|
+
/** Face-specific leanness, independent of body mass (gaunt, lean, average, full, round). */
|
|
1052
|
+
facialFullness?: string
|
|
1053
|
+
/** Eye shape — anatomical shape only (almond, round, monolid, double-eyelid,
|
|
1054
|
+
* wide, narrow). Lid type, canthal tilt, spacing and brow-set are separate. */
|
|
1055
|
+
eyeShape?: string
|
|
1056
|
+
/** Upper-eyelid trait (standard, hooded, droopy, deep-set). */
|
|
1057
|
+
eyelidType?: string
|
|
1058
|
+
/** Canthal tilt — outer-corner angle (neutral, upturned, downturned). */
|
|
1059
|
+
canthalTilt?: string
|
|
1060
|
+
/** Horizontal eye spacing (close-set, average, wide-set). */
|
|
1061
|
+
eyeSpacing?: string
|
|
1062
|
+
/** Brow-to-eye distance / eye set (low-set "hunter", average, high-set "doe"). */
|
|
1063
|
+
eyeSetBrow?: string
|
|
1064
|
+
/** Nose bridge shape (straight, aquiline, snub, broad…). */
|
|
1065
|
+
nose?: string
|
|
1066
|
+
/** Nose tip rotation / shape (natural, refined, upturned, rounded, drooping). */
|
|
1067
|
+
noseTip?: string
|
|
1068
|
+
/** Lip fullness (thin, medium, full, full-lower). Replaces the legacy
|
|
1069
|
+
* combined `lips` field together with `lipShape`. */
|
|
1070
|
+
lipFullness?: string
|
|
1071
|
+
/** Lip shape (natural, cupid's bow, wide, heart, small). Replaces the legacy
|
|
1072
|
+
* combined `lips` field together with `lipFullness`. */
|
|
1073
|
+
lipShape?: string
|
|
1074
|
+
/** @deprecated Split into `lipFullness` + `lipShape`. Retained so legacy
|
|
1075
|
+
* workflow data keeps resolving — `buildPersonHints` reads it as a fallback
|
|
1076
|
+
* and `migratePersonValue` relocates it onto the new fields. */
|
|
1077
|
+
lips?: string
|
|
1078
|
+
/** Lip state — what the lips are doing right now (chapped, glossy,
|
|
1079
|
+
* parted, biting, pursed, bold-red…). Distinct from `lips` which is
|
|
1080
|
+
* anatomical shape. Single id or up to 2 (e.g. glossy + parted). */
|
|
1081
|
+
lipState?: string | ReadonlyArray<string>
|
|
1082
|
+
/** Single id or up to 2 ids for two-tone / ombre / highlighted hair
|
|
1083
|
+
* (e.g. ["hair-black","hair-platinum"]). */
|
|
1084
|
+
hairColor?: string | ReadonlyArray<string>
|
|
1085
|
+
/** Natural hair texture + length (texture×length combos). The actual cut
|
|
1086
|
+
* / styling choice (bob, wolf cut, braids…) lives in Styling.hair-cut. */
|
|
1087
|
+
hairBase?: string
|
|
1088
|
+
eyebrows?: string
|
|
1089
|
+
skinTone?: string
|
|
1090
|
+
/** Skin texture (smooth, porcelain, freckled, …). Single id or up to 2
|
|
1091
|
+
* combined (e.g. porcelain + freckled, sun-kissed + dewy). */
|
|
1092
|
+
skinTexture?: string | ReadonlyArray<string>
|
|
1093
|
+
/** Single id or up to 2 ids for heterochromia (e.g.
|
|
1094
|
+
* ["eyes-blue","eyes-green"]). */
|
|
1095
|
+
eyeColor?: string | ReadonlyArray<string>
|
|
1096
|
+
/** Eye state — what the eyes are doing (closed, half-lidded, wide-eyed,
|
|
1097
|
+
* staring at camera, gazing away/up/down, glassy). Distinct from
|
|
1098
|
+
* `eyeShape` (anatomy) and `eyeColor`. Single id or up to 2 (e.g.
|
|
1099
|
+
* half-lidded + glassy). */
|
|
1100
|
+
eyeState?: string | ReadonlyArray<string>
|
|
1101
|
+
facialHair?: string
|
|
1102
|
+
/** Single id or up to 3 ids for combined features (e.g. freckles +
|
|
1103
|
+
* glasses + sleeve tattoo). */
|
|
1104
|
+
distinctiveFeature?: string | ReadonlyArray<string>
|
|
1105
|
+
/** Free-text appended BEFORE the dimension compound. Use when you want
|
|
1106
|
+
* context/framing to come first (e.g. "wet-haired" or "covered in paint"). */
|
|
1107
|
+
preText?: string
|
|
1108
|
+
/** Free-text appended AFTER the dimension compound. Use for extra
|
|
1109
|
+
* specifics that dimensions can't capture (e.g. "wearing a leather
|
|
1110
|
+
* jacket", "with a silver necklace"). */
|
|
1111
|
+
postText?: string
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
const personById = new Map<string, Person>(PEOPLE.map((p) => [p.id, p]))
|
|
1115
|
+
|
|
1116
|
+
export function getPerson(id: string | undefined | null): Person | undefined {
|
|
1117
|
+
if (!id) return undefined
|
|
1118
|
+
return personById.get(id)
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
export function getPersonLabel(id: string | undefined | null, fallback?: string): string {
|
|
1122
|
+
const p = getPerson(id)
|
|
1123
|
+
if (p) return p.label
|
|
1124
|
+
if (fallback !== undefined) return fallback
|
|
1125
|
+
return (id ?? "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
export function getPersonPromptHint(id: string | undefined | null): string {
|
|
1129
|
+
return getPerson(id)?.promptHint ?? ""
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
export const PERSON_IDS: ReadonlyArray<string> = PEOPLE.map((p) => p.id)
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* Aggregate all enabled per-dimension person prompt hints from a consumer's
|
|
1136
|
+
* data, in canonical dimension order (type, age, ethnicity, build, hair
|
|
1137
|
+
* style, hair color, skin tone, eye color, facial hair, distinctive
|
|
1138
|
+
* features).
|
|
1139
|
+
*
|
|
1140
|
+
* Accepts a loosely typed record (the helper is shared between strongly
|
|
1141
|
+
* typed frontend node data and the backend's `Record<string, unknown>`
|
|
1142
|
+
* workflow data). Non-string values are ignored.
|
|
1143
|
+
*
|
|
1144
|
+
* Returns array of fragment strings — caller joins on ", " for the
|
|
1145
|
+
* compound description ("a beautiful woman, in their 30s, East Asian, slim
|
|
1146
|
+
* build, long wavy hair, brown hair, fair skin, green eyes, wearing
|
|
1147
|
+
* glasses").
|
|
1148
|
+
*/
|
|
1149
|
+
/**
|
|
1150
|
+
* Build the ethnicity hint clause. Single pick → "of {Group} descent"
|
|
1151
|
+
* (the entry's own promptHint). Two picks → mixed-heritage clause that
|
|
1152
|
+
* names both backgrounds. We use the entry label rather than re-running
|
|
1153
|
+
* promptHint, since "of mixed X and Y heritage" is a different grammar
|
|
1154
|
+
* from concatenating two "of X descent" / "of Y descent" sentences.
|
|
1155
|
+
*/
|
|
1156
|
+
function normalizePickIds(value: unknown): string[] {
|
|
1157
|
+
if (typeof value === "string") return value ? [value] : []
|
|
1158
|
+
if (!Array.isArray(value)) return []
|
|
1159
|
+
const out: string[] = []
|
|
1160
|
+
for (const v of value) {
|
|
1161
|
+
if (typeof v === "string" && v && !out.includes(v)) out.push(v)
|
|
1162
|
+
}
|
|
1163
|
+
return out
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
function buildEthnicityHint(value: unknown): string {
|
|
1167
|
+
const ids = normalizePickIds(value)
|
|
1168
|
+
if (ids.length === 0) return ""
|
|
1169
|
+
if (ids.length === 1) return getPersonPromptHint(ids[0])
|
|
1170
|
+
const labels = ids
|
|
1171
|
+
.slice(0, 2)
|
|
1172
|
+
.map((id) => getPerson(id)?.shortLabel ?? getPerson(id)?.label ?? "")
|
|
1173
|
+
.filter((s): s is string => Boolean(s))
|
|
1174
|
+
if (labels.length < 2) return getPersonPromptHint(ids[0])
|
|
1175
|
+
return `of mixed ${labels[0]} and ${labels[1]} heritage`
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* Hair color: single → entry's hint ("blonde hair", "auburn hair"). Two →
|
|
1180
|
+
* "two-tone X and Y hair" using lowercased entry labels (covers ombre,
|
|
1181
|
+
* balayage, highlights, dyed two-tone styles).
|
|
1182
|
+
*/
|
|
1183
|
+
function buildHairColorHint(value: unknown): string {
|
|
1184
|
+
const ids = normalizePickIds(value)
|
|
1185
|
+
if (ids.length === 0) return ""
|
|
1186
|
+
if (ids.length === 1) return getPersonPromptHint(ids[0])
|
|
1187
|
+
const labels = ids
|
|
1188
|
+
.slice(0, 2)
|
|
1189
|
+
.map((id) => getPerson(id)?.label?.toLowerCase() ?? "")
|
|
1190
|
+
.filter((s): s is string => Boolean(s))
|
|
1191
|
+
if (labels.length < 2) return getPersonPromptHint(ids[0])
|
|
1192
|
+
return `two-tone ${labels[0]} and ${labels[1]} hair`
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* Eye color: single → entry's hint ("blue eyes"). Two → independent emit
|
|
1197
|
+
* like distinctive-features. We deliberately avoid hard-coding a
|
|
1198
|
+
* heterochromia interpretation because users also pick two colors to
|
|
1199
|
+
* mean "natural eye color + tinted contacts" or "sectoral / mixed iris".
|
|
1200
|
+
* Letting both hints stand side-by-side lets the diffusion model
|
|
1201
|
+
* resolve from the rest of the prompt context. Users wanting an explicit
|
|
1202
|
+
* heterochromia look should add the `feature-heterochromia` distinctive
|
|
1203
|
+
* feature alongside the two color picks.
|
|
1204
|
+
*/
|
|
1205
|
+
function buildEyeColorHints(value: unknown): string[] {
|
|
1206
|
+
return emitIndependentHints(value)
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Age hint with optional custom-numeric override. When the user picks the
|
|
1211
|
+
* "age-custom" sentinel and provides a specific number, generate a phrase
|
|
1212
|
+
* tuned to the life stage (toddler / child / teen wording for the lower
|
|
1213
|
+
* ranges, plain "{N} years old" for adults). Otherwise return the catalog
|
|
1214
|
+
* entry's static promptHint.
|
|
1215
|
+
*
|
|
1216
|
+
* Custom number is clamped to a sane range and rejected if non-finite.
|
|
1217
|
+
*/
|
|
1218
|
+
export function buildAgeHint(
|
|
1219
|
+
ageId: string | undefined | null,
|
|
1220
|
+
customAge: number | undefined | null,
|
|
1221
|
+
): string {
|
|
1222
|
+
if (ageId !== "age-custom") return getPersonPromptHint(ageId)
|
|
1223
|
+
if (typeof customAge !== "number" || !Number.isFinite(customAge)) return ""
|
|
1224
|
+
const n = Math.max(0, Math.min(120, Math.round(customAge)))
|
|
1225
|
+
if (n === 0) return "a newborn under 1 year old"
|
|
1226
|
+
if (n === 1) return "around 1 year old"
|
|
1227
|
+
if (n < 4) return `a toddler around ${n} years old`
|
|
1228
|
+
if (n < 7) return `a young child around ${n} years old`
|
|
1229
|
+
if (n < 10) return `a child around ${n} years old`
|
|
1230
|
+
if (n < 13) return `a pre-teen around ${n} years old`
|
|
1231
|
+
if (n < 20) return `${n} years old, in their teens`
|
|
1232
|
+
return `${n} years old`
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* Multi-pick "state" dims (distinctive-features, lip-state, eye-state,
|
|
1237
|
+
* skin-texture) — independent flags rather than mutually exclusive. Each
|
|
1238
|
+
* entry's promptHint is a standalone "with X" / "wearing X" / "staring X"
|
|
1239
|
+
* clause, so we emit them all separately and let buildPersonHints'
|
|
1240
|
+
* comma-join read naturally.
|
|
1241
|
+
*/
|
|
1242
|
+
function emitIndependentHints(value: unknown): string[] {
|
|
1243
|
+
const ids = normalizePickIds(value)
|
|
1244
|
+
const out: string[] = []
|
|
1245
|
+
for (const id of ids) {
|
|
1246
|
+
const hint = getPersonPromptHint(id)
|
|
1247
|
+
if (hint) out.push(hint)
|
|
1248
|
+
}
|
|
1249
|
+
return out
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
export function buildPersonHints(
|
|
1253
|
+
data: Record<string, unknown> & PersonValue,
|
|
1254
|
+
): string[] {
|
|
1255
|
+
const hints: string[] = []
|
|
1256
|
+
|
|
1257
|
+
const pre = typeof data.preText === "string" ? data.preText.trim() : ""
|
|
1258
|
+
if (pre) hints.push(pre)
|
|
1259
|
+
|
|
1260
|
+
for (const dimension of PERSON_DIMENSION_ORDER) {
|
|
1261
|
+
const field = PERSON_FIELD_BY_DIMENSION[dimension]
|
|
1262
|
+
const raw = data[field]
|
|
1263
|
+
if (dimension === "age") {
|
|
1264
|
+
const ageId = typeof raw === "string" ? raw : undefined
|
|
1265
|
+
const customAge = typeof data.customAge === "number" ? data.customAge : undefined
|
|
1266
|
+
const h = buildAgeHint(ageId, customAge)
|
|
1267
|
+
if (h) hints.push(h)
|
|
1268
|
+
continue
|
|
1269
|
+
}
|
|
1270
|
+
if (dimension === "ethnicity") {
|
|
1271
|
+
const h = buildEthnicityHint(raw)
|
|
1272
|
+
if (h) hints.push(h)
|
|
1273
|
+
continue
|
|
1274
|
+
}
|
|
1275
|
+
if (dimension === "hair-color") {
|
|
1276
|
+
const h = buildHairColorHint(raw)
|
|
1277
|
+
if (h) hints.push(h)
|
|
1278
|
+
continue
|
|
1279
|
+
}
|
|
1280
|
+
if (dimension === "eye-color") {
|
|
1281
|
+
for (const h of buildEyeColorHints(raw)) hints.push(h)
|
|
1282
|
+
continue
|
|
1283
|
+
}
|
|
1284
|
+
if (
|
|
1285
|
+
dimension === "distinctive-features" ||
|
|
1286
|
+
dimension === "lip-state" ||
|
|
1287
|
+
dimension === "eye-state" ||
|
|
1288
|
+
dimension === "skin-texture" ||
|
|
1289
|
+
dimension === "regional-aesthetic"
|
|
1290
|
+
) {
|
|
1291
|
+
for (const h of emitIndependentHints(raw)) hints.push(h)
|
|
1292
|
+
continue
|
|
1293
|
+
}
|
|
1294
|
+
if (typeof raw !== "string" || raw.length === 0) continue
|
|
1295
|
+
const hint = getPersonPromptHint(raw)
|
|
1296
|
+
if (hint) hints.push(hint)
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// Legacy fallback: the old combined `lips` dimension was split into
|
|
1300
|
+
// `lip-fullness` + `lip-shape`. Saved workflow data may still carry a single
|
|
1301
|
+
// `data.lips` id; emit its hint, but ONLY when neither new field is set so we
|
|
1302
|
+
// never double-count after a migration. (The relocated lip ids remain in the
|
|
1303
|
+
// catalog, so `getPersonPromptHint` resolves them identically.)
|
|
1304
|
+
if (
|
|
1305
|
+
typeof data.lips === "string" &&
|
|
1306
|
+
data.lips.length > 0 &&
|
|
1307
|
+
!data.lipFullness &&
|
|
1308
|
+
!data.lipShape
|
|
1309
|
+
) {
|
|
1310
|
+
const legacyLips = getPersonPromptHint(data.lips)
|
|
1311
|
+
if (legacyLips) hints.push(legacyLips)
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
const post = typeof data.postText === "string" ? data.postText.trim() : ""
|
|
1315
|
+
if (post) hints.push(post)
|
|
1316
|
+
|
|
1317
|
+
return hints
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// Legacy → new field relocation tables for `migratePersonValue`. The old
|
|
1321
|
+
// single `eye-shape` dimension crammed shape + lid + tilt + spacing into one
|
|
1322
|
+
// pick, and `lips` crammed fullness + shape; both were split. Ids were kept
|
|
1323
|
+
// stable so prompt output is identical either way — this only moves a stored
|
|
1324
|
+
// value onto the field the picker now renders it under.
|
|
1325
|
+
const PERSON_EYELID_RELOCATE: ReadonlySet<string> = new Set(["eye-hooded", "eye-droopy", "eye-deep-set"])
|
|
1326
|
+
const PERSON_CANTHAL_RELOCATE: ReadonlySet<string> = new Set(["eye-upturned", "eye-downturned"])
|
|
1327
|
+
const PERSON_SPACING_RELOCATE: ReadonlySet<string> = new Set(["eye-close-set", "eye-wide-set"])
|
|
1328
|
+
const PERSON_LIP_FULLNESS_RELOCATE: ReadonlySet<string> = new Set(["lips-thin", "lips-medium", "lips-full"])
|
|
1329
|
+
const PERSON_LIP_SHAPE_RELOCATE: ReadonlySet<string> = new Set(["lips-wide", "lips-cupids-bow", "lips-small"])
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* Relocate legacy single-field Person values onto the post-split fields so the
|
|
1333
|
+
* picker UI shows them in their new home:
|
|
1334
|
+
*
|
|
1335
|
+
* - `eyeShape` ∈ {hooded, droopy, deep-set} → `eyelidType`
|
|
1336
|
+
* - `eyeShape` ∈ {upturned, downturned} → `canthalTilt`
|
|
1337
|
+
* - `eyeShape` ∈ {wide-set, close-set} → `eyeSpacing`
|
|
1338
|
+
* - `lips` ∈ {thin, medium, full} → `lipFullness`
|
|
1339
|
+
* - `lips` ∈ {wide, cupids-bow, small} → `lipShape`
|
|
1340
|
+
*
|
|
1341
|
+
* PROMPT OUTPUT IS UNAFFECTED either way — `getPersonPromptHint` resolves the
|
|
1342
|
+
* ids regardless of dimension and `buildPersonHints` has a `lips` fallback;
|
|
1343
|
+
* this is UI hygiene + clean re-saves only. Immutable + idempotent: returns the
|
|
1344
|
+
* same reference when nothing moves, never overwrites an already-set target,
|
|
1345
|
+
* clears the legacy source once relocated, and preserves every other key (node
|
|
1346
|
+
* data carries non-Person fields too).
|
|
1347
|
+
*/
|
|
1348
|
+
export function migratePersonValue<T extends Record<string, unknown>>(data: T): T {
|
|
1349
|
+
let next: Record<string, unknown> | undefined
|
|
1350
|
+
const relocate = (from: "eyeShape" | "lips", to: string, value: string) => {
|
|
1351
|
+
next = { ...(next ?? data), [to]: value, [from]: undefined }
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
const eyeShape = data.eyeShape
|
|
1355
|
+
if (typeof eyeShape === "string" && eyeShape.length > 0) {
|
|
1356
|
+
if (PERSON_EYELID_RELOCATE.has(eyeShape) && !data.eyelidType) relocate("eyeShape", "eyelidType", eyeShape)
|
|
1357
|
+
else if (PERSON_CANTHAL_RELOCATE.has(eyeShape) && !data.canthalTilt) relocate("eyeShape", "canthalTilt", eyeShape)
|
|
1358
|
+
else if (PERSON_SPACING_RELOCATE.has(eyeShape) && !data.eyeSpacing) relocate("eyeShape", "eyeSpacing", eyeShape)
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const lips = data.lips
|
|
1362
|
+
if (typeof lips === "string" && lips.length > 0) {
|
|
1363
|
+
if (PERSON_LIP_FULLNESS_RELOCATE.has(lips) && !data.lipFullness) relocate("lips", "lipFullness", lips)
|
|
1364
|
+
else if (PERSON_LIP_SHAPE_RELOCATE.has(lips) && !data.lipShape) relocate("lips", "lipShape", lips)
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
return (next ?? data) as T
|
|
1368
|
+
}
|