@nodaro/prompts 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +243 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -3
- package/dist/index.d.ts +149 -3
- package/dist/index.js +239 -70
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/assemble-image-input.test.ts +30 -0
- package/src/__tests__/described-references-video.test.ts +250 -0
- package/src/__tests__/described-references.test.ts +595 -0
- package/src/__tests__/doctrine-roster-completeness.test.ts +1 -0
- package/src/__tests__/fixtures/parameter-hint-golden.json +313 -35
- package/src/__tests__/minor-age-floor.test.ts +4 -4
- package/src/__tests__/person-body-axes.test.ts +2 -2
- package/src/__tests__/person-exposure-hints.test.ts +59 -4
- package/src/__tests__/retired-hint-strings.test.ts +68 -0
- package/src/__tests__/subject-registry.test.ts +1 -1
- package/src/__tests__/w1b-rephrase.test.ts +119 -0
- package/src/age-floor.ts +65 -2
- package/src/assemble-image-input.ts +11 -1
- package/src/described-references.ts +138 -0
- package/src/index.ts +1 -0
- package/src/person.ts +18 -17
- package/src/pose.ts +1 -1
- package/src/prompt-builder.ts +196 -19
- package/src/styling.ts +38 -2
- package/src/video-reference-resolver.ts +85 -10
package/dist/index.cjs
CHANGED
|
@@ -2719,9 +2719,9 @@ var PEOPLE = [
|
|
|
2719
2719
|
{ id: "bust-small", label: "Small", dimension: "bust", description: "Small bust", promptHint: "small bust", term: "small bust", adultOnly: true },
|
|
2720
2720
|
{ id: "bust-average", label: "Average", dimension: "bust", description: "Average bust", promptHint: "" },
|
|
2721
2721
|
{ id: "bust-full", label: "Full", dimension: "bust", description: "Full bust", promptHint: "full bust", term: "full bust", adultOnly: true },
|
|
2722
|
-
{ id: "bust-very-full", label: "Very Full", dimension: "bust", description: "Very full bust", promptHint: "
|
|
2722
|
+
{ id: "bust-very-full", label: "Very Full", dimension: "bust", description: "Very full bust", promptHint: "a fuller bust", term: "fuller bust", adultOnly: true },
|
|
2723
2723
|
// ----- Waist -----
|
|
2724
|
-
{ id: "waist-defined", label: "Defined", dimension: "waist", description: "Defined, cinched waist", promptHint: "defined
|
|
2724
|
+
{ id: "waist-defined", label: "Defined", dimension: "waist", description: "Defined, cinched waist", promptHint: "a defined waistline", term: "defined waistline", adultOnly: true },
|
|
2725
2725
|
{ id: "waist-average", label: "Average", dimension: "waist", description: "Average waist", promptHint: "" },
|
|
2726
2726
|
{ id: "waist-straight", label: "Straight", dimension: "waist", description: "Straight, undefined waist", promptHint: "straight waistline", term: "straight waistline", adultOnly: true },
|
|
2727
2727
|
// ----- Hips -----
|
|
@@ -2729,7 +2729,7 @@ var PEOPLE = [
|
|
|
2729
2729
|
{ id: "hips-balanced", label: "Balanced", dimension: "hips", description: "Balanced hips", promptHint: "" },
|
|
2730
2730
|
{ id: "hips-wide", label: "Wide", dimension: "hips", description: "Wide hips", promptHint: "wide hips", term: "wide hips", adultOnly: true },
|
|
2731
2731
|
// ----- Silhouette (overall body shape; optional, no neutral) -----
|
|
2732
|
-
{ id: "silhouette-hourglass", label: "Hourglass", dimension: "silhouette", description: "Balanced bust + hips, defined waist", promptHint: "hourglass
|
|
2732
|
+
{ id: "silhouette-hourglass", label: "Hourglass", dimension: "silhouette", description: "Balanced bust + hips, defined waist", promptHint: "an hourglass figure", term: "hourglass figure", adultOnly: true },
|
|
2733
2733
|
{ id: "silhouette-rectangular", label: "Rectangular", dimension: "silhouette", description: "Straight up-and-down silhouette", promptHint: "rectangular silhouette", term: "rectangular silhouette" },
|
|
2734
2734
|
{ id: "silhouette-pear", label: "Pear", dimension: "silhouette", description: "Hips wider than shoulders", promptHint: "pear-shaped silhouette", term: "pear-shaped silhouette", adultOnly: true },
|
|
2735
2735
|
{ id: "silhouette-inverted", label: "Inverted Triangle", dimension: "silhouette", description: "Broad shoulders, narrow hips", promptHint: "inverted-triangle silhouette", term: "inverted-triangle silhouette" },
|
|
@@ -2818,11 +2818,11 @@ var PEOPLE = [
|
|
|
2818
2818
|
{ id: "lips-small", label: "Small", dimension: "lip-shape", description: "Petite mouth", promptHint: "a small petite mouth", term: "small petite mouth" },
|
|
2819
2819
|
// -------------------- Lip State (what the lips are doing / wearing) --------------------
|
|
2820
2820
|
{ id: "lip-state-chapped", label: "Chapped", dimension: "lip-state", description: "Cracked, dry, weather-worn lips", promptHint: "with chapped, cracked, weather-worn dry lips", term: "chapped dry lips" },
|
|
2821
|
-
{ id: "lip-state-glossy", label: "Glossy", dimension: "lip-state", description: "High-shine, wet-look lips", promptHint: "with
|
|
2821
|
+
{ id: "lip-state-glossy", label: "Glossy", dimension: "lip-state", description: "High-shine, wet-look lips", promptHint: "with a glossy lip finish", term: "glossy lips", adultOnly: true },
|
|
2822
2822
|
{ id: "lip-state-bare", label: "Bare", dimension: "lip-state", description: "Natural, untreated, no makeup", promptHint: "with bare, natural, untreated lips", term: "bare natural lips" },
|
|
2823
2823
|
{ id: "lip-state-bold-red", label: "Bold Red", dimension: "lip-state", description: "Saturated red lipstick statement", promptHint: "with a bold, saturated red lipstick statement", term: "bold red lipstick" },
|
|
2824
|
-
{ id: "lip-state-bitten", label: "Bitten", dimension: "lip-state", description: "Slight playful lip-bite, lower lip caught", promptHint: "
|
|
2825
|
-
{ id: "lip-state-parted", label: "Parted", dimension: "lip-state", description: "Lips slightly parted, breath of air", promptHint: "with lips slightly
|
|
2824
|
+
{ id: "lip-state-bitten", label: "Bitten", dimension: "lip-state", description: "Slight playful lip-bite, lower lip caught", promptHint: "lightly biting the lower lip", term: "biting lower lip", adultOnly: true },
|
|
2825
|
+
{ id: "lip-state-parted", label: "Parted", dimension: "lip-state", description: "Lips slightly parted, breath of air", promptHint: "with lips relaxed and slightly open, as if mid-sentence", term: "lips slightly open", adultOnly: true },
|
|
2826
2826
|
{ id: "lip-state-pursed", label: "Pursed", dimension: "lip-state", description: "Lips pressed and pushed forward", promptHint: "with lips pursed, pressed and pushed forward", term: "pursed lips" },
|
|
2827
2827
|
{ 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", term: "bold black lipstick" },
|
|
2828
2828
|
{ id: "lip-state-burgundy", label: "Burgundy", dimension: "lip-state", description: "Deep wine-red lipstick", promptHint: "with deep wine-red burgundy lipstick", term: "burgundy lipstick" },
|
|
@@ -2935,9 +2935,9 @@ var PEOPLE = [
|
|
|
2935
2935
|
{ 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", term: "jet-black eyes" },
|
|
2936
2936
|
// -------------------- Eye State (what the eyes are doing / where they look) --------------------
|
|
2937
2937
|
{ id: "eye-state-closed", label: "Closed", dimension: "eye-state", description: "Eyes fully closed, peaceful", promptHint: "with eyes fully closed in a peaceful expression", term: "eyes fully closed" },
|
|
2938
|
-
{ id: "eye-state-half-lidded", label: "Half-lidded", dimension: "eye-state", description: "Heavy-lidded sleepy gaze", promptHint: "with
|
|
2938
|
+
{ id: "eye-state-half-lidded", label: "Half-lidded", dimension: "eye-state", description: "Heavy-lidded sleepy gaze", promptHint: "with drowsy, partly closed eyes, the lids sitting low over the iris", term: "drowsy, partly closed eyes", adultOnly: true },
|
|
2939
2939
|
{ 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" },
|
|
2940
|
-
{ id: "eye-state-staring-camera", label: "Staring at Camera", dimension: "eye-state", description: "Direct unbroken eye contact with the lens", promptHint: "
|
|
2940
|
+
{ id: "eye-state-staring-camera", label: "Staring at Camera", dimension: "eye-state", description: "Direct unbroken eye contact with the lens", promptHint: "looking directly into the camera", term: "direct gaze to camera" },
|
|
2941
2941
|
{ id: "eye-state-gazing-away", label: "Gazing Away", dimension: "eye-state", description: "Looking off-camera, contemplative", promptHint: "gazing off-camera with a contemplative expression", term: "gazing off-camera" },
|
|
2942
2942
|
{ 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" },
|
|
2943
2943
|
{ id: "eye-state-gazing-down", label: "Gazing Down", dimension: "eye-state", description: "Eyes downcast", promptHint: "with eyes downcast, gazing softly downward" },
|
|
@@ -2954,8 +2954,8 @@ var PEOPLE = [
|
|
|
2954
2954
|
{ id: "texture-smooth", label: "Smooth", dimension: "skin-texture", description: "Flawless, silky smooth skin", promptHint: "with flawless, silky smooth skin", term: "flawless smooth skin" },
|
|
2955
2955
|
{ id: "texture-wrinkled", label: "Wrinkled", dimension: "skin-texture", description: "Aged, deeply lined skin", promptHint: "with deep wrinkles and aged skin texture", term: "deeply wrinkled aged skin" },
|
|
2956
2956
|
{ id: "texture-goosebumps", label: "Goosebumps", dimension: "skin-texture", description: "Raised goosebumps on skin", promptHint: "with goosebumps raised on the skin" },
|
|
2957
|
-
{ id: "texture-dewy", label: "Dewy", dimension: "skin-texture", description: "Glowing, dewy fresh skin", promptHint: "with dewy,
|
|
2958
|
-
{ id: "texture-glistening", label: "Glistening", dimension: "skin-texture", description: "Sweat or oil sheen", promptHint: "with
|
|
2957
|
+
{ id: "texture-dewy", label: "Dewy", dimension: "skin-texture", description: "Glowing, dewy fresh skin", promptHint: "with dewy, luminous skin", term: "dewy skin" },
|
|
2958
|
+
{ id: "texture-glistening", label: "Glistening", dimension: "skin-texture", description: "Sweat or oil sheen", promptHint: "with a light glistening sheen on the skin", term: "glistening sheen", adultOnly: true },
|
|
2959
2959
|
{ id: "texture-weathered", label: "Weathered", dimension: "skin-texture", description: "Sun-aged rough skin", promptHint: "with weathered, sun-worn rough skin", term: "weathered sun-worn skin" },
|
|
2960
2960
|
{ id: "texture-porcelain", label: "Porcelain", dimension: "skin-texture", description: "Flawless near-translucent porcelain", promptHint: "with flawless, near-translucent porcelain skin", term: "porcelain skin" },
|
|
2961
2961
|
{ 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", term: "sun-kissed skin" },
|
|
@@ -2967,8 +2967,8 @@ var PEOPLE = [
|
|
|
2967
2967
|
{ 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", term: "oily shiny skin", adultOnly: true },
|
|
2968
2968
|
{ id: "texture-matte", label: "Matte", dimension: "skin-texture", description: "Poreless matte finish", promptHint: "with a poreless, matte skin finish", term: "matte skin finish" },
|
|
2969
2969
|
{ id: "texture-blemished", label: "Blemished", dimension: "skin-texture", description: "Visible blemishes, real-skin imperfections", promptHint: "with visible blemishes and natural real-skin imperfections", term: "blemished skin" },
|
|
2970
|
-
{ id: "texture-baby-soft", label: "Baby-soft", dimension: "skin-texture", description: "Smooth, fine-pored youthful skin", promptHint: "with
|
|
2971
|
-
{ id: "texture-shower-fresh-wet", label: "Shower-Fresh Wet", dimension: "skin-texture", description: "Just-out-of-shower wet skin with water beads", promptHint: "with
|
|
2970
|
+
{ id: "texture-baby-soft", label: "Baby-soft", dimension: "skin-texture", description: "Smooth, fine-pored youthful skin", promptHint: "with soft, fine-pored skin", term: "soft fine-pored skin" },
|
|
2971
|
+
{ id: "texture-shower-fresh-wet", label: "Shower-Fresh Wet", dimension: "skin-texture", description: "Just-out-of-shower wet skin with water beads", promptHint: "with fresh, water-dappled skin as if just out of the shower", term: "water-dappled skin", adultOnly: true },
|
|
2972
2972
|
{ id: "texture-acne-scarred", label: "Acne-scarred", dimension: "skin-texture", description: "Visible acne scarring (distinct from blemished \u2014 healed scar pattern)", promptHint: "with visible acne scarring, healed pitted-skin texture and uneven surface from past breakouts", term: "acne-scarred skin" },
|
|
2973
2973
|
// -------------------- Distinctive Features --------------------
|
|
2974
2974
|
{ id: "feature-glasses", label: "Glasses", dimension: "distinctive-features", description: "Wears glasses", promptHint: "wearing glasses", term: "wearing glasses" },
|
|
@@ -2997,9 +2997,9 @@ var PEOPLE = [
|
|
|
2997
2997
|
{ id: "feature-ear-piercings", label: "Ear Piercings", dimension: "distinctive-features", description: "Multiple stacked ear piercings (cartilage, helix, conch)", promptHint: "with multiple stacked ear piercings \u2014 cartilage, helix, and conch", term: "multiple stacked ear piercings" },
|
|
2998
2998
|
{ 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" },
|
|
2999
2999
|
{ 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" },
|
|
3000
|
-
{ id: "feature-bare-shoulders", label: "Bare Shoulders", dimension: "distinctive-features", description: "Bare shoulders exposed", promptHint: "with
|
|
3001
|
-
{ id: "feature-collarbone-visible", label: "Collarbone Visible", dimension: "distinctive-features", description: "Prominent collarbone catching light", promptHint: "with
|
|
3002
|
-
{ id: "feature-midriff-visible", label: "Midriff Visible", dimension: "distinctive-features", description: "Exposed midriff between top and bottom", promptHint: "
|
|
3000
|
+
{ id: "feature-bare-shoulders", label: "Bare Shoulders", dimension: "distinctive-features", description: "Bare shoulders exposed", promptHint: "with the shoulders uncovered", term: "shoulders uncovered", adultOnly: true },
|
|
3001
|
+
{ id: "feature-collarbone-visible", label: "Collarbone Visible", dimension: "distinctive-features", description: "Prominent collarbone catching light", promptHint: "with an open neckline that leaves the collarbones uncovered", term: "open neckline, collarbones uncovered", adultOnly: true },
|
|
3002
|
+
{ id: "feature-midriff-visible", label: "Midriff Visible", dimension: "distinctive-features", description: "Exposed midriff between top and bottom", promptHint: "with a cropped hemline", term: "cropped hemline", adultOnly: true },
|
|
3003
3003
|
{ id: "feature-navel-visible", label: "Navel Visible", dimension: "distinctive-features", description: "Visible navel on bare stomach", promptHint: "with the navel visible", term: "visible navel", adultOnly: true },
|
|
3004
3004
|
{ 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", term: "elongated swan-like neck" },
|
|
3005
3005
|
{ 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", term: "dark under-eye circles" },
|
|
@@ -3326,7 +3326,7 @@ function emitIndependentFragments(value, fragmentFor) {
|
|
|
3326
3326
|
const navel = ids.includes("feature-navel-visible");
|
|
3327
3327
|
for (const id of ids) {
|
|
3328
3328
|
if (midriff && navel && (id === "feature-midriff-visible" || id === "feature-navel-visible")) {
|
|
3329
|
-
if (id === "feature-midriff-visible" && fragmentFor(id) !== "") out.push("
|
|
3329
|
+
if (id === "feature-midriff-visible" && fragmentFor(id) !== "") out.push("with a cropped hemline and the navel showing");
|
|
3330
3330
|
continue;
|
|
3331
3331
|
}
|
|
3332
3332
|
const fragment = fragmentFor(id);
|
|
@@ -3591,7 +3591,7 @@ var POSES = [
|
|
|
3591
3591
|
{ id: "throwing", label: "Throwing", category: "action", description: "Mid-throw motion", promptHint: "caught mid-throw, body coiled and releasing", term: "caught mid-throw" },
|
|
3592
3592
|
{ id: "leaping", label: "Leaping", category: "action", description: "Leaping forward dynamically", promptHint: "leaping forward dynamically with body extended" },
|
|
3593
3593
|
{ id: "dramatic-action", label: "Dramatic Action", category: "action", description: "Exaggerated action pose", promptHint: "in a dramatic, exaggerated action pose full of motion", term: "dramatic exaggerated action pose" },
|
|
3594
|
-
{ id: "biting-lip", label: "Biting Lip", category: "action", description: "Slight playful lip-bite", promptHint: "biting the lower lip
|
|
3594
|
+
{ id: "biting-lip", label: "Biting Lip", category: "action", description: "Slight playful lip-bite", promptHint: "lightly biting the lower lip, a subtle playful expression", term: "biting lower lip", adultOnly: true },
|
|
3595
3595
|
{ id: "mid-laugh", label: "Mid-Laugh", category: "action", description: "Caught mid-laugh, head back", promptHint: "caught mid-laugh with head tipped back, eyes crinkled" },
|
|
3596
3596
|
{ id: "pointing-at-camera", label: "Pointing at Camera", category: "action", description: "Pointing directly at camera", promptHint: "pointing one finger directly at the camera, arm extended", term: "pointing directly at the camera" },
|
|
3597
3597
|
{ id: "tongue-out", label: "Sticking Tongue Out", category: "action", description: "Playful tongue-out expression", promptHint: "sticking the tongue out playfully" },
|
|
@@ -4019,10 +4019,10 @@ var STYLINGS = [
|
|
|
4019
4019
|
{ id: "footwear-espadrilles", label: "Espadrilles", dimension: "footwear", description: "Woven jute-sole canvas summer shoe", promptHint: "in woven jute-sole espadrilles with a canvas upper, a casual summer shoe" },
|
|
4020
4020
|
// -------------------- Wardrobe State (modifier — composes with any garment) --------------------
|
|
4021
4021
|
{ id: "state-oversized", label: "Oversized", dimension: "wardrobe-state", description: "Loose, oversized fit", promptHint: "the clothing loose and oversized, draping freely well past the body", term: "oversized fit" },
|
|
4022
|
-
{ id: "state-fitted", label: "Fitted", dimension: "wardrobe-state", description: "Form-fitting silhouette", promptHint: "
|
|
4022
|
+
{ id: "state-fitted", label: "Fitted", dimension: "wardrobe-state", description: "Form-fitting silhouette", promptHint: "tailored, close-fitting clothing", term: "close-fitting clothing", adultOnly: true },
|
|
4023
4023
|
{ id: "state-cropped", label: "Cropped", dimension: "wardrobe-state", description: "Top cropped at midriff", promptHint: "the top cropped above the midriff with the stomach visible", term: "cropped top", adultOnly: true },
|
|
4024
4024
|
{ id: "state-sheer", label: "Sheer", dimension: "wardrobe-state", description: "Translucent fabric", promptHint: "the fabric translucent and sheer, hinting at the silhouette underneath", term: "sheer clothing", adultOnly: true },
|
|
4025
|
-
{ id: "state-wet", label: "Wet", dimension: "wardrobe-state", description: "Soaked, water-clinging", promptHint: "the clothing soaked
|
|
4025
|
+
{ id: "state-wet", label: "Wet", dimension: "wardrobe-state", description: "Soaked, water-clinging", promptHint: "the clothing soaked through and dripping", term: "soaked clothing", adultOnly: true },
|
|
4026
4026
|
{ id: "state-ripped", label: "Ripped", dimension: "wardrobe-state", description: "Torn and frayed", promptHint: "the fabric torn and ripped at the seams, with frayed edges", term: "ripped clothing" },
|
|
4027
4027
|
{ id: "state-distressed", label: "Distressed", dimension: "wardrobe-state", description: "Worn, faded, weathered", promptHint: "the clothing distressed and weathered, with faded color and worn-down edges", term: "distressed clothing" },
|
|
4028
4028
|
{ id: "state-vintage", label: "Vintage", dimension: "wardrobe-state", description: "Worn, retro character", promptHint: "the clothing carrying vintage character \u2014 softened color, broken-in fit, retro silhouette", term: "vintage clothing" },
|
|
@@ -4132,16 +4132,23 @@ function collectStylingFragments(data, fragmentFor) {
|
|
|
4132
4132
|
const boldRedAlready = Array.isArray(lipStateRaw) ? lipStateRaw.includes("lip-state-bold-red") : lipStateRaw === "lip-state-bold-red";
|
|
4133
4133
|
const floored = isMinorAge(data);
|
|
4134
4134
|
const emit = floored ? (id) => getStyling(id)?.adultOnly === true ? "" : fragmentFor(id) : fragmentFor;
|
|
4135
|
+
const featureRaw = data.distinctiveFeature;
|
|
4136
|
+
const midriffPicked = Array.isArray(featureRaw) ? featureRaw.includes("feature-midriff-visible") : featureRaw === "feature-midriff-visible";
|
|
4137
|
+
const midriffAlready = midriffPicked && !floored;
|
|
4138
|
+
const cropTopPicked = data.top === "top-crop-top";
|
|
4139
|
+
const croppedSuppressed = (id) => id === "top-crop-top" && midriffAlready || id === "state-cropped" && (midriffAlready || cropTopPicked);
|
|
4135
4140
|
for (const dimension of STYLING_DIMENSION_ORDER) {
|
|
4136
4141
|
const field = STYLING_FIELD_BY_DIMENSION[dimension];
|
|
4137
4142
|
const raw = data[field];
|
|
4138
4143
|
if (dimension === "makeup" && boldRedAlready && raw === "makeup-bold-lips") continue;
|
|
4139
4144
|
if (typeof raw === "string" && raw.length > 0) {
|
|
4145
|
+
if (croppedSuppressed(raw)) continue;
|
|
4140
4146
|
const fragment = emit(raw);
|
|
4141
4147
|
if (fragment) out.push(fragment);
|
|
4142
4148
|
} else if (Array.isArray(raw)) {
|
|
4143
4149
|
for (const item of raw) {
|
|
4144
4150
|
if (typeof item !== "string" || item.length === 0) continue;
|
|
4151
|
+
if (croppedSuppressed(item)) continue;
|
|
4145
4152
|
const fragment = emit(item);
|
|
4146
4153
|
if (fragment) out.push(fragment);
|
|
4147
4154
|
}
|
|
@@ -10337,32 +10344,6 @@ function resolveBrandInput(brand) {
|
|
|
10337
10344
|
return brand;
|
|
10338
10345
|
}
|
|
10339
10346
|
|
|
10340
|
-
// src/prompt-templates.ts
|
|
10341
|
-
var DEFAULT_TEMPLATES = {
|
|
10342
|
-
"character-description": "Include character '{name}': {description}.",
|
|
10343
|
-
"object-description": "Include object '{name}': {description}.",
|
|
10344
|
-
"location-description": "Include location '{name}': {description}.",
|
|
10345
|
-
"face-description": "Include the exact face and facial features of '{name}' from the reference image. Maintain perfect likeness and facial identity.",
|
|
10346
|
-
// `-generation` templates drive standalone entity generation routes
|
|
10347
|
-
// (`/v1/generate-character`, `/v1/generate-face`, etc.). Duplicated between
|
|
10348
|
-
// frontend and backend historically; consolidated here so the backend DAG
|
|
10349
|
-
// orchestrator produces the same prompt as a single-node HTTP call.
|
|
10350
|
-
"character-generation": "Create a full-body character portrait: {description}. Style: {style}. Gender: {gender}. High quality, detailed, consistent lighting, neutral background.",
|
|
10351
|
-
"object-generation": "Create a product photo of: {description}. Category: {category}. Clean background, professional studio lighting, high detail.",
|
|
10352
|
-
"location-generation": "Create a cinematic scene of: {description}. Category: {category}. Atmospheric lighting, high detail, wide angle.",
|
|
10353
|
-
"face-generation": "Create a professional close-up face portrait headshot: {description}. Style: {style}. Looking directly at camera, sharp focus on facial features, clean background, studio lighting, high resolution. Maintain exact facial identity and features from the reference image.",
|
|
10354
|
-
"generate-image-wrapper": "{userPrompt}\n{assetDescriptions}"
|
|
10355
|
-
};
|
|
10356
|
-
function resolveTemplate(key, userTemplates, flowTemplates) {
|
|
10357
|
-
return flowTemplates?.[key] ?? userTemplates?.[key] ?? DEFAULT_TEMPLATES[key] ?? "";
|
|
10358
|
-
}
|
|
10359
|
-
function applyTemplate(template, vars) {
|
|
10360
|
-
return Object.entries(vars).reduce(
|
|
10361
|
-
(result, [key, value]) => result.replaceAll(`{${key}}`, value || ""),
|
|
10362
|
-
template
|
|
10363
|
-
);
|
|
10364
|
-
}
|
|
10365
|
-
|
|
10366
10347
|
// src/prompt-hint-join.ts
|
|
10367
10348
|
var PROMPT_HINT_SEPARATOR = ". ";
|
|
10368
10349
|
function joinPromptHints(userPrompt, hints) {
|
|
@@ -10574,6 +10555,87 @@ function sectionedClauseCosts(userPrompt, clauses, structuredFragment) {
|
|
|
10574
10555
|
}
|
|
10575
10556
|
return costs;
|
|
10576
10557
|
}
|
|
10558
|
+
|
|
10559
|
+
// src/described-references.ts
|
|
10560
|
+
var CHARACTER_BLOCK_HEADER = "Use these characters:";
|
|
10561
|
+
function referenceDescriptionLine(subject, description) {
|
|
10562
|
+
const s = subject.trim();
|
|
10563
|
+
const d = description?.trim();
|
|
10564
|
+
if (!s || !d) return "";
|
|
10565
|
+
return `${s} \u2014 ${d}.`;
|
|
10566
|
+
}
|
|
10567
|
+
function renderDescribedReferenceLines(refs) {
|
|
10568
|
+
if (!refs || refs.length === 0) return [];
|
|
10569
|
+
const lines = [];
|
|
10570
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10571
|
+
for (const r of refs) {
|
|
10572
|
+
const key = r.name?.trim().toLowerCase();
|
|
10573
|
+
if (!key || seen.has(key)) continue;
|
|
10574
|
+
const line = referenceDescriptionLine(r.name, r.description);
|
|
10575
|
+
if (!line) continue;
|
|
10576
|
+
seen.add(key);
|
|
10577
|
+
lines.push(line);
|
|
10578
|
+
}
|
|
10579
|
+
return lines;
|
|
10580
|
+
}
|
|
10581
|
+
function renderReferenceCaptionLines(videoCaptions, audioCaptions, counts) {
|
|
10582
|
+
const lines = [];
|
|
10583
|
+
const render = (captions, kind, cap) => {
|
|
10584
|
+
if (!captions) return;
|
|
10585
|
+
for (let i = 0; i < captions.length && i < cap; i++) {
|
|
10586
|
+
const text = captions[i]?.trim();
|
|
10587
|
+
if (!text) continue;
|
|
10588
|
+
lines.push(`@${kind}_${i + 1}: ${text}.`);
|
|
10589
|
+
}
|
|
10590
|
+
};
|
|
10591
|
+
render(videoCaptions, "video", counts.video);
|
|
10592
|
+
render(audioCaptions, "audio", counts.audio);
|
|
10593
|
+
return lines;
|
|
10594
|
+
}
|
|
10595
|
+
function appendReferenceLines(prompt, lines, format) {
|
|
10596
|
+
if (lines.length === 0) return prompt;
|
|
10597
|
+
if (format === "hybrid") return insertBeforeStyleSection(prompt, lines);
|
|
10598
|
+
const bullets = lines.map((l) => `- ${l}`).join("\n");
|
|
10599
|
+
if (prompt.startsWith(`${CHARACTER_BLOCK_HEADER}
|
|
10600
|
+
`)) {
|
|
10601
|
+
const splitIdx = prompt.indexOf("\n\n");
|
|
10602
|
+
if (splitIdx === -1) return `${prompt}
|
|
10603
|
+
${bullets}`;
|
|
10604
|
+
return `${prompt.slice(0, splitIdx)}
|
|
10605
|
+
${bullets}${prompt.slice(splitIdx)}`;
|
|
10606
|
+
}
|
|
10607
|
+
const block = `${CHARACTER_BLOCK_HEADER}
|
|
10608
|
+
${bullets}`;
|
|
10609
|
+
return prompt ? `${block}
|
|
10610
|
+
|
|
10611
|
+
${prompt}` : block;
|
|
10612
|
+
}
|
|
10613
|
+
|
|
10614
|
+
// src/prompt-templates.ts
|
|
10615
|
+
var DEFAULT_TEMPLATES = {
|
|
10616
|
+
"character-description": "Include character '{name}': {description}.",
|
|
10617
|
+
"object-description": "Include object '{name}': {description}.",
|
|
10618
|
+
"location-description": "Include location '{name}': {description}.",
|
|
10619
|
+
"face-description": "Include the exact face and facial features of '{name}' from the reference image. Maintain perfect likeness and facial identity.",
|
|
10620
|
+
// `-generation` templates drive standalone entity generation routes
|
|
10621
|
+
// (`/v1/generate-character`, `/v1/generate-face`, etc.). Duplicated between
|
|
10622
|
+
// frontend and backend historically; consolidated here so the backend DAG
|
|
10623
|
+
// orchestrator produces the same prompt as a single-node HTTP call.
|
|
10624
|
+
"character-generation": "Create a full-body character portrait: {description}. Style: {style}. Gender: {gender}. High quality, detailed, consistent lighting, neutral background.",
|
|
10625
|
+
"object-generation": "Create a product photo of: {description}. Category: {category}. Clean background, professional studio lighting, high detail.",
|
|
10626
|
+
"location-generation": "Create a cinematic scene of: {description}. Category: {category}. Atmospheric lighting, high detail, wide angle.",
|
|
10627
|
+
"face-generation": "Create a professional close-up face portrait headshot: {description}. Style: {style}. Looking directly at camera, sharp focus on facial features, clean background, studio lighting, high resolution. Maintain exact facial identity and features from the reference image.",
|
|
10628
|
+
"generate-image-wrapper": "{userPrompt}\n{assetDescriptions}"
|
|
10629
|
+
};
|
|
10630
|
+
function resolveTemplate(key, userTemplates, flowTemplates) {
|
|
10631
|
+
return flowTemplates?.[key] ?? userTemplates?.[key] ?? DEFAULT_TEMPLATES[key] ?? "";
|
|
10632
|
+
}
|
|
10633
|
+
function applyTemplate(template, vars) {
|
|
10634
|
+
return Object.entries(vars).reduce(
|
|
10635
|
+
(result, [key, value]) => result.replaceAll(`{${key}}`, value || ""),
|
|
10636
|
+
template
|
|
10637
|
+
);
|
|
10638
|
+
}
|
|
10577
10639
|
function composeIdentityDescPart(subject, canonicalDesc, elementInjection) {
|
|
10578
10640
|
const parts = [];
|
|
10579
10641
|
const c = canonicalDesc?.trim();
|
|
@@ -10582,6 +10644,10 @@ function composeIdentityDescPart(subject, canonicalDesc, elementInjection) {
|
|
|
10582
10644
|
if (e) parts.push(e);
|
|
10583
10645
|
return parts.length > 0 ? `${subject} \u2014 ${parts.join(". ")}` : subject;
|
|
10584
10646
|
}
|
|
10647
|
+
function pushOverrideDirective(out, ref, binding) {
|
|
10648
|
+
const line = referenceDescriptionLine(binding, ref.descriptionOverride);
|
|
10649
|
+
if (line) out.push(line);
|
|
10650
|
+
}
|
|
10585
10651
|
function resolveCharacterMentions(prompt, tokens, refs) {
|
|
10586
10652
|
const bySlug = /* @__PURE__ */ new Map();
|
|
10587
10653
|
const byVariant = /* @__PURE__ */ new Map();
|
|
@@ -10622,7 +10688,7 @@ function resolveCharacterMentions(prompt, tokens, refs) {
|
|
|
10622
10688
|
const includeCanonicalDesc = effectiveMode === "identical" || effectiveMode === "face-pose";
|
|
10623
10689
|
const descPart = composeIdentityDescPart(
|
|
10624
10690
|
subject,
|
|
10625
|
-
includeCanonicalDesc ? match.characterCanonicalDescription : void 0,
|
|
10691
|
+
match.descriptionOverride?.trim() || (includeCanonicalDesc ? match.characterCanonicalDescription : void 0),
|
|
10626
10692
|
match.elementInjection
|
|
10627
10693
|
);
|
|
10628
10694
|
directiveLines.push(`- ${descPart}.${directive ? ` ${directive}` : ""}`);
|
|
@@ -10700,6 +10766,7 @@ function resolveCharacterMentionsHybrid(prompt, tokens, refs, existingUrls) {
|
|
|
10700
10766
|
const binding = bindingFor(m.url);
|
|
10701
10767
|
const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding);
|
|
10702
10768
|
if (lock) lockLines.push(lock);
|
|
10769
|
+
pushOverrideDirective(elementDirectives, ref, binding);
|
|
10703
10770
|
const inject = ref.elementInjection?.trim();
|
|
10704
10771
|
if (inject) elementDirectives.push(inject);
|
|
10705
10772
|
}
|
|
@@ -10773,7 +10840,8 @@ function resolveLocationMentions(prompt, tokens, refs) {
|
|
|
10773
10840
|
const subject = `Image ${t.imageIndex} (${displayName})`;
|
|
10774
10841
|
const includeCanonicalDesc = effectiveMode === "identical";
|
|
10775
10842
|
const canonicalDesc = match.locationCanonicalDescription?.trim();
|
|
10776
|
-
const
|
|
10843
|
+
const shownDesc = match.descriptionOverride?.trim() || (includeCanonicalDesc ? canonicalDesc : void 0);
|
|
10844
|
+
const descPart = shownDesc ? `${subject} \u2014 ${shownDesc}` : subject;
|
|
10777
10845
|
directiveLines.push(`- ${descPart}.${directive ? ` ${directive}` : ""}`);
|
|
10778
10846
|
if (t.bucket && t.variant && match.locationVariantDisplayName && match.locationVariantDisplayName !== "canonical") {
|
|
10779
10847
|
directiveLines.push(` (in this image: ${match.locationVariantDisplayName})`);
|
|
@@ -10841,6 +10909,7 @@ function resolveLocationMentionsHybrid(prompt, tokens, refs, existingUrls) {
|
|
|
10841
10909
|
const binding = bindingFor(m.url);
|
|
10842
10910
|
const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding);
|
|
10843
10911
|
if (lock) lockLines.push(lock);
|
|
10912
|
+
pushOverrideDirective(elementDirectives, ref, binding);
|
|
10844
10913
|
const inject = ref.elementInjection?.trim();
|
|
10845
10914
|
if (inject) elementDirectives.push(inject);
|
|
10846
10915
|
}
|
|
@@ -10890,10 +10959,11 @@ function resolveImageMentionsHybrid(prompt, tokens, refs, existingUrls) {
|
|
|
10890
10959
|
const binding = bindingFor(m.url);
|
|
10891
10960
|
const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding);
|
|
10892
10961
|
if (lock) lockLines.push(lock);
|
|
10962
|
+
pushOverrideDirective(elementDirectives, ref, binding);
|
|
10893
10963
|
const inject = ref.elementInjection?.trim();
|
|
10894
10964
|
if (inject) elementDirectives.push(inject);
|
|
10895
10965
|
}
|
|
10896
|
-
return { prompt: resolvedPrompt, additionalUrls, lockLines, elementDirectives };
|
|
10966
|
+
return { prompt: resolvedPrompt, additionalUrls, mentionedUrls: seenUrls, lockLines, elementDirectives };
|
|
10897
10967
|
}
|
|
10898
10968
|
function resolveEntityMentionsHybrid(prompt, tokens, refs, existingUrls) {
|
|
10899
10969
|
const bySlug = /* @__PURE__ */ new Map();
|
|
@@ -10944,6 +11014,7 @@ function resolveEntityMentionsHybrid(prompt, tokens, refs, existingUrls) {
|
|
|
10944
11014
|
const binding = bindingFor(m.url);
|
|
10945
11015
|
const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding);
|
|
10946
11016
|
if (lock) lockLines.push(lock);
|
|
11017
|
+
pushOverrideDirective(elementDirectives, ref, binding);
|
|
10947
11018
|
const inject = ref.elementInjection?.trim();
|
|
10948
11019
|
if (inject) elementDirectives.push(inject);
|
|
10949
11020
|
}
|
|
@@ -10979,7 +11050,7 @@ function buildCanonicalFallback(refs, mentionedSlugs, startIndex) {
|
|
|
10979
11050
|
const subject = `Image ${cursor} (${displayName})`;
|
|
10980
11051
|
const descPart = composeIdentityDescPart(
|
|
10981
11052
|
subject,
|
|
10982
|
-
includeCanonicalDesc ? r.characterCanonicalDescription : void 0,
|
|
11053
|
+
r.descriptionOverride?.trim() || (includeCanonicalDesc ? r.characterCanonicalDescription : void 0),
|
|
10983
11054
|
r.elementInjection
|
|
10984
11055
|
);
|
|
10985
11056
|
directiveLines.push(`- ${descPart}.${directive ? ` ${directive}` : ""}`);
|
|
@@ -10995,7 +11066,7 @@ function buildExtraRefDirectives(refs, emittedCharacterPositions, startIndex) {
|
|
|
10995
11066
|
for (const r of refs) {
|
|
10996
11067
|
if (!r.isExtraRef) continue;
|
|
10997
11068
|
if (!r.url) continue;
|
|
10998
|
-
const description = (r.description ?? r.variantDescription ?? "").trim();
|
|
11069
|
+
const description = (r.descriptionOverride ?? "").trim() || (r.description ?? r.variantDescription ?? "").trim();
|
|
10999
11070
|
if (r.source === "wired-character" && r.characterSlug) {
|
|
11000
11071
|
const effectiveMode = r.defaultUsageMode ?? shared.DEFAULT_USAGE_MODE;
|
|
11001
11072
|
const earlierPos = positionsByChar.get(r.characterSlug);
|
|
@@ -11059,6 +11130,7 @@ function renderCanonicalFallbackHybrid(refs, letterForUrl) {
|
|
|
11059
11130
|
phrases.push(shared.roleToPhrase(shared.resolveDefaultRole(r.defaultRole, r.defaultUsageMode, r.source), binding));
|
|
11060
11131
|
const lock = buildIdentityLockLine(r, binding);
|
|
11061
11132
|
if (lock) lockLines.push(lock);
|
|
11133
|
+
pushOverrideDirective(elementDirectives, r, binding);
|
|
11062
11134
|
const inject = r.elementInjection?.trim();
|
|
11063
11135
|
if (inject) elementDirectives.push(inject);
|
|
11064
11136
|
}
|
|
@@ -11073,7 +11145,7 @@ function renderExtraRefsHybrid(extraRefs, letterForUrl, seedLetterByChar) {
|
|
|
11073
11145
|
if (!r.url) continue;
|
|
11074
11146
|
const letter = letterForUrl(r.url);
|
|
11075
11147
|
const binding = `reference image ${letter}`;
|
|
11076
|
-
const description = (r.description ?? r.variantDescription ?? "").trim();
|
|
11148
|
+
const description = (r.descriptionOverride ?? "").trim() || (r.description ?? r.variantDescription ?? "").trim();
|
|
11077
11149
|
if (r.source === "wired-character" && r.characterSlug) {
|
|
11078
11150
|
const earlier = firstLetterByChar.get(r.characterSlug);
|
|
11079
11151
|
if (earlier !== void 0 && earlier !== letter) {
|
|
@@ -11110,10 +11182,11 @@ function renderLocationCanonicalHybrid(nonCharacterRefs, finalIndexByUrl, covere
|
|
|
11110
11182
|
phrases.push(shared.roleToPhrase(resolveLocationRole(null, null, r), binding));
|
|
11111
11183
|
const lock = buildIdentityLockLine(r, binding);
|
|
11112
11184
|
if (lock) lockLines.push(lock);
|
|
11185
|
+
pushOverrideDirective(elementDirectives, r, binding);
|
|
11113
11186
|
const inject = r.elementInjection?.trim();
|
|
11114
11187
|
if (inject) elementDirectives.push(inject);
|
|
11115
11188
|
}
|
|
11116
|
-
return { phrases, lockLines, elementDirectives };
|
|
11189
|
+
return { phrases, lockLines, elementDirectives, renderedUrls: seenUrls };
|
|
11117
11190
|
}
|
|
11118
11191
|
function renderObjectCreatureCanonicalHybrid(nonCharacterRefs, finalIndexByUrl, coveredUrls) {
|
|
11119
11192
|
const phrases = [];
|
|
@@ -11130,10 +11203,24 @@ function renderObjectCreatureCanonicalHybrid(nonCharacterRefs, finalIndexByUrl,
|
|
|
11130
11203
|
phrases.push(shared.roleToPhrase(shared.defaultRoleForSource(r.source), binding));
|
|
11131
11204
|
const lock = buildIdentityLockLine(r, binding);
|
|
11132
11205
|
if (lock) lockLines.push(lock);
|
|
11206
|
+
pushOverrideDirective(elementDirectives, r, binding);
|
|
11133
11207
|
const inject = r.elementInjection?.trim();
|
|
11134
11208
|
if (inject) elementDirectives.push(inject);
|
|
11135
11209
|
}
|
|
11136
|
-
return { phrases, lockLines, elementDirectives };
|
|
11210
|
+
return { phrases, lockLines, elementDirectives, renderedUrls: seenUrls };
|
|
11211
|
+
}
|
|
11212
|
+
function renderUntoldOverridesHybrid(nonCharacterRefs, finalIndexByUrl, toldUrls) {
|
|
11213
|
+
const lines = [];
|
|
11214
|
+
const sweptUrls = /* @__PURE__ */ new Set();
|
|
11215
|
+
for (const r of nonCharacterRefs) {
|
|
11216
|
+
if (!r.url || toldUrls.has(r.url) || sweptUrls.has(r.url)) continue;
|
|
11217
|
+
const slot = finalIndexByUrl.get(r.url);
|
|
11218
|
+
if (!slot) continue;
|
|
11219
|
+
const before = lines.length;
|
|
11220
|
+
pushOverrideDirective(lines, r, `reference image ${slotToLetter(slot)}`);
|
|
11221
|
+
if (lines.length > before) sweptUrls.add(r.url);
|
|
11222
|
+
}
|
|
11223
|
+
return lines;
|
|
11137
11224
|
}
|
|
11138
11225
|
function buildTileIdForUrl(urls, refs, prompt, sourceNodeIdById) {
|
|
11139
11226
|
const knownSlugs = Array.from(
|
|
@@ -11306,8 +11393,10 @@ function buildImagePromptInternal(config2, marks) {
|
|
|
11306
11393
|
);
|
|
11307
11394
|
const suppressedCanonicalLocationIds = config2.suppressedCanonicalLocationIds ?? [];
|
|
11308
11395
|
const isHybrid = config2.referenceFormat === "hybrid";
|
|
11396
|
+
const describedLines = renderDescribedReferenceLines(config2.describedReferences);
|
|
11309
11397
|
let hybridBodyConverged = false;
|
|
11310
11398
|
const mentionedEntityUrls = /* @__PURE__ */ new Set();
|
|
11399
|
+
const mentionedImageUrls = /* @__PURE__ */ new Set();
|
|
11311
11400
|
if (config2.skipCharacterMentions) {
|
|
11312
11401
|
config2 = {
|
|
11313
11402
|
...config2,
|
|
@@ -11425,6 +11514,7 @@ function buildImagePromptInternal(config2, marks) {
|
|
|
11425
11514
|
resolved.additionalUrls = [...resolved.additionalUrls, ...hi.additionalUrls];
|
|
11426
11515
|
hybridImageLockLines = hi.lockLines;
|
|
11427
11516
|
hybridImageElementDirectives = hi.elementDirectives;
|
|
11517
|
+
for (const u of hi.mentionedUrls) mentionedImageUrls.add(u);
|
|
11428
11518
|
if (hi.additionalUrls.length > 0) hybridBodyConverged = true;
|
|
11429
11519
|
}
|
|
11430
11520
|
}
|
|
@@ -11555,9 +11645,13 @@ ${promptForNext}`;
|
|
|
11555
11645
|
referenceImageUrls = config2.referenceImageUrls || [];
|
|
11556
11646
|
}
|
|
11557
11647
|
}
|
|
11558
|
-
if (connectedReferences) {
|
|
11648
|
+
if (connectedReferences || describedLines.length > 0) {
|
|
11649
|
+
const structuredRefs = connectedReferences ?? [];
|
|
11559
11650
|
let prompt2 = config2.prompt;
|
|
11560
|
-
|
|
11651
|
+
if (!isHybrid && describedLines.length > 0) {
|
|
11652
|
+
prompt2 = appendReferenceLines(prompt2, describedLines, "legacy");
|
|
11653
|
+
}
|
|
11654
|
+
const nonCharacterRefs = structuredRefs.filter(
|
|
11561
11655
|
(r) => r.source !== "wired-character" && r.isExtraRef !== true
|
|
11562
11656
|
);
|
|
11563
11657
|
const nonCharacterUrls = nonCharacterRefs.map((r) => r.url).filter((u) => Boolean(u));
|
|
@@ -11587,11 +11681,23 @@ ${promptForNext}`;
|
|
|
11587
11681
|
const canonLockBlock = canonLockLines.length > 0 ? `${canonLockLines.join("\n")}
|
|
11588
11682
|
|
|
11589
11683
|
` : "";
|
|
11684
|
+
const toldOverrideUrls = /* @__PURE__ */ new Set([
|
|
11685
|
+
...locCanon.renderedUrls,
|
|
11686
|
+
...objCanon.renderedUrls,
|
|
11687
|
+
...mentionedEntityUrls,
|
|
11688
|
+
...mentionedImageUrls
|
|
11689
|
+
]);
|
|
11690
|
+
const untoldOverrides = renderUntoldOverridesHybrid(
|
|
11691
|
+
nonCharacterRefs,
|
|
11692
|
+
finalIndexByUrl,
|
|
11693
|
+
toldOverrideUrls
|
|
11694
|
+
);
|
|
11590
11695
|
const canonTrailingLines = [
|
|
11591
11696
|
...locCanon.phrases,
|
|
11592
11697
|
...objCanon.phrases,
|
|
11593
11698
|
...locCanon.elementDirectives,
|
|
11594
|
-
...objCanon.elementDirectives
|
|
11699
|
+
...objCanon.elementDirectives,
|
|
11700
|
+
...untoldOverrides
|
|
11595
11701
|
];
|
|
11596
11702
|
const composedScene = `${canonLockBlock}${insertBeforeStyleSection(scene, canonTrailingLines)}`;
|
|
11597
11703
|
prompt2 = config2.referenceLockSnippet ? `${config2.referenceLockSnippet}
|
|
@@ -11618,6 +11724,9 @@ ${directives}`;
|
|
|
11618
11724
|
}
|
|
11619
11725
|
}
|
|
11620
11726
|
}
|
|
11727
|
+
if (isHybrid && describedLines.length > 0) {
|
|
11728
|
+
prompt2 = appendReferenceLines(prompt2, describedLines, "hybrid");
|
|
11729
|
+
}
|
|
11621
11730
|
const styleText2 = style?.trim();
|
|
11622
11731
|
const styleLine2 = styleText2 && !isDeniedStyleId(styleText2) ? `Style: ${getStylePromptHint(styleText2) || styleText2}` : "";
|
|
11623
11732
|
const negPrompt2 = negativePrompt?.trim();
|
|
@@ -11658,7 +11767,7 @@ ${directives}`;
|
|
|
11658
11767
|
const reordered = applyReferenceOrder(
|
|
11659
11768
|
assembledUrls,
|
|
11660
11769
|
prompt2,
|
|
11661
|
-
|
|
11770
|
+
structuredRefs,
|
|
11662
11771
|
referenceOrder,
|
|
11663
11772
|
sourceNodeIdById
|
|
11664
11773
|
);
|
|
@@ -11783,7 +11892,10 @@ function collectIdentities(prompt, refs, meta3) {
|
|
|
11783
11892
|
label,
|
|
11784
11893
|
fidelity: m?.fidelity ?? defaultFidelityForSource(ref?.source),
|
|
11785
11894
|
customText: m?.customText?.trim() || void 0,
|
|
11786
|
-
|
|
11895
|
+
// Per-use override ahead of the ref's own description; the directive
|
|
11896
|
+
// builder's location-canonical fallback only fires when BOTH are absent,
|
|
11897
|
+
// so an override outranks the stored location wording too.
|
|
11898
|
+
description: ref?.descriptionOverride?.trim() || ref?.description,
|
|
11787
11899
|
source: ref?.source,
|
|
11788
11900
|
locationCanonicalDescription: ref?.locationCanonicalDescription,
|
|
11789
11901
|
locationSlug: ref?.locationSlug,
|
|
@@ -11891,7 +12003,7 @@ function buildNonCharacterDirectives(prompt, nonCharacterRefs, identityMeta, fin
|
|
|
11891
12003
|
// wired-object falls through to the strict "match exactly" verb.
|
|
11892
12004
|
label: ref.source === "wired-location" ? "location" : ref.source === "wired-creature" ? "creature" : "object",
|
|
11893
12005
|
fidelity: defaultFidelityForSource(ref.source),
|
|
11894
|
-
description: ref.description?.trim() || void 0,
|
|
12006
|
+
description: ref.descriptionOverride?.trim() || ref.description?.trim() || void 0,
|
|
11895
12007
|
source: ref.source,
|
|
11896
12008
|
locationCanonicalDescription: ref.locationCanonicalDescription,
|
|
11897
12009
|
locationSlug: ref.locationSlug,
|
|
@@ -12418,6 +12530,8 @@ function resolveVideoCharacterMentionsHybrid(prompt, tokens, refs, offset) {
|
|
|
12418
12530
|
const binding = bindingFor(m.url);
|
|
12419
12531
|
const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding);
|
|
12420
12532
|
if (lock) lockLines.push(lock);
|
|
12533
|
+
const overrideLine = referenceDescriptionLine(binding, ref.descriptionOverride);
|
|
12534
|
+
if (overrideLine) elementDirectives.push(overrideLine);
|
|
12421
12535
|
const inject = ref.elementInjection?.trim();
|
|
12422
12536
|
if (inject) elementDirectives.push(inject);
|
|
12423
12537
|
}
|
|
@@ -12443,6 +12557,13 @@ function resolveVideoReferenceCore(args) {
|
|
|
12443
12557
|
video: args.videoRefCount ?? 0,
|
|
12444
12558
|
audio: args.audioRefCount ?? 0
|
|
12445
12559
|
});
|
|
12560
|
+
const describedAndCaptionLines = [
|
|
12561
|
+
...renderDescribedReferenceLines(args.describedReferences),
|
|
12562
|
+
...renderReferenceCaptionLines(args.videoCaptions, args.audioCaptions, {
|
|
12563
|
+
video: args.videoRefCount ?? 0,
|
|
12564
|
+
audio: args.audioRefCount ?? 0
|
|
12565
|
+
})
|
|
12566
|
+
];
|
|
12446
12567
|
let wiredCharRefs = [...args.wiredCharRefs];
|
|
12447
12568
|
const suppressedSlugs = new Set(args.suppressedCanonicalCharacterIds ?? []);
|
|
12448
12569
|
if (suppressedSlugs.size > 0) {
|
|
@@ -12467,14 +12588,15 @@ function resolveVideoReferenceCore(args) {
|
|
|
12467
12588
|
const slotByRefId = /* @__PURE__ */ new Map();
|
|
12468
12589
|
if (wiredCharRefs.length === 0 && !hasExtras) {
|
|
12469
12590
|
const counts = tokenCounts(leadingRefUrls.length);
|
|
12591
|
+
const resolved2 = resolveReferenceTokens(
|
|
12592
|
+
resolveRefIdTokens(args.prompt, { slotById: slotByRefId, nameById: nameByRefId, imageCount: counts.image }),
|
|
12593
|
+
counts
|
|
12594
|
+
);
|
|
12470
12595
|
return {
|
|
12471
|
-
//
|
|
12472
|
-
//
|
|
12473
|
-
//
|
|
12474
|
-
prompt:
|
|
12475
|
-
resolveRefIdTokens(args.prompt, { slotById: slotByRefId, nameById: nameByRefId, imageCount: counts.image }),
|
|
12476
|
-
counts
|
|
12477
|
-
),
|
|
12596
|
+
// Described references and rail captions are the ONE thing this branch can
|
|
12597
|
+
// still contribute: they need no seat, so a request that carries only them
|
|
12598
|
+
// (a story landing before any entity exists) reaches the model here.
|
|
12599
|
+
prompt: describedAndCaptionLines.length > 0 ? appendReferenceLines(resolved2 ?? "", describedAndCaptionLines, args.hybridRoles === true ? "hybrid" : "legacy") : resolved2,
|
|
12478
12600
|
additionalUrls: [...leadingRefUrls]
|
|
12479
12601
|
};
|
|
12480
12602
|
}
|
|
@@ -12532,6 +12654,8 @@ function resolveVideoReferenceCore(args) {
|
|
|
12532
12654
|
canonicalPhrases.push(shared.roleToPhrase(shared.resolveDefaultRole(r.defaultRole, r.defaultUsageMode, r.source), binding));
|
|
12533
12655
|
const lock = buildIdentityLockLine(r, binding);
|
|
12534
12656
|
if (lock) canonicalLockLines.push(lock);
|
|
12657
|
+
const overrideLine = referenceDescriptionLine(binding, r.descriptionOverride);
|
|
12658
|
+
if (overrideLine) canonicalElementDirectives.push(overrideLine);
|
|
12535
12659
|
const inject = r.elementInjection?.trim();
|
|
12536
12660
|
if (inject) canonicalElementDirectives.push(inject);
|
|
12537
12661
|
continue;
|
|
@@ -12548,7 +12672,10 @@ function resolveVideoReferenceCore(args) {
|
|
|
12548
12672
|
const directive = shared.usageModeDirective(effectiveMode);
|
|
12549
12673
|
const includeCanonicalDesc = effectiveMode === "identical" || effectiveMode === "face-pose";
|
|
12550
12674
|
const descBodyParts = [];
|
|
12551
|
-
|
|
12675
|
+
const canonicalOverride = r.descriptionOverride?.trim();
|
|
12676
|
+
if (canonicalOverride) {
|
|
12677
|
+
descBodyParts.push(canonicalOverride);
|
|
12678
|
+
} else if (includeCanonicalDesc && r.characterCanonicalDescription?.trim()) {
|
|
12552
12679
|
descBodyParts.push(r.characterCanonicalDescription.trim());
|
|
12553
12680
|
}
|
|
12554
12681
|
if (r.elementInjection?.trim()) descBodyParts.push(r.elementInjection.trim());
|
|
@@ -12565,7 +12692,7 @@ function resolveVideoReferenceCore(args) {
|
|
|
12565
12692
|
if (!ex.url) continue;
|
|
12566
12693
|
position += 1;
|
|
12567
12694
|
if (ex.id && !slotByRefId.has(ex.id)) slotByRefId.set(ex.id, position);
|
|
12568
|
-
const desc = (ex.description ?? "").trim();
|
|
12695
|
+
const desc = (ex.descriptionOverride ?? "").trim() || (ex.description ?? "").trim();
|
|
12569
12696
|
if (ex.characterSlug) {
|
|
12570
12697
|
const meta3 = args.lookupCharacterBySlug?.(ex.characterSlug);
|
|
12571
12698
|
const charDefaultMode = meta3?.defaultUsageMode;
|
|
@@ -12709,6 +12836,11 @@ ${finalPrompt}` : block;
|
|
|
12709
12836
|
merged.push(u);
|
|
12710
12837
|
}
|
|
12711
12838
|
}
|
|
12839
|
+
finalPrompt = appendReferenceLines(
|
|
12840
|
+
finalPrompt,
|
|
12841
|
+
describedAndCaptionLines,
|
|
12842
|
+
hybrid ? "hybrid" : "legacy"
|
|
12843
|
+
);
|
|
12712
12844
|
finalPrompt = resolveRefIdTokens(finalPrompt, {
|
|
12713
12845
|
slotById: slotByRefId,
|
|
12714
12846
|
nameById: nameByRefId,
|
|
@@ -12923,6 +13055,7 @@ function assembleImageInput(input) {
|
|
|
12923
13055
|
prompt: composePromptText(input.userPrompt, pieces, keptHintClauses),
|
|
12924
13056
|
provider: input.provider,
|
|
12925
13057
|
...input.connectedReferences !== void 0 ? { connectedReferences: input.connectedReferences } : {},
|
|
13058
|
+
...input.describedReferences !== void 0 ? { describedReferences: input.describedReferences } : {},
|
|
12926
13059
|
// Manual uploads / direct refs ride the builder's reference-URL channel so
|
|
12927
13060
|
// the per-provider reference gate filters them alongside bound entities.
|
|
12928
13061
|
// Omit the field entirely when absent so the builder's default ([]) kicks
|
|
@@ -13355,8 +13488,44 @@ function getAdultOnlyEntries() {
|
|
|
13355
13488
|
function getAdultOnlyIds() {
|
|
13356
13489
|
return new Set(getAdultOnlyEntries().map((e) => e.id));
|
|
13357
13490
|
}
|
|
13491
|
+
var RETIRED_ADULT_ONLY_HINT_STRINGS = [
|
|
13492
|
+
// bust-very-full
|
|
13493
|
+
"very full bust",
|
|
13494
|
+
// silhouette-hourglass
|
|
13495
|
+
"hourglass silhouette",
|
|
13496
|
+
// waist-defined
|
|
13497
|
+
"defined waist",
|
|
13498
|
+
// lip-state-glossy
|
|
13499
|
+
"with high-shine glossy wet-look lips",
|
|
13500
|
+
// lip-state-parted
|
|
13501
|
+
"with lips slightly parted, taking a soft breath",
|
|
13502
|
+
// lip-state-bitten
|
|
13503
|
+
"playfully biting the lower lip",
|
|
13504
|
+
// eye-state-half-lidded
|
|
13505
|
+
"with heavy half-lidded sleepy eyes",
|
|
13506
|
+
// texture-glistening
|
|
13507
|
+
"with glistening skin, sweat or oil catching the light",
|
|
13508
|
+
// texture-shower-fresh-wet
|
|
13509
|
+
"with just-out-of-the-shower wet skin, water beading on the surface and rolling in slow droplets down the curves of the body",
|
|
13510
|
+
// feature-bare-shoulders
|
|
13511
|
+
"with bare shoulders exposed, the line of the collarbone and shoulder muscles uncovered",
|
|
13512
|
+
// feature-collarbone-visible
|
|
13513
|
+
"with a prominent collarbone clearly defined and catching the light",
|
|
13514
|
+
// feature-midriff-visible
|
|
13515
|
+
"wearing a cropped style with the midriff visible",
|
|
13516
|
+
// state-fitted — the 2026-07-30 incident clause
|
|
13517
|
+
"the clothing fitted and form-conscious, hugging the contours of the body",
|
|
13518
|
+
// state-wet
|
|
13519
|
+
"the clothing soaked and wet, the fabric clinging to the body and dripping water",
|
|
13520
|
+
// pose `biting-lip` (the lip-state-bitten twin)
|
|
13521
|
+
"biting the lower lip with a subtle playful expression",
|
|
13522
|
+
// the hard-coded midriff+navel fold in emitIndependentFragments — not any
|
|
13523
|
+
// entry's hint, so retiring it ADDS a needle for a clause that really is
|
|
13524
|
+
// emitted verbatim.
|
|
13525
|
+
"wearing a cropped style, midriff and navel visible"
|
|
13526
|
+
];
|
|
13358
13527
|
function getAdultOnlyHintStrings() {
|
|
13359
|
-
const out =
|
|
13528
|
+
const out = new Set(RETIRED_ADULT_ONLY_HINT_STRINGS);
|
|
13360
13529
|
for (const e of getAdultOnlyEntries()) {
|
|
13361
13530
|
if (e.promptHint) out.add(e.promptHint.toLowerCase());
|
|
13362
13531
|
}
|
|
@@ -34759,6 +34928,7 @@ exports.REFERENCE_RULES_MULTI_PERSON = REFERENCE_RULES_MULTI_PERSON;
|
|
|
34759
34928
|
exports.REF_BINDING = REF_BINDING;
|
|
34760
34929
|
exports.RENDER_QUALITIES = RENDER_QUALITIES;
|
|
34761
34930
|
exports.RENDER_QUALITY_IDS = RENDER_QUALITY_IDS;
|
|
34931
|
+
exports.RETIRED_ADULT_ONLY_HINT_STRINGS = RETIRED_ADULT_ONLY_HINT_STRINGS;
|
|
34762
34932
|
exports.SCENE_FRAME_RULE = SCENE_FRAME_RULE;
|
|
34763
34933
|
exports.SCENE_PROMPT_MAX_LENGTH = SCENE_PROMPT_MAX_LENGTH;
|
|
34764
34934
|
exports.SETTINGS = SETTINGS;
|
|
@@ -34822,6 +34992,7 @@ exports.WARDROBE_FIELD_BY_DIMENSION = WARDROBE_FIELD_BY_DIMENSION;
|
|
|
34822
34992
|
exports.__resetCatalogIdGuardForTests = __resetCatalogIdGuardForTests;
|
|
34823
34993
|
exports.appendField = appendField;
|
|
34824
34994
|
exports.appendMusicMeta = appendMusicMeta;
|
|
34995
|
+
exports.appendReferenceLines = appendReferenceLines;
|
|
34825
34996
|
exports.applyMinorAgeFloorToPickerValues = applyMinorAgeFloorToPickerValues;
|
|
34826
34997
|
exports.applyPickerJson = applyPickerJson;
|
|
34827
34998
|
exports.applyPromptAffixes = applyPromptAffixes;
|
|
@@ -35148,11 +35319,14 @@ exports.promptPartSeparator = promptPartSeparator;
|
|
|
35148
35319
|
exports.readDirectionFields = readDirectionFields;
|
|
35149
35320
|
exports.readStructuredFields = readStructuredFields;
|
|
35150
35321
|
exports.readSubjectFields = readSubjectFields;
|
|
35322
|
+
exports.referenceDescriptionLine = referenceDescriptionLine;
|
|
35151
35323
|
exports.referenceRulesBlock = referenceRulesBlock;
|
|
35152
35324
|
exports.registerCatalogPack = registerCatalogPack;
|
|
35153
35325
|
exports.registerPersonPack = registerPersonPack;
|
|
35326
|
+
exports.renderDescribedReferenceLines = renderDescribedReferenceLines;
|
|
35154
35327
|
exports.renderDirectionHintClauses = renderDirectionHintClauses;
|
|
35155
35328
|
exports.renderDirectionHints = renderDirectionHints;
|
|
35329
|
+
exports.renderReferenceCaptionLines = renderReferenceCaptionLines;
|
|
35156
35330
|
exports.renderStructuredFields = renderStructuredFields;
|
|
35157
35331
|
exports.renderStyleSection = renderStyleSection;
|
|
35158
35332
|
exports.renderSubjectHints = renderSubjectHints;
|