@nodaro/prompts 1.11.0 → 1.13.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.
Files changed (36) hide show
  1. package/dist/index.cjs +627 -177
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +726 -33
  4. package/dist/index.d.ts +726 -33
  5. package/dist/index.js +598 -179
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/__tests__/__snapshots__/entity-convergence-image.test.ts.snap +19 -0
  9. package/src/__tests__/animal-getters-parity.test.ts +82 -0
  10. package/src/__tests__/assemble-image-input-cap.test.ts +236 -0
  11. package/src/__tests__/assemble-image-input.test.ts +100 -19
  12. package/src/__tests__/assemble-video-input-cap.test.ts +442 -0
  13. package/src/__tests__/assemble-video-input.test.ts +167 -33
  14. package/src/__tests__/direction-hint-token-safety.test.ts +21 -0
  15. package/src/__tests__/entity-convergence-image.test.ts +374 -0
  16. package/src/__tests__/location-convergence-image.test.ts +29 -1
  17. package/src/__tests__/location-default-role-image.test.ts +166 -0
  18. package/src/__tests__/mention-splice-spacing.test.ts +257 -0
  19. package/src/__tests__/prompt-style-section.test.ts +345 -0
  20. package/src/__tests__/read-node-subject.test.ts +140 -0
  21. package/src/__tests__/style-section-boundary.test.ts +179 -0
  22. package/src/__tests__/subject-fold.test.ts +251 -0
  23. package/src/__tests__/subject-registry.test.ts +312 -0
  24. package/src/assemble-image-input.ts +169 -41
  25. package/src/assemble-video-input.ts +200 -25
  26. package/src/direction-registry.ts +116 -28
  27. package/src/hint-shedding.ts +87 -0
  28. package/src/index.ts +3 -0
  29. package/src/parameter-prompt-hint.ts +8 -7
  30. package/src/picker-catalogs.ts +14 -7
  31. package/src/prompt-builder.ts +628 -88
  32. package/src/prompt-hint-join.ts +9 -0
  33. package/src/prompt-style-section.ts +256 -0
  34. package/src/read-node-direction.ts +60 -1
  35. package/src/subject-registry.ts +464 -0
  36. package/src/video-reference-resolver.ts +5 -2
package/dist/index.cjs CHANGED
@@ -9241,7 +9241,7 @@ function objectOptions(arr, phrase) {
9241
9241
  label: e.label,
9242
9242
  description: e.description,
9243
9243
  category: e.subcategory,
9244
- promptHint: phrase(e.label.toLowerCase(), e.description),
9244
+ promptHint: phrase(e),
9245
9245
  // Object entities have no `promptHint` field of their own (it is
9246
9246
  // synthesized above), so the term cannot come from `resolveTerm`'s
9247
9247
  // empty-hint rule: an authored `term` wins, and otherwise the label IS the
@@ -9573,7 +9573,7 @@ var SINGLE_CATALOGS = [
9573
9573
  defaultValue: "dog-golden-retriever",
9574
9574
  categoryOrder: shared.ANIMAL_SUBCATEGORY_ORDER,
9575
9575
  categoryLabels: shared.ANIMAL_SUBCATEGORY_LABELS,
9576
- options: objectOptions(shared.ANIMALS, (label, description) => `featuring a ${label}, ${description}`)
9576
+ options: objectOptions(shared.ANIMALS, (e) => shared.getAnimalPromptHint(e.id))
9577
9577
  },
9578
9578
  {
9579
9579
  nodeType: "vehicle",
@@ -9584,7 +9584,7 @@ var SINGLE_CATALOGS = [
9584
9584
  defaultValue: "sedan",
9585
9585
  categoryOrder: shared.VEHICLE_SUBCATEGORY_ORDER,
9586
9586
  categoryLabels: shared.VEHICLE_SUBCATEGORY_LABELS,
9587
- options: objectOptions(shared.VEHICLES, (label, description) => `featuring a ${label}, ${description}`)
9587
+ options: objectOptions(shared.VEHICLES, (e) => `featuring a ${e.label.toLowerCase()}, ${e.description}`)
9588
9588
  },
9589
9589
  {
9590
9590
  nodeType: "weapon",
@@ -9595,7 +9595,7 @@ var SINGLE_CATALOGS = [
9595
9595
  defaultValue: "katana",
9596
9596
  categoryOrder: shared.WEAPON_SUBCATEGORY_ORDER,
9597
9597
  categoryLabels: shared.WEAPON_SUBCATEGORY_LABELS,
9598
- options: objectOptions(shared.WEAPONS, (label, description) => `with a ${label}, ${description}`)
9598
+ options: objectOptions(shared.WEAPONS, (e) => `with a ${e.label.toLowerCase()}, ${e.description}`)
9599
9599
  },
9600
9600
  {
9601
9601
  nodeType: "furniture",
@@ -9606,7 +9606,7 @@ var SINGLE_CATALOGS = [
9606
9606
  defaultValue: "sofa",
9607
9607
  categoryOrder: shared.FURNITURE_SUBCATEGORY_ORDER,
9608
9608
  categoryLabels: shared.FURNITURE_SUBCATEGORY_LABELS,
9609
- options: objectOptions(shared.FURNITURE, (label, description) => `including a ${label}, ${description}`)
9609
+ options: objectOptions(shared.FURNITURE, (e) => `including a ${e.label.toLowerCase()}, ${e.description}`)
9610
9610
  },
9611
9611
  {
9612
9612
  nodeType: "held-prop",
@@ -9974,13 +9974,16 @@ function resolveBaseHint(type, data, mode = "full") {
9974
9974
  return withCustomText(data, byMode(mode, getLoopSubjectPromptHint, getLoopSubjectTerm)(asStr(data.loopSubject)));
9975
9975
  case "material":
9976
9976
  return withCustomText(data, buildMaterialHints(data.material, mode));
9977
- case "animal": {
9978
- const animal = shared.getAnimal(asStr(data.animal));
9977
+ // Animal is the one Object-entity catalog whose phrasing has a single
9978
+ // owner: `@nodaro/shared`'s `getAnimalPromptHint` / `getAnimalTerm`, which
9979
+ // the picker-catalog funnel calls too. Both getters already return "" on a
9980
+ // miss, so the entry lookup and the `animal ? … : ""` guard are the
9981
+ // getters' job now, not this switch's.
9982
+ case "animal":
9979
9983
  return withCustomText(
9980
9984
  data,
9981
- animal ? byMode(mode, `featuring a ${animal.label.toLowerCase()}, ${animal.description}`, objectEntityTerm(animal)) : ""
9985
+ byMode(mode, shared.getAnimalPromptHint, shared.getAnimalTerm)(asStr(data.animal))
9982
9986
  );
9983
- }
9984
9987
  case "vehicle": {
9985
9988
  const vehicle = shared.getVehicle(asStr(data.vehicle));
9986
9989
  return withCustomText(
@@ -10214,6 +10217,218 @@ function applyTemplate(template, vars) {
10214
10217
  template
10215
10218
  );
10216
10219
  }
10220
+
10221
+ // src/prompt-hint-join.ts
10222
+ var PROMPT_HINT_SEPARATOR = ". ";
10223
+ function joinPromptHints(userPrompt, hints) {
10224
+ if (hints.length === 0) return userPrompt;
10225
+ return [userPrompt.trim(), ...hints].filter((p) => p.length > 0).join(PROMPT_HINT_SEPARATOR);
10226
+ }
10227
+
10228
+ // src/direction-registry.ts
10229
+ var perId = (full, compact) => (ids, mode) => {
10230
+ const out = [];
10231
+ for (const id of ids) {
10232
+ const frag = mode === "compact" ? compact(id) : full(id);
10233
+ if (frag.length > 0) out.push(frag);
10234
+ }
10235
+ return out;
10236
+ };
10237
+ var viaListBuilder = (build) => (ids, mode) => build(ids.length === 1 ? ids[0] : [...ids], mode).filter((s) => s.length > 0);
10238
+ var viaStringBuilder = (build) => (ids, mode) => {
10239
+ const s = build(ids.length === 1 ? ids[0] : [...ids], mode);
10240
+ return s.length > 0 ? [s] : [];
10241
+ };
10242
+ var viaMood = (ids, mode) => buildMoodHints({ mood: ids.length === 1 ? ids[0] : [...ids] }, mode).filter(
10243
+ (s) => s.length > 0
10244
+ );
10245
+ var framing = perId(getFramingPromptHint, getFramingTerm);
10246
+ var lighting = perId(getLightingPromptHint, getLightingTerm);
10247
+ var exposure = perId(getExposurePromptHint, getExposureTerm);
10248
+ var temporal = perId(getTemporalPromptHint, getTemporalTerm);
10249
+ var DIRECTION_FIELDS = [
10250
+ { key: "cameraMotion", surface: "video", family: "motion", maxPicks: 1, render: perId(getCameraMotionPromptHint, getCameraMotionTerm) },
10251
+ // Composition (the Framing catalog, one row per category).
10252
+ { key: "shotSize", surface: "both", family: "look", maxPicks: 1, render: framing },
10253
+ { key: "angle", surface: "both", family: "look", maxPicks: 1, render: framing },
10254
+ { key: "coverage", surface: "both", family: "look", maxPicks: 1, render: framing },
10255
+ { key: "composition", surface: "both", family: "look", maxPicks: 2, render: framing },
10256
+ { key: "vantage", surface: "both", family: "look", maxPicks: 1, render: framing },
10257
+ { key: "pose", surface: "both", family: "look", maxPicks: 1, render: perId(getPosePromptHint, getPoseTerm) },
10258
+ { key: "compositionEffect", surface: "both", family: "look", maxPicks: 1, render: perId(getCompositionEffectPromptHint, getCompositionEffectTerm) },
10259
+ // Camera.
10260
+ { key: "cameraFormat", surface: "both", family: "look", styleGroup: "film", maxPicks: 1, render: perId(getCameraFormatPromptHint, getCameraFormatTerm) },
10261
+ { key: "lens", surface: "both", family: "look", maxPicks: 1, render: perId(getLensPromptHint, getLensTerm) },
10262
+ // Exposure (stills only — a video's exposure rides its own temporal levers).
10263
+ { key: "aperture", surface: "image", family: "look", maxPicks: 1, render: exposure },
10264
+ { key: "shutterSpeed", surface: "image", family: "look", maxPicks: 1, render: exposure },
10265
+ { key: "isoValue", surface: "image", family: "look", maxPicks: 1, render: exposure },
10266
+ // Light (the Lighting catalog, one row per category).
10267
+ { key: "timeOfDay", surface: "both", family: "look", maxPicks: 1, render: lighting },
10268
+ { key: "lightingStyle", surface: "both", family: "look", maxPicks: 2, render: lighting },
10269
+ { key: "lightingDirection", surface: "both", family: "look", maxPicks: 1, render: lighting },
10270
+ { key: "lightingRatio", surface: "both", family: "look", maxPicks: 1, render: lighting },
10271
+ { key: "colorTemperature", surface: "both", family: "look", maxPicks: 1, render: lighting },
10272
+ { key: "colorLook", surface: "both", family: "look", styleGroup: "film", maxPicks: 1, render: perId(getColorLookPromptHint, getColorLookTerm) },
10273
+ { key: "atmosphere", surface: "both", family: "look", maxPicks: 2, render: viaListBuilder(buildAtmosphereHints) },
10274
+ { key: "postProcess", surface: "image", family: "look", maxPicks: 2, render: viaListBuilder(buildPostProcessHints) },
10275
+ // Style.
10276
+ { key: "style", surface: "both", family: "look", styleGroup: "film", maxPicks: 1, render: perId(getStylePromptHint, getStyleTerm) },
10277
+ { key: "mood", surface: "both", family: "look", maxPicks: 2, render: viaMood },
10278
+ { key: "aesthetic", surface: "both", family: "look", maxPicks: 2, render: viaStringBuilder(buildAestheticHints) },
10279
+ { key: "photoGenre", surface: "image", family: "look", maxPicks: 1, render: perId(getPhotoGenrePromptHint, getPhotoGenreTerm) },
10280
+ { key: "photographer", surface: "image", family: "look", maxPicks: 2, render: viaStringBuilder(buildPhotographerHints) },
10281
+ { key: "renderQuality", surface: "image", family: "look", maxPicks: 1, render: perId(getRenderQualityPromptHint, getRenderQualityTerm) },
10282
+ // Scene.
10283
+ { key: "setting", surface: "both", family: "look", maxPicks: 1, render: perId(getSettingPromptHint, getSettingTerm) },
10284
+ { key: "era", surface: "both", family: "look", styleGroup: "film", maxPicks: 1, render: perId(getEraPromptHint, getEraTerm) },
10285
+ { key: "backdrop", surface: "both", family: "look", maxPicks: 1, render: perId(getBackdropPromptHint, getBackdropTerm) },
10286
+ // Motion & time.
10287
+ { key: "actionFx", surface: "video", family: "motion", maxPicks: 2, render: viaListBuilder(buildActionFxHints) },
10288
+ { key: "temporalSpeed", surface: "video", family: "motion", maxPicks: 1, render: temporal },
10289
+ { key: "temporalFreeze", surface: "video", family: "motion", maxPicks: 1, render: temporal },
10290
+ { key: "temporalDirection", surface: "video", family: "motion", maxPicks: 1, render: temporal },
10291
+ { key: "temporalShutter", surface: "video", family: "motion", maxPicks: 1, render: temporal },
10292
+ { key: "transition", surface: "video", family: "motion", maxPicks: 2, render: perId(getTransitionPromptHint, getTransitionTerm) },
10293
+ { key: "loopSubject", surface: "video", family: "motion", maxPicks: 1, render: perId(getLoopSubjectPromptHint, getLoopSubjectTerm) },
10294
+ // ── LEGACY BLOCK — see the table doc above. Placed LAST, in today's exact
10295
+ // `composePromptText` order, so every pre-registry caller is byte-identical.
10296
+ { key: "framingId", surface: "both", family: "look", maxPicks: 1, render: framing },
10297
+ { key: "framingAngleId", surface: "both", family: "look", maxPicks: 1, render: framing },
10298
+ { key: "lightingId", surface: "both", family: "look", maxPicks: 1, render: lighting },
10299
+ { key: "lensId", surface: "both", family: "look", maxPicks: 1, render: perId(getLensPromptHint, getLensTerm) },
10300
+ { key: "cameraFormatId", surface: "both", family: "look", styleGroup: "film", maxPicks: 1, render: perId(getCameraFormatPromptHint, getCameraFormatTerm) }
10301
+ ];
10302
+ var DIRECTION_SPECS = DIRECTION_FIELDS;
10303
+ var DIRECTION_KEYS = DIRECTION_FIELDS.map((f) => f.key);
10304
+ var FILM_STYLE_KEYS = DIRECTION_SPECS.filter(
10305
+ (f) => f.styleGroup === "film"
10306
+ ).map((f) => f.key);
10307
+ var IMAGE_HINT_MODE_DEFAULT = "full";
10308
+ var VIDEO_HINT_MODE_DEFAULT = { look: "full", motion: "compact" };
10309
+ function modeForFamily(mode, family) {
10310
+ return typeof mode === "string" ? mode : family === "motion" ? mode.motion : mode.look;
10311
+ }
10312
+ var DIRECTION_ARRAY_CEILING = 8;
10313
+ var DIRECTION_ID_MAX_CHARS = 100;
10314
+ function normalizeDirectionIds(value, maxPicks) {
10315
+ const ids = [];
10316
+ if (typeof value === "string") {
10317
+ if (value) ids.push(value);
10318
+ } else if (Array.isArray(value)) {
10319
+ for (const v of value) {
10320
+ if (ids.length >= maxPicks) break;
10321
+ if (typeof v === "string" && v && !ids.includes(v)) ids.push(v);
10322
+ }
10323
+ }
10324
+ return ids.slice(0, maxPicks);
10325
+ }
10326
+ function directionFieldsForSurface(surface) {
10327
+ return DIRECTION_SPECS.filter((f) => f.surface === "both" || f.surface === surface);
10328
+ }
10329
+ function renderDirectionHintClauses(direction, opts) {
10330
+ if (!direction) return [];
10331
+ const mode = opts.mode ?? "full";
10332
+ const out = [];
10333
+ const seen = /* @__PURE__ */ new Set();
10334
+ for (const spec of DIRECTION_SPECS) {
10335
+ if (spec.surface !== "both" && spec.surface !== opts.surface) continue;
10336
+ const ids = normalizeDirectionIds(
10337
+ direction[spec.key],
10338
+ spec.maxPicks
10339
+ );
10340
+ if (ids.length === 0) continue;
10341
+ for (const hint of spec.render(ids, modeForFamily(mode, spec.family))) {
10342
+ if (hint.length > 0 && !seen.has(hint)) {
10343
+ seen.add(hint);
10344
+ out.push({
10345
+ key: spec.key,
10346
+ family: spec.family,
10347
+ ...spec.styleGroup !== void 0 ? { styleGroup: spec.styleGroup } : {},
10348
+ text: hint
10349
+ });
10350
+ }
10351
+ }
10352
+ }
10353
+ return out;
10354
+ }
10355
+ function renderDirectionHints(direction, opts) {
10356
+ return renderDirectionHintClauses(direction, opts).map((c) => c.text);
10357
+ }
10358
+
10359
+ // src/prompt-style-section.ts
10360
+ var STYLE_SECTION_HEADER = "[style]:";
10361
+ var STYLE_SECTION_GAP = "\n\n";
10362
+ var STYLE_SECTION_OPENING = `${STYLE_SECTION_GAP}${STYLE_SECTION_HEADER}
10363
+ `;
10364
+ function splitStyleSection(prompt) {
10365
+ const at = prompt.lastIndexOf(STYLE_SECTION_OPENING);
10366
+ if (at >= 0) {
10367
+ return { body: prompt.slice(0, at), section: prompt.slice(at + STYLE_SECTION_GAP.length) };
10368
+ }
10369
+ return prompt.startsWith(`${STYLE_SECTION_HEADER}
10370
+ `) ? { body: "", section: prompt } : { body: prompt, section: "" };
10371
+ }
10372
+ function insertBeforeStyleSection(prompt, lines) {
10373
+ if (lines.length === 0) return prompt;
10374
+ const block = lines.join("\n");
10375
+ const { body, section } = splitStyleSection(prompt);
10376
+ if (section.length === 0) return `${prompt}
10377
+ ${block}`;
10378
+ return `${body.length > 0 ? `${body}
10379
+ ${block}` : block}${STYLE_SECTION_GAP}${section}`;
10380
+ }
10381
+ function endsInsideStyleSection(prompt) {
10382
+ const at = prompt.lastIndexOf(STYLE_SECTION_GAP);
10383
+ const lastBlock = at >= 0 ? prompt.slice(at + STYLE_SECTION_GAP.length) : prompt;
10384
+ return lastBlock.startsWith(`${STYLE_SECTION_HEADER}
10385
+ `);
10386
+ }
10387
+ function styleSlotFor(row) {
10388
+ if (row.family === "motion") return "body";
10389
+ return row.styleGroup === "film" ? "film" : "scene";
10390
+ }
10391
+ function asBodyClauses(texts) {
10392
+ return texts.map((text) => ({ text, slot: "body" }));
10393
+ }
10394
+ function partitionStyleClauses(direction, opts) {
10395
+ return renderDirectionHintClauses(direction, opts).map((clause) => ({
10396
+ text: clause.text,
10397
+ slot: styleSlotFor(clause)
10398
+ }));
10399
+ }
10400
+ function styleSectionFromClauses(clauses) {
10401
+ const line = (slot) => clauses.filter((c) => c.slot === slot).map((c) => c.text).join(PROMPT_HINT_SEPARATOR);
10402
+ const lines = [line("film"), line("scene")].filter((l) => l.length > 0);
10403
+ return lines.length === 0 ? "" : `${STYLE_SECTION_HEADER}
10404
+ ${lines.join("\n")}`;
10405
+ }
10406
+ function renderStyleSection(direction, opts) {
10407
+ return styleSectionFromClauses(partitionStyleClauses(direction, opts));
10408
+ }
10409
+ function composeSectionedPrompt(userPrompt, clauses, structuredFragment) {
10410
+ const bodyHints = [
10411
+ ...clauses.filter((c) => c.slot === "body").map((c) => c.text),
10412
+ structuredFragment
10413
+ ].filter((p) => p.length > 0);
10414
+ const section = styleSectionFromClauses(clauses);
10415
+ if (section.length === 0) {
10416
+ return bodyHints.length === 0 ? userPrompt : joinPromptHints(userPrompt ?? "", bodyHints);
10417
+ }
10418
+ const body = bodyHints.length > 0 ? joinPromptHints(userPrompt ?? "", bodyHints) : (userPrompt ?? "").trim();
10419
+ return body.length > 0 ? `${body}${STYLE_SECTION_GAP}${section}` : section;
10420
+ }
10421
+ function sectionedClauseCosts(userPrompt, clauses, structuredFragment) {
10422
+ const lengthAt = (kept) => composeSectionedPrompt(userPrompt, clauses.slice(0, kept), structuredFragment)?.length ?? 0;
10423
+ const costs = [];
10424
+ let below = lengthAt(0);
10425
+ for (let i = 0; i < clauses.length; i++) {
10426
+ const at = lengthAt(i + 1);
10427
+ costs.push(at - below);
10428
+ below = at;
10429
+ }
10430
+ return costs;
10431
+ }
10217
10432
  function composeIdentityDescPart(subject, canonicalDesc, elementInjection) {
10218
10433
  const parts = [];
10219
10434
  const c = canonicalDesc?.trim();
@@ -10284,6 +10499,11 @@ ${resolvedPrompt}`;
10284
10499
  }
10285
10500
  return { prompt: resolvedPrompt, additionalUrls, mentionedCharacterSlugs };
10286
10501
  }
10502
+ function spliceMentionPhrase(prompt, offset, tokenLength, phrase) {
10503
+ const before = prompt.slice(0, offset).replace(/(?<=\S)[^\S\r\n]{2,}$/, " ");
10504
+ const after = prompt.slice(offset + tokenLength).replace(/^[^\S\r\n]{2,}/, " ");
10505
+ return before + phrase + after;
10506
+ }
10287
10507
  function resolveCharacterMentionsHybrid(prompt, tokens, refs, existingUrls) {
10288
10508
  const bySlug = /* @__PURE__ */ new Map();
10289
10509
  const byVariant = /* @__PURE__ */ new Map();
@@ -10322,7 +10542,7 @@ function resolveCharacterMentionsHybrid(prompt, tokens, refs, existingUrls) {
10322
10542
  let resolvedPrompt = prompt;
10323
10543
  for (const m of [...matched].sort((a, b) => b.offset - a.offset)) {
10324
10544
  const phrase = shared.roleToPhrase(m.role, bindingFor(m.url));
10325
- resolvedPrompt = resolvedPrompt.slice(0, m.offset) + phrase + resolvedPrompt.slice(m.offset + m.token.length);
10545
+ resolvedPrompt = spliceMentionPhrase(resolvedPrompt, m.offset, m.token.length, phrase);
10326
10546
  }
10327
10547
  const lockLines = [];
10328
10548
  const elementDirectives = [];
@@ -10353,16 +10573,21 @@ function locationModeDirective(mode) {
10353
10573
  }
10354
10574
  }
10355
10575
  function locationModeToRole(mode) {
10356
- switch (mode) {
10357
- case "identical":
10358
- return "background";
10359
- case "style":
10360
- return "style";
10361
- case "layout":
10362
- return "layout";
10363
- default:
10364
- return shared.defaultRoleForSource("wired-location");
10365
- }
10576
+ return roleBearingLocationMode(mode) ?? shared.defaultRoleForSource("wired-location");
10577
+ }
10578
+ function roleBearingLocationMode(mode) {
10579
+ const m = mode?.trim();
10580
+ if (!m) return null;
10581
+ return shared.REFERENCE_ROLE_PRESETS["wired-location"].includes(m) ? m : null;
10582
+ }
10583
+ function resolveLocationRole(tokenRole, tokenUsageMode, ref) {
10584
+ const explicitRole = tokenRole?.trim();
10585
+ if (explicitRole) return shared.normalizeRoleSlug(explicitRole);
10586
+ const modeRole = roleBearingLocationMode(tokenUsageMode);
10587
+ if (modeRole) return modeRole;
10588
+ const nodeRole = ref.defaultRole?.trim();
10589
+ if (nodeRole) return shared.normalizeRoleSlug(nodeRole);
10590
+ return locationModeToRole(ref.defaultUsageMode ?? shared.DEFAULT_LOCATION_USAGE_MODE);
10366
10591
  }
10367
10592
  function resolveLocationMentions(prompt, tokens, refs) {
10368
10593
  const bySlug = /* @__PURE__ */ new Map();
@@ -10444,8 +10669,7 @@ function resolveLocationMentionsHybrid(prompt, tokens, refs, existingUrls) {
10444
10669
  mentionedLocationSlugs.add(t.locationSlug);
10445
10670
  refByUrl.set(match.url, match);
10446
10671
  if (t.lock !== void 0) lockOverrideByUrl.set(match.url, t.lock);
10447
- const mode = t.usageMode ?? match.defaultUsageMode ?? shared.DEFAULT_LOCATION_USAGE_MODE;
10448
- const role = t.role ? shared.normalizeRoleSlug(t.role) : locationModeToRole(mode);
10672
+ const role = resolveLocationRole(t.role, t.usageMode, match);
10449
10673
  matched.push({ token: t.token, offset: t.offset, url: match.url, role });
10450
10674
  }
10451
10675
  const slotByUrl = /* @__PURE__ */ new Map();
@@ -10459,7 +10683,7 @@ function resolveLocationMentionsHybrid(prompt, tokens, refs, existingUrls) {
10459
10683
  let resolvedPrompt = prompt;
10460
10684
  for (const m of [...matched].sort((a, b) => b.offset - a.offset)) {
10461
10685
  const phrase = shared.roleToPhrase(m.role, bindingFor(m.url));
10462
- resolvedPrompt = resolvedPrompt.slice(0, m.offset) + phrase + resolvedPrompt.slice(m.offset + m.token.length);
10686
+ resolvedPrompt = spliceMentionPhrase(resolvedPrompt, m.offset, m.token.length, phrase);
10463
10687
  }
10464
10688
  const lockLines = [];
10465
10689
  const elementDirectives = [];
@@ -10508,7 +10732,7 @@ function resolveImageMentionsHybrid(prompt, tokens, refs, existingUrls) {
10508
10732
  let resolvedPrompt = prompt;
10509
10733
  for (const m of [...matched].sort((a, b) => b.offset - a.offset)) {
10510
10734
  const phrase = shared.roleToPhrase(m.role, bindingFor(m.url));
10511
- resolvedPrompt = resolvedPrompt.slice(0, m.offset) + phrase + resolvedPrompt.slice(m.offset + m.token.length);
10735
+ resolvedPrompt = spliceMentionPhrase(resolvedPrompt, m.offset, m.token.length, phrase);
10512
10736
  }
10513
10737
  const lockLines = [];
10514
10738
  const elementDirectives = [];
@@ -10526,6 +10750,60 @@ function resolveImageMentionsHybrid(prompt, tokens, refs, existingUrls) {
10526
10750
  }
10527
10751
  return { prompt: resolvedPrompt, additionalUrls, lockLines, elementDirectives };
10528
10752
  }
10753
+ function resolveEntityMentionsHybrid(prompt, tokens, refs, existingUrls) {
10754
+ const bySlug = /* @__PURE__ */ new Map();
10755
+ for (const source of ["wired-creature", "wired-object"]) {
10756
+ for (const r of refs) {
10757
+ if (r.source !== source) continue;
10758
+ const slug = shared.entityMentionSlugForRef(r);
10759
+ if (!slug) continue;
10760
+ if (!bySlug.has(slug)) bySlug.set(slug, r);
10761
+ }
10762
+ }
10763
+ const additionalUrls = [];
10764
+ const mentionedUrls = /* @__PURE__ */ new Set();
10765
+ const refByUrl = /* @__PURE__ */ new Map();
10766
+ const lockOverrideByUrl = /* @__PURE__ */ new Map();
10767
+ const matched = [];
10768
+ for (const t of tokens) {
10769
+ const match = bySlug.get(t.entitySlug);
10770
+ if (!match || !match.url) continue;
10771
+ additionalUrls.push(match.url);
10772
+ mentionedUrls.add(match.url);
10773
+ refByUrl.set(match.url, match);
10774
+ if (t.lock !== void 0) lockOverrideByUrl.set(match.url, t.lock);
10775
+ const role = (t.role ?? "").trim() || shared.resolveDefaultRole(match.defaultRole, match.defaultUsageMode, match.source);
10776
+ matched.push({ token: t.token, offset: t.offset, url: match.url, role });
10777
+ }
10778
+ const slotByUrl = /* @__PURE__ */ new Map();
10779
+ for (const u of [...existingUrls, ...additionalUrls]) {
10780
+ if (!slotByUrl.has(u)) slotByUrl.set(u, slotByUrl.size + 1);
10781
+ }
10782
+ const bindingFor = (url2) => {
10783
+ const slot = slotByUrl.get(url2);
10784
+ return slot ? `reference image ${slotToLetter(slot)}` : "the reference image";
10785
+ };
10786
+ let resolvedPrompt = prompt;
10787
+ for (const m of [...matched].sort((a, b) => b.offset - a.offset)) {
10788
+ const phrase = shared.roleToPhrase(m.role, bindingFor(m.url));
10789
+ resolvedPrompt = spliceMentionPhrase(resolvedPrompt, m.offset, m.token.length, phrase);
10790
+ }
10791
+ const lockLines = [];
10792
+ const elementDirectives = [];
10793
+ const seenUrls = /* @__PURE__ */ new Set();
10794
+ for (const m of matched) {
10795
+ if (seenUrls.has(m.url)) continue;
10796
+ seenUrls.add(m.url);
10797
+ const ref = refByUrl.get(m.url);
10798
+ if (!ref) continue;
10799
+ const binding = bindingFor(m.url);
10800
+ const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding);
10801
+ if (lock) lockLines.push(lock);
10802
+ const inject = ref.elementInjection?.trim();
10803
+ if (inject) elementDirectives.push(inject);
10804
+ }
10805
+ return { prompt: resolvedPrompt, additionalUrls, mentionedUrls, lockLines, elementDirectives };
10806
+ }
10529
10807
  function buildCanonicalFallback(refs, mentionedSlugs, startIndex) {
10530
10808
  const directiveLines = [];
10531
10809
  const urls = [];
@@ -10684,7 +10962,7 @@ function renderLocationCanonicalHybrid(nonCharacterRefs, finalIndexByUrl, covere
10684
10962
  if (!slot) continue;
10685
10963
  seenUrls.add(r.url);
10686
10964
  const binding = `reference image ${slotToLetter(slot)}`;
10687
- phrases.push(shared.roleToPhrase(locationModeToRole(r.defaultUsageMode), binding));
10965
+ phrases.push(shared.roleToPhrase(resolveLocationRole(null, null, r), binding));
10688
10966
  const lock = buildIdentityLockLine(r, binding);
10689
10967
  if (lock) lockLines.push(lock);
10690
10968
  const inject = r.elementInjection?.trim();
@@ -10840,9 +11118,29 @@ function reconcileBodySegments(body, bodySegments) {
10840
11118
  }
10841
11119
  return [{ text: body, origin: "user" }];
10842
11120
  }
11121
+ function controlSuffixes(body, styleLine, avoidLine) {
11122
+ const separator = (text) => endsInsideStyleSection(text) ? STYLE_SECTION_GAP : "\n";
11123
+ const styleSuffix = styleLine ? `${separator(body)}${styleLine}` : "";
11124
+ const avoidSuffix = avoidLine ? `${separator(body + styleSuffix)}${avoidLine}` : "";
11125
+ return { styleSuffix, avoidSuffix };
11126
+ }
10843
11127
  function buildImagePrompt(config2) {
10844
11128
  return buildImagePromptInternal(config2);
10845
11129
  }
11130
+ function buildImagePromptWithOverflow(config2) {
11131
+ const marks = newAssemblyMarks();
11132
+ const result = buildImagePromptInternal(config2, marks);
11133
+ return { ...result, overflowChars: marks.overflowChars };
11134
+ }
11135
+ function newAssemblyMarks() {
11136
+ return {
11137
+ directivesPrefix: "",
11138
+ bodyBeforeSuffixes: "",
11139
+ styleSuffix: "",
11140
+ avoidSuffix: "",
11141
+ overflowChars: 0
11142
+ };
11143
+ }
10846
11144
  function buildImagePromptInternal(config2, marks) {
10847
11145
  let {
10848
11146
  provider,
@@ -10864,6 +11162,7 @@ function buildImagePromptInternal(config2, marks) {
10864
11162
  const suppressedCanonicalLocationIds = config2.suppressedCanonicalLocationIds ?? [];
10865
11163
  const isHybrid = config2.referenceFormat === "hybrid";
10866
11164
  let hybridBodyConverged = false;
11165
+ const mentionedEntityUrls = /* @__PURE__ */ new Set();
10867
11166
  if (config2.skipCharacterMentions) {
10868
11167
  config2 = {
10869
11168
  ...config2,
@@ -10910,7 +11209,9 @@ function buildImagePromptInternal(config2, marks) {
10910
11209
  );
10911
11210
  const knownImageSlugs = shared.knownImageSlugsFromRefs(connectedReferences);
10912
11211
  const hasImageMentionTokens = isHybrid && knownImageSlugs.length > 0 && shared.findImageMentionTokens(config2.prompt, knownImageSlugs).length > 0;
10913
- if (knownCharacterSlugs.length > 0 || hasExtraRefs || knownLocationSlugs.length > 0 || hasImageMentionTokens) {
11212
+ const knownEntitySlugs = shared.knownEntitySlugsFromRefs(connectedReferences);
11213
+ const hasEntityMentionTokens = isHybrid && knownEntitySlugs.length > 0 && shared.findEntityMentionTokens(config2.prompt, knownEntitySlugs).length > 0;
11214
+ if (knownCharacterSlugs.length > 0 || hasExtraRefs || knownLocationSlugs.length > 0 || hasImageMentionTokens || hasEntityMentionTokens) {
10914
11215
  const mentionTokens = knownCharacterSlugs.length > 0 ? shared.findCharacterMentionTokens(config2.prompt, knownCharacterSlugs) : [];
10915
11216
  let hybridLockLines = [];
10916
11217
  let hybridElementDirectives = [];
@@ -10982,6 +11283,25 @@ function buildImagePromptInternal(config2, marks) {
10982
11283
  if (hi.additionalUrls.length > 0) hybridBodyConverged = true;
10983
11284
  }
10984
11285
  }
11286
+ let hybridEntityLockLines = [];
11287
+ let hybridEntityElementDirectives = [];
11288
+ if (hasEntityMentionTokens) {
11289
+ const entityTokens = shared.findEntityMentionTokens(resolved.prompt, knownEntitySlugs);
11290
+ if (entityTokens.length > 0) {
11291
+ const he = resolveEntityMentionsHybrid(
11292
+ resolved.prompt,
11293
+ entityTokens,
11294
+ connectedReferences,
11295
+ [...referenceImageUrls || [], ...resolved.additionalUrls]
11296
+ );
11297
+ resolved.prompt = he.prompt;
11298
+ resolved.additionalUrls = [...resolved.additionalUrls, ...he.additionalUrls];
11299
+ hybridEntityLockLines = he.lockLines;
11300
+ hybridEntityElementDirectives = he.elementDirectives;
11301
+ for (const u of he.mentionedUrls) mentionedEntityUrls.add(u);
11302
+ if (he.additionalUrls.length > 0) hybridBodyConverged = true;
11303
+ }
11304
+ }
10985
11305
  const prefixDedupedLength = [
10986
11306
  ...referenceImageUrls || [],
10987
11307
  ...resolved.additionalUrls
@@ -11063,6 +11383,7 @@ ${promptForNext}`;
11063
11383
  ...hybridLockLines,
11064
11384
  ...hybridLocationLockLines,
11065
11385
  ...hybridImageLockLines,
11386
+ ...hybridEntityLockLines,
11066
11387
  ...canonical.lockLines,
11067
11388
  ...extrasRendered.lockLines
11068
11389
  ])];
@@ -11070,6 +11391,7 @@ ${promptForNext}`;
11070
11391
  ...hybridElementDirectives,
11071
11392
  ...hybridLocationElementDirectives,
11072
11393
  ...hybridImageElementDirectives,
11394
+ ...hybridEntityElementDirectives,
11073
11395
  ...canonical.phrases,
11074
11396
  ...canonical.elementDirectives,
11075
11397
  ...extrasRendered.bodyLines,
@@ -11078,9 +11400,7 @@ ${promptForNext}`;
11078
11400
  const lockBlock = allLockLines.length > 0 ? `${allLockLines.join("\n")}
11079
11401
 
11080
11402
  ` : "";
11081
- const trailingBlock = trailingLines.length > 0 ? `
11082
- ${trailingLines.join("\n")}` : "";
11083
- promptForNext = `${lockBlock}${promptForNext}${trailingBlock}`;
11403
+ promptForNext = `${lockBlock}${insertBeforeStyleSection(promptForNext, trailingLines)}`;
11084
11404
  }
11085
11405
  config2 = {
11086
11406
  ...config2,
@@ -11116,7 +11436,8 @@ ${trailingLines.join("\n")}` : "";
11116
11436
  if (coveredUrl) tokenCoveredUrls.add(coveredUrl);
11117
11437
  }
11118
11438
  const locCanon = renderLocationCanonicalHybrid(nonCharacterRefs, finalIndexByUrl, tokenCoveredUrls);
11119
- const objCanon = renderObjectCreatureCanonicalHybrid(nonCharacterRefs, finalIndexByUrl, tokenCoveredUrls);
11439
+ const objCoveredUrls = mentionedEntityUrls.size > 0 ? /* @__PURE__ */ new Set([...tokenCoveredUrls, ...mentionedEntityUrls]) : tokenCoveredUrls;
11440
+ const objCanon = renderObjectCreatureCanonicalHybrid(nonCharacterRefs, finalIndexByUrl, objCoveredUrls);
11120
11441
  const canonLockLines = [...locCanon.lockLines, ...objCanon.lockLines];
11121
11442
  const canonLockBlock = canonLockLines.length > 0 ? `${canonLockLines.join("\n")}
11122
11443
 
@@ -11127,9 +11448,7 @@ ${trailingLines.join("\n")}` : "";
11127
11448
  ...locCanon.elementDirectives,
11128
11449
  ...objCanon.elementDirectives
11129
11450
  ];
11130
- const canonTrailingBlock = canonTrailingLines.length > 0 ? `
11131
- ${canonTrailingLines.join("\n")}` : "";
11132
- const composedScene = `${canonLockBlock}${scene}${canonTrailingBlock}`;
11451
+ const composedScene = `${canonLockBlock}${insertBeforeStyleSection(scene, canonTrailingLines)}`;
11133
11452
  prompt2 = config2.referenceLockSnippet ? `${config2.referenceLockSnippet}
11134
11453
  ${composedScene}` : composedScene;
11135
11454
  if (marks) marks.directivesPrefix = "";
@@ -11155,31 +11474,33 @@ ${directives}`;
11155
11474
  }
11156
11475
  }
11157
11476
  const styleText2 = style?.trim();
11158
- const styleSuffix2 = styleText2 ? `
11159
- Style: ${getStylePromptHint(styleText2) || styleText2}` : "";
11477
+ const styleLine2 = styleText2 ? `Style: ${getStylePromptHint(styleText2) || styleText2}` : "";
11160
11478
  const negPrompt2 = negativePrompt?.trim();
11161
11479
  let nativeNegativePrompt2;
11162
- let avoidSuffix2 = "";
11480
+ let avoidLine2 = "";
11163
11481
  if (negPrompt2) {
11164
11482
  if (shared.NATIVE_NEGATIVE_PROMPT_MODELS.has(provider)) {
11165
11483
  nativeNegativePrompt2 = negPrompt2.slice(0, shared.getMaxNegativePromptChars(provider));
11166
11484
  } else {
11167
- avoidSuffix2 = `
11168
- Avoid: ${negPrompt2}`;
11485
+ avoidLine2 = `Avoid: ${negPrompt2}`;
11169
11486
  }
11170
11487
  }
11171
- if (marks) {
11172
- marks.styleSuffix = styleSuffix2;
11173
- marks.avoidSuffix = avoidSuffix2;
11174
- }
11175
11488
  const maxLen2 = shared.getMaxImagePromptChars(provider);
11176
- const reserved2 = styleSuffix2.length + avoidSuffix2.length;
11489
+ const preCut2 = controlSuffixes(prompt2, styleLine2, avoidLine2);
11490
+ const reserved2 = preCut2.styleSuffix.length + preCut2.avoidSuffix.length;
11177
11491
  if (prompt2.length + reserved2 > maxLen2) {
11492
+ if (marks) marks.overflowChars += prompt2.length + reserved2 - maxLen2;
11178
11493
  prompt2 = prompt2.slice(0, Math.max(0, maxLen2 - reserved2 - 3)) + "...";
11179
11494
  }
11495
+ const { styleSuffix: styleSuffix2, avoidSuffix: avoidSuffix2 } = controlSuffixes(prompt2, styleLine2, avoidLine2);
11496
+ if (marks) {
11497
+ marks.styleSuffix = styleSuffix2;
11498
+ marks.avoidSuffix = avoidSuffix2;
11499
+ }
11180
11500
  if (marks) marks.bodyBeforeSuffixes = prompt2.slice(marks.directivesPrefix.length);
11181
11501
  prompt2 = prompt2 + styleSuffix2 + avoidSuffix2;
11182
11502
  if (prompt2.length > maxLen2) {
11503
+ if (marks) marks.overflowChars += prompt2.length - maxLen2;
11183
11504
  prompt2 = prompt2.slice(0, maxLen2 - 3) + "...";
11184
11505
  }
11185
11506
  if (!isHybrid) {
@@ -11227,37 +11548,41 @@ Avoid: ${negPrompt2}`;
11227
11548
  let prompt = config2.prompt;
11228
11549
  if (charDescs.length > 0) {
11229
11550
  const wrapperTemplate = resolveTemplate("generate-image-wrapper", userTemplates, flowTemplates);
11230
- prompt = applyTemplate(wrapperTemplate, {
11231
- userPrompt: prompt,
11551
+ const { body, section } = splitStyleSection(prompt);
11552
+ const wrapped = applyTemplate(wrapperTemplate, {
11553
+ userPrompt: body,
11232
11554
  assetDescriptions: charDescs.join(" ")
11233
11555
  });
11556
+ prompt = section.length > 0 ? `${wrapped}${STYLE_SECTION_GAP}${section}` : wrapped;
11234
11557
  }
11235
11558
  const styleText = style?.trim();
11236
- const styleSuffix = styleText ? `
11237
- Style: ${getStylePromptHint(styleText) || styleText}` : "";
11559
+ const styleLine = styleText ? `Style: ${getStylePromptHint(styleText) || styleText}` : "";
11238
11560
  const negPrompt = negativePrompt?.trim();
11239
11561
  let nativeNegativePrompt;
11240
- let avoidSuffix = "";
11562
+ let avoidLine = "";
11241
11563
  if (negPrompt) {
11242
11564
  if (shared.NATIVE_NEGATIVE_PROMPT_MODELS.has(provider)) {
11243
11565
  nativeNegativePrompt = negPrompt.slice(0, shared.getMaxNegativePromptChars(provider));
11244
11566
  } else {
11245
- avoidSuffix = `
11246
- Avoid: ${negPrompt}`;
11567
+ avoidLine = `Avoid: ${negPrompt}`;
11247
11568
  }
11248
11569
  }
11249
- if (marks) {
11250
- marks.styleSuffix = styleSuffix;
11251
- marks.avoidSuffix = avoidSuffix;
11252
- }
11253
11570
  const maxLen = shared.getMaxImagePromptChars(provider);
11254
- const reserved = styleSuffix.length + avoidSuffix.length;
11571
+ const preCut = controlSuffixes(prompt, styleLine, avoidLine);
11572
+ const reserved = preCut.styleSuffix.length + preCut.avoidSuffix.length;
11255
11573
  if (prompt.length + reserved > maxLen) {
11574
+ if (marks) marks.overflowChars += prompt.length + reserved - maxLen;
11256
11575
  prompt = prompt.slice(0, Math.max(0, maxLen - reserved - 3)) + "...";
11257
11576
  }
11577
+ const { styleSuffix, avoidSuffix } = controlSuffixes(prompt, styleLine, avoidLine);
11578
+ if (marks) {
11579
+ marks.styleSuffix = styleSuffix;
11580
+ marks.avoidSuffix = avoidSuffix;
11581
+ }
11258
11582
  if (marks) marks.bodyBeforeSuffixes = prompt;
11259
11583
  prompt = prompt + styleSuffix + avoidSuffix;
11260
11584
  if (prompt.length > maxLen) {
11585
+ if (marks) marks.overflowChars += prompt.length - maxLen;
11261
11586
  prompt = prompt.slice(0, maxLen - 3) + "...";
11262
11587
  }
11263
11588
  const allRefs = referenceImageUrls.length > 0 ? referenceImageUrls : ancestorRefs;
@@ -11267,12 +11592,7 @@ Avoid: ${negPrompt}`;
11267
11592
  return { prompt, nativeNegativePrompt, referenceImageUrls: refsToSend };
11268
11593
  }
11269
11594
  function buildImagePromptSegments(config2, bodySegments) {
11270
- const marks = {
11271
- directivesPrefix: "",
11272
- bodyBeforeSuffixes: "",
11273
- styleSuffix: "",
11274
- avoidSuffix: ""
11275
- };
11595
+ const marks = newAssemblyMarks();
11276
11596
  const result = buildImagePromptInternal(config2, marks);
11277
11597
  let segments = [
11278
11598
  ...marks.directivesPrefix ? [{ text: marks.directivesPrefix, origin: "mention" }] : [],
@@ -11462,19 +11782,35 @@ function hybridRolePhrase(label, letter) {
11462
11782
  return `the ${label} from reference image ${letter}`;
11463
11783
  }
11464
11784
  function expandImageRefTokensHybrid(prompt, refs, finalIndexByUrl) {
11465
- return prompt.replace(IMAGE_TOKEN_PATTERN, (match, num, label) => {
11466
- const n = parseInt(num, 10);
11785
+ const splices = [];
11786
+ for (const m of prompt.matchAll(IMAGE_TOKEN_PATTERN)) {
11787
+ const n = parseInt(m[1], 10);
11467
11788
  const ref = refs[n - 1];
11468
11789
  const finalIdx = ref?.url ? finalIndexByUrl.get(ref.url) : void 0;
11469
- if (!finalIdx) return match;
11470
- return hybridRolePhrase((label ?? "").trim(), slotToLetter(finalIdx));
11471
- });
11790
+ if (!finalIdx) continue;
11791
+ splices.push({
11792
+ offset: m.index ?? 0,
11793
+ length: m[0].length,
11794
+ phrase: hybridRolePhrase((m[2] ?? "").trim(), slotToLetter(finalIdx))
11795
+ });
11796
+ }
11797
+ let resolved = prompt;
11798
+ for (let i = splices.length - 1; i >= 0; i--) {
11799
+ const s = splices[i];
11800
+ resolved = spliceMentionPhrase(resolved, s.offset, s.length, s.phrase);
11801
+ }
11802
+ return resolved;
11472
11803
  }
11473
11804
  function capitalizeLineInitial(line) {
11474
11805
  return line.replace(/[a-zA-Z]/, (c) => c.toUpperCase());
11475
11806
  }
11476
11807
  function buildHybridScene(prompt, refs, finalIndexByUrl) {
11477
- return prompt.split("\n").map((line) => capitalizeLineInitial(expandImageRefTokensHybrid(line, refs, finalIndexByUrl))).join("\n");
11808
+ const lines = prompt.split("\n");
11809
+ const sectionAt = lines.indexOf(STYLE_SECTION_HEADER);
11810
+ return lines.map((line, i) => {
11811
+ const expanded = expandImageRefTokensHybrid(line, refs, finalIndexByUrl);
11812
+ return sectionAt >= 0 && i >= sectionAt ? expanded : capitalizeLineInitial(expanded);
11813
+ }).join("\n");
11478
11814
  }
11479
11815
  function buildIdentityDirectives(prompt, refs, meta3 = [], suppressedCanonicalLocationIds) {
11480
11816
  return collectIdentities(prompt, refs, meta3).map((id) => buildIdentityDirective(id, suppressedCanonicalLocationIds)).filter((s) => s.length > 0).join("\n");
@@ -11692,9 +12028,16 @@ function renderLens(l) {
11692
12028
  if (l.aperture) bits.push(`f/${l.aperture}`);
11693
12029
  return bits.length > 0 ? `Lens: ${bits.join(", ")}.` : "";
11694
12030
  }
11695
-
11696
- // src/direction-registry.ts
11697
- var perId = (full, compact) => (ids, mode) => {
12031
+ var viaFragmentBuilder = (build) => (subject, mode) => {
12032
+ const clause = build(subject, mode).filter((s) => s.length > 0).join(", ");
12033
+ return clause.length > 0 ? [clause] : [];
12034
+ };
12035
+ var viaListBuilder2 = (build) => (ids, mode) => build(ids.length === 1 ? ids[0] : [...ids], mode).filter((s) => s.length > 0);
12036
+ var viaStringBuilder2 = (build) => (ids, mode) => {
12037
+ const s = build(ids.length === 1 ? ids[0] : [...ids], mode);
12038
+ return s.length > 0 ? [s] : [];
12039
+ };
12040
+ var perId2 = (full, compact) => (ids, mode) => {
11698
12041
  const out = [];
11699
12042
  for (const id of ids) {
11700
12043
  const frag = mode === "compact" ? compact(id) : full(id);
@@ -11702,80 +12045,45 @@ var perId = (full, compact) => (ids, mode) => {
11702
12045
  }
11703
12046
  return out;
11704
12047
  };
11705
- var viaListBuilder = (build) => (ids, mode) => build(ids.length === 1 ? ids[0] : [...ids], mode).filter((s) => s.length > 0);
11706
- var viaStringBuilder = (build) => (ids, mode) => {
11707
- const s = build(ids.length === 1 ? ids[0] : [...ids], mode);
11708
- return s.length > 0 ? [s] : [];
11709
- };
11710
- var viaMood = (ids, mode) => buildMoodHints({ mood: ids.length === 1 ? ids[0] : [...ids] }, mode).filter(
11711
- (s) => s.length > 0
11712
- );
11713
- var framing = perId(getFramingPromptHint, getFramingTerm);
11714
- var lighting = perId(getLightingPromptHint, getLightingTerm);
11715
- var exposure = perId(getExposurePromptHint, getExposureTerm);
11716
- var temporal = perId(getTemporalPromptHint, getTemporalTerm);
11717
- var DIRECTION_FIELDS = [
11718
- { key: "cameraMotion", surface: "video", family: "motion", maxPicks: 1, render: perId(getCameraMotionPromptHint, getCameraMotionTerm) },
11719
- // Composition (the Framing catalog, one row per category).
11720
- { key: "shotSize", surface: "both", family: "look", maxPicks: 1, render: framing },
11721
- { key: "angle", surface: "both", family: "look", maxPicks: 1, render: framing },
11722
- { key: "coverage", surface: "both", family: "look", maxPicks: 1, render: framing },
11723
- { key: "composition", surface: "both", family: "look", maxPicks: 2, render: framing },
11724
- { key: "vantage", surface: "both", family: "look", maxPicks: 1, render: framing },
11725
- { key: "pose", surface: "both", family: "look", maxPicks: 1, render: perId(getPosePromptHint, getPoseTerm) },
11726
- { key: "compositionEffect", surface: "both", family: "look", maxPicks: 1, render: perId(getCompositionEffectPromptHint, getCompositionEffectTerm) },
11727
- // Camera.
11728
- { key: "cameraFormat", surface: "both", family: "look", maxPicks: 1, render: perId(getCameraFormatPromptHint, getCameraFormatTerm) },
11729
- { key: "lens", surface: "both", family: "look", maxPicks: 1, render: perId(getLensPromptHint, getLensTerm) },
11730
- // Exposure (stills only — a video's exposure rides its own temporal levers).
11731
- { key: "aperture", surface: "image", family: "look", maxPicks: 1, render: exposure },
11732
- { key: "shutterSpeed", surface: "image", family: "look", maxPicks: 1, render: exposure },
11733
- { key: "isoValue", surface: "image", family: "look", maxPicks: 1, render: exposure },
11734
- // Light (the Lighting catalog, one row per category).
11735
- { key: "timeOfDay", surface: "both", family: "look", maxPicks: 1, render: lighting },
11736
- { key: "lightingStyle", surface: "both", family: "look", maxPicks: 2, render: lighting },
11737
- { key: "lightingDirection", surface: "both", family: "look", maxPicks: 1, render: lighting },
11738
- { key: "lightingRatio", surface: "both", family: "look", maxPicks: 1, render: lighting },
11739
- { key: "colorTemperature", surface: "both", family: "look", maxPicks: 1, render: lighting },
11740
- { key: "colorLook", surface: "both", family: "look", maxPicks: 1, render: perId(getColorLookPromptHint, getColorLookTerm) },
11741
- { key: "atmosphere", surface: "both", family: "look", maxPicks: 2, render: viaListBuilder(buildAtmosphereHints) },
11742
- { key: "postProcess", surface: "image", family: "look", maxPicks: 2, render: viaListBuilder(buildPostProcessHints) },
11743
- // Style.
11744
- { key: "style", surface: "both", family: "look", maxPicks: 1, render: perId(getStylePromptHint, getStyleTerm) },
11745
- { key: "mood", surface: "both", family: "look", maxPicks: 2, render: viaMood },
11746
- { key: "aesthetic", surface: "both", family: "look", maxPicks: 2, render: viaStringBuilder(buildAestheticHints) },
11747
- { key: "photoGenre", surface: "image", family: "look", maxPicks: 1, render: perId(getPhotoGenrePromptHint, getPhotoGenreTerm) },
11748
- { key: "photographer", surface: "image", family: "look", maxPicks: 2, render: viaStringBuilder(buildPhotographerHints) },
11749
- { key: "renderQuality", surface: "image", family: "look", maxPicks: 1, render: perId(getRenderQualityPromptHint, getRenderQualityTerm) },
11750
- // Scene.
11751
- { key: "setting", surface: "both", family: "look", maxPicks: 1, render: perId(getSettingPromptHint, getSettingTerm) },
11752
- { key: "era", surface: "both", family: "look", maxPicks: 1, render: perId(getEraPromptHint, getEraTerm) },
11753
- { key: "backdrop", surface: "both", family: "look", maxPicks: 1, render: perId(getBackdropPromptHint, getBackdropTerm) },
11754
- // Motion & time.
11755
- { key: "actionFx", surface: "video", family: "motion", maxPicks: 2, render: viaListBuilder(buildActionFxHints) },
11756
- { key: "temporalSpeed", surface: "video", family: "motion", maxPicks: 1, render: temporal },
11757
- { key: "temporalFreeze", surface: "video", family: "motion", maxPicks: 1, render: temporal },
11758
- { key: "temporalDirection", surface: "video", family: "motion", maxPicks: 1, render: temporal },
11759
- { key: "temporalShutter", surface: "video", family: "motion", maxPicks: 1, render: temporal },
11760
- { key: "transition", surface: "video", family: "motion", maxPicks: 2, render: perId(getTransitionPromptHint, getTransitionTerm) },
11761
- { key: "loopSubject", surface: "video", family: "motion", maxPicks: 1, render: perId(getLoopSubjectPromptHint, getLoopSubjectTerm) },
11762
- // ── LEGACY BLOCK — see the table doc above. Placed LAST, in today's exact
11763
- // `composePromptText` order, so every pre-registry caller is byte-identical.
11764
- { key: "framingId", surface: "both", family: "look", maxPicks: 1, render: framing },
11765
- { key: "framingAngleId", surface: "both", family: "look", maxPicks: 1, render: framing },
11766
- { key: "lightingId", surface: "both", family: "look", maxPicks: 1, render: lighting },
11767
- { key: "lensId", surface: "both", family: "look", maxPicks: 1, render: perId(getLensPromptHint, getLensTerm) },
11768
- { key: "cameraFormatId", surface: "both", family: "look", maxPicks: 1, render: perId(getCameraFormatPromptHint, getCameraFormatTerm) }
12048
+ var SUBJECT_FIELDS = [
12049
+ { key: "person", kind: "group", surface: "both", render: viaFragmentBuilder(buildPersonHints) },
12050
+ { key: "styling", kind: "group", surface: "both", render: viaFragmentBuilder(buildStylingHints) },
12051
+ { key: "heldProp", kind: "ids", surface: "both", maxPicks: 2, render: viaListBuilder2(buildHeldPropHints) },
12052
+ { key: "material", kind: "ids", surface: "both", maxPicks: 2, render: viaStringBuilder2(buildMaterialHints) },
12053
+ { key: "animal", kind: "ids", surface: "both", maxPicks: 1, render: perId2(shared.getAnimalPromptHint, shared.getAnimalTerm) }
11769
12054
  ];
11770
- var DIRECTION_KEYS = DIRECTION_FIELDS.map((f) => f.key);
11771
- var IMAGE_HINT_MODE_DEFAULT = "full";
11772
- var VIDEO_HINT_MODE_DEFAULT = { look: "full", motion: "compact" };
11773
- function modeForFamily(mode, family) {
11774
- return typeof mode === "string" ? mode : family === "motion" ? mode.motion : mode.look;
12055
+ var SUBJECT_FOLD_KEYS = SUBJECT_FIELDS.map((f) => f.key);
12056
+ var IDS_ROWS = SUBJECT_FIELDS.filter((f) => f.kind === "ids");
12057
+ var SUBJECT_CUSTOM_AGE_KEY = "customAge";
12058
+ var CUSTOM_AGE_MIN = 0;
12059
+ var CUSTOM_AGE_MAX = 120;
12060
+ var SUBJECT_KEYS = [
12061
+ ...PERSON_DIMENSION_ORDER.map((d) => PERSON_FIELD_BY_DIMENSION[d]),
12062
+ SUBJECT_CUSTOM_AGE_KEY,
12063
+ ...STYLING_DIMENSION_ORDER.map((d) => STYLING_FIELD_BY_DIMENSION[d]),
12064
+ ...IDS_ROWS.map((f) => f.key)
12065
+ ];
12066
+ function getRegisteredSubjectKeys() {
12067
+ const fieldByDimension = getRegisteredPersonFieldByDimension();
12068
+ const personFields = [];
12069
+ for (const dimension of getRegisteredPersonDimensionOrder()) {
12070
+ const field = fieldByDimension[dimension];
12071
+ if (field) personFields.push(field);
12072
+ }
12073
+ return [
12074
+ ...personFields,
12075
+ SUBJECT_CUSTOM_AGE_KEY,
12076
+ ...STYLING_DIMENSION_ORDER.map((d) => STYLING_FIELD_BY_DIMENSION[d]),
12077
+ ...IDS_ROWS.map((f) => f.key)
12078
+ ];
11775
12079
  }
11776
- var DIRECTION_ARRAY_CEILING = 8;
11777
- var DIRECTION_ID_MAX_CHARS = 100;
11778
- function normalizeDirectionIds(value, maxPicks) {
12080
+ var SUBJECT_IMAGE_HINT_MODE_DEFAULT = "full";
12081
+ var SUBJECT_VIDEO_HINT_MODE_DEFAULT = "compact";
12082
+ var SUBJECT_ARRAY_CEILING = DIRECTION_ARRAY_CEILING;
12083
+ var SUBJECT_ID_MAX_CHARS = DIRECTION_ID_MAX_CHARS;
12084
+ var MAX_SUBJECT_KEYS = 128;
12085
+ var SUBJECT_KEY_MAX_CHARS = 64;
12086
+ function normalizeIds(value, maxPicks) {
11779
12087
  const ids = [];
11780
12088
  if (typeof value === "string") {
11781
12089
  if (value) ids.push(value);
@@ -11787,22 +12095,54 @@ function normalizeDirectionIds(value, maxPicks) {
11787
12095
  }
11788
12096
  return ids.slice(0, maxPicks);
11789
12097
  }
11790
- function directionFieldsForSurface(surface) {
11791
- return DIRECTION_FIELDS.filter((f) => f.surface === "both" || f.surface === surface);
12098
+ function limitByField() {
12099
+ const out = /* @__PURE__ */ new Map();
12100
+ const personFieldByDimension = getRegisteredPersonFieldByDimension();
12101
+ for (const dimension of getRegisteredPersonDimensionOrder()) {
12102
+ const field = personFieldByDimension[dimension];
12103
+ if (field) out.set(field, getPersonDimensionLimit(dimension));
12104
+ }
12105
+ for (const dimension of STYLING_DIMENSION_ORDER) {
12106
+ out.set(
12107
+ STYLING_FIELD_BY_DIMENSION[dimension],
12108
+ getStylingDimensionLimit(dimension)
12109
+ );
12110
+ }
12111
+ for (const row of IDS_ROWS) out.set(row.key, row.maxPicks);
12112
+ return out;
11792
12113
  }
11793
- function renderDirectionHints(direction, opts) {
11794
- if (!direction) return [];
12114
+ function normalizeSubjectFields(subject) {
12115
+ if (!subject || typeof subject !== "object" || Array.isArray(subject)) return void 0;
12116
+ const src = subject;
12117
+ const limits = limitByField();
12118
+ const out = {};
12119
+ const rawAge = src[SUBJECT_CUSTOM_AGE_KEY];
12120
+ if (typeof rawAge === "number" && Number.isFinite(rawAge)) {
12121
+ out[SUBJECT_CUSTOM_AGE_KEY] = Math.max(
12122
+ CUSTOM_AGE_MIN,
12123
+ Math.min(CUSTOM_AGE_MAX, Math.round(rawAge))
12124
+ );
12125
+ }
12126
+ for (const [field, limit] of limits) {
12127
+ const ids = normalizeIds(src[field], limit);
12128
+ if (ids.length === 0) continue;
12129
+ out[field] = ids.length === 1 ? ids[0] : ids;
12130
+ }
12131
+ return Object.keys(out).length > 0 ? out : void 0;
12132
+ }
12133
+ function subjectFieldsForSurface(surface) {
12134
+ return SUBJECT_FIELDS.filter((f) => f.surface === "both" || f.surface === surface);
12135
+ }
12136
+ function renderSubjectHints(subject, opts) {
12137
+ const bag = normalizeSubjectFields(subject);
12138
+ if (!bag) return [];
11795
12139
  const mode = opts.mode ?? "full";
11796
12140
  const out = [];
11797
12141
  const seen = /* @__PURE__ */ new Set();
11798
- for (const spec of DIRECTION_FIELDS) {
12142
+ for (const spec of SUBJECT_FIELDS) {
11799
12143
  if (spec.surface !== "both" && spec.surface !== opts.surface) continue;
11800
- const ids = normalizeDirectionIds(
11801
- direction[spec.key],
11802
- spec.maxPicks
11803
- );
11804
- if (ids.length === 0) continue;
11805
- for (const hint of spec.render(ids, modeForFamily(mode, spec.family))) {
12144
+ const hints = spec.kind === "group" ? spec.render(bag, mode) : spec.render(normalizeIds(bag[spec.key], spec.maxPicks), mode);
12145
+ for (const hint of hints) {
11806
12146
  if (hint.length > 0 && !seen.has(hint)) {
11807
12147
  seen.add(hint);
11808
12148
  out.push(hint);
@@ -11812,11 +12152,15 @@ function renderDirectionHints(direction, opts) {
11812
12152
  return out;
11813
12153
  }
11814
12154
 
11815
- // src/prompt-hint-join.ts
11816
- var PROMPT_HINT_SEPARATOR = ". ";
11817
- function joinPromptHints(userPrompt, hints) {
11818
- if (hints.length === 0) return userPrompt;
11819
- return [userPrompt.trim(), ...hints].filter((p) => p.length > 0).join(PROMPT_HINT_SEPARATOR);
12155
+ // src/hint-shedding.ts
12156
+ function keepableDirectionHints(hintClauses, kept, deficit, clauseCosts) {
12157
+ let remaining = deficit;
12158
+ let next = kept;
12159
+ while (next > 0 && remaining > 0) {
12160
+ next -= 1;
12161
+ remaining -= clauseCosts?.[next] ?? hintClauses[next].length + PROMPT_HINT_SEPARATOR.length;
12162
+ }
12163
+ return next;
11820
12164
  }
11821
12165
 
11822
12166
  // src/ref-binding.ts
@@ -12168,9 +12512,7 @@ function resolveVideoReferenceCore(args) {
12168
12512
  const lockBlock = lockLines.length > 0 ? `${lockLines.join("\n")}
12169
12513
 
12170
12514
  ` : "";
12171
- const trailingBlock = trailingLines.length > 0 ? `
12172
- ${trailingLines.join("\n")}` : "";
12173
- finalPrompt = `${lockBlock}${finalPrompt}${trailingBlock}`;
12515
+ finalPrompt = `${lockBlock}${insertBeforeStyleSection(finalPrompt, trailingLines)}`;
12174
12516
  } else {
12175
12517
  const allFallbackLines = [...fallbackDirectiveLines, ...extraDirectiveLines];
12176
12518
  if (allFallbackLines.length > 0) {
@@ -12404,17 +12746,34 @@ function assembleSunoInput(input) {
12404
12746
  }
12405
12747
 
12406
12748
  // src/assemble-image-input.ts
12407
- function composePromptText(userPrompt, direction, structured) {
12408
- const hints = [
12409
- ...renderDirectionHints(direction, { surface: "image", mode: IMAGE_HINT_MODE_DEFAULT }),
12410
- structured ? renderStructuredFields(structured) : ""
12411
- ].filter((p) => p.length > 0);
12412
- return joinPromptHints(userPrompt, hints);
12749
+ function renderImageHintPieces(subject, direction, structured) {
12750
+ return {
12751
+ hintClauses: [
12752
+ ...asBodyClauses(
12753
+ renderSubjectHints(subject, {
12754
+ surface: "image",
12755
+ mode: SUBJECT_IMAGE_HINT_MODE_DEFAULT
12756
+ })
12757
+ ),
12758
+ ...partitionStyleClauses(direction, {
12759
+ surface: "image",
12760
+ mode: IMAGE_HINT_MODE_DEFAULT
12761
+ })
12762
+ ].filter((c) => c.text.length > 0),
12763
+ structuredFragment: structured ? renderStructuredFields(structured) : ""
12764
+ };
12765
+ }
12766
+ function composePromptText(userPrompt, pieces, keptHintClauses) {
12767
+ return composeSectionedPrompt(
12768
+ userPrompt,
12769
+ pieces.hintClauses.slice(0, keptHintClauses),
12770
+ pieces.structuredFragment
12771
+ );
12413
12772
  }
12414
12773
  function assembleImageInput(input) {
12415
- const prompt = composePromptText(input.userPrompt, input.direction, input.structured);
12416
- const result = buildImagePrompt({
12417
- prompt,
12774
+ const pieces = renderImageHintPieces(input.subject, input.direction, input.structured);
12775
+ const assembleWith = (keptHintClauses) => buildImagePromptWithOverflow({
12776
+ prompt: composePromptText(input.userPrompt, pieces, keptHintClauses),
12418
12777
  provider: input.provider,
12419
12778
  ...input.connectedReferences !== void 0 ? { connectedReferences: input.connectedReferences } : {},
12420
12779
  // Manual uploads / direct refs ride the builder's reference-URL channel so
@@ -12437,6 +12796,21 @@ function assembleImageInput(input) {
12437
12796
  ...input.sourceNodeIdById !== void 0 ? { sourceNodeIdById: input.sourceNodeIdById } : {},
12438
12797
  ...input.skipCharacterMentions !== void 0 ? { skipCharacterMentions: input.skipCharacterMentions } : {}
12439
12798
  });
12799
+ let kept = pieces.hintClauses.length;
12800
+ let fitted = assembleWith(kept);
12801
+ if (fitted.overflowChars > 0) {
12802
+ const costs = sectionedClauseCosts(
12803
+ input.userPrompt,
12804
+ pieces.hintClauses,
12805
+ pieces.structuredFragment
12806
+ );
12807
+ const texts = pieces.hintClauses.map((c) => c.text);
12808
+ while (fitted.overflowChars > 0 && kept > 0) {
12809
+ kept = keepableDirectionHints(texts, kept, fitted.overflowChars, costs);
12810
+ fitted = assembleWith(kept);
12811
+ }
12812
+ }
12813
+ const { overflowChars, ...result } = fitted;
12440
12814
  if (input.throwOnEmpty && !result.prompt.trim()) {
12441
12815
  throw new Error(
12442
12816
  "No prompt \u2014 type one, bind a character, or pick a cinematography direction"
@@ -12447,15 +12821,35 @@ function assembleImageInput(input) {
12447
12821
 
12448
12822
  // src/assemble-video-input.ts
12449
12823
  function composeVideoPromptText(userPrompt, direction, structured, opts) {
12450
- const hints = [
12451
- ...renderDirectionHints(direction, {
12824
+ const hintClauses = [
12825
+ ...asBodyClauses(
12826
+ renderSubjectHints(opts?.subject, {
12827
+ surface: "video",
12828
+ mode: opts?.subjectHintMode ?? SUBJECT_VIDEO_HINT_MODE_DEFAULT
12829
+ })
12830
+ ),
12831
+ ...partitionStyleClauses(direction, {
12452
12832
  surface: "video",
12453
12833
  mode: opts?.hintMode ?? VIDEO_HINT_MODE_DEFAULT
12454
- }),
12455
- structured ? renderStructuredFields(structured) : ""
12456
- ].filter((p) => p.length > 0);
12457
- if (hints.length === 0) return userPrompt;
12458
- return joinPromptHints(userPrompt ?? "", hints);
12834
+ })
12835
+ ].filter((c) => c.text.length > 0);
12836
+ const structuredFragment = structured ? renderStructuredFields(structured) : "";
12837
+ const composeWith = (kept2) => composeSectionedPrompt(userPrompt, hintClauses.slice(0, kept2), structuredFragment);
12838
+ const cap = opts?.cap;
12839
+ if (cap === void 0) return composeWith(hintClauses.length);
12840
+ const frame = opts?.frame ?? ((body2) => body2);
12841
+ let kept = hintClauses.length;
12842
+ let body = composeWith(kept);
12843
+ let framedLength = frame(body)?.length ?? 0;
12844
+ if (framedLength <= cap) return body;
12845
+ const costs = sectionedClauseCosts(userPrompt, hintClauses, structuredFragment);
12846
+ const texts = hintClauses.map((c) => c.text);
12847
+ while (framedLength > cap && kept > 0) {
12848
+ kept = keepableDirectionHints(texts, kept, framedLength - cap, costs);
12849
+ body = composeWith(kept);
12850
+ framedLength = frame(body)?.length ?? 0;
12851
+ }
12852
+ return body;
12459
12853
  }
12460
12854
 
12461
12855
  // src/read-node-direction.ts
@@ -12480,6 +12874,31 @@ function readDirectionFields(value) {
12480
12874
  }
12481
12875
  return Object.keys(out).length > 0 ? out : void 0;
12482
12876
  }
12877
+ function readSubjectFields(value) {
12878
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
12879
+ const src = value;
12880
+ const out = {};
12881
+ for (const key of getRegisteredSubjectKeys()) {
12882
+ const v = src[key];
12883
+ if (key === SUBJECT_CUSTOM_AGE_KEY) {
12884
+ if (typeof v === "number" && Number.isFinite(v)) out[key] = v;
12885
+ continue;
12886
+ }
12887
+ if (typeof v === "string") {
12888
+ if (v.length > 0 && v.length <= SUBJECT_ID_MAX_CHARS) out[key] = v;
12889
+ } else if (Array.isArray(v)) {
12890
+ const kept = [];
12891
+ for (const x of v) {
12892
+ if (kept.length >= SUBJECT_ARRAY_CEILING) break;
12893
+ if (typeof x === "string" && x.length > 0 && x.length <= SUBJECT_ID_MAX_CHARS) {
12894
+ kept.push(x);
12895
+ }
12896
+ }
12897
+ if (kept.length > 0) out[key] = kept;
12898
+ }
12899
+ }
12900
+ return Object.keys(out).length > 0 ? out : void 0;
12901
+ }
12483
12902
  var GENDERS = ["man", "woman", "child", "non-binary"];
12484
12903
  var MAX_STRUCTURED_VALUE_CHARS = 200;
12485
12904
  var PERSON_FIELDS2 = {
@@ -33804,6 +34223,7 @@ exports.EXPOSURE_SETTINGS = EXPOSURE_SETTINGS;
33804
34223
  exports.FACTORY_PRESETS = FACTORY_PRESETS;
33805
34224
  exports.FACTORY_SNIPPETS = FACTORY_SNIPPETS;
33806
34225
  exports.FILM_STILL_PREFIX = FILM_STILL_PREFIX;
34226
+ exports.FILM_STYLE_KEYS = FILM_STYLE_KEYS;
33807
34227
  exports.FRAMINGS = FRAMINGS;
33808
34228
  exports.FRAMING_CATEGORY_LABELS = FRAMING_CATEGORY_LABELS;
33809
34229
  exports.FRAMING_CATEGORY_ORDER = FRAMING_CATEGORY_ORDER;
@@ -33839,6 +34259,7 @@ exports.MATERIAL_IDS = MATERIAL_IDS;
33839
34259
  exports.MAX_SELECTED_BY_DIMENSION = MAX_SELECTED_BY_DIMENSION;
33840
34260
  exports.MAX_SELECTED_BY_FRAMING_CATEGORY = MAX_SELECTED_BY_FRAMING_CATEGORY;
33841
34261
  exports.MAX_SELECTED_BY_STYLING_DIMENSION = MAX_SELECTED_BY_STYLING_DIMENSION;
34262
+ exports.MAX_SUBJECT_KEYS = MAX_SUBJECT_KEYS;
33842
34263
  exports.MOODS = MOODS;
33843
34264
  exports.MOOD_CATEGORY_LABELS = MOOD_CATEGORY_LABELS;
33844
34265
  exports.MOOD_CATEGORY_ORDER = MOOD_CATEGORY_ORDER;
@@ -33913,11 +34334,22 @@ exports.SNIPPET_MEDIA_VALUES = SNIPPET_MEDIA_VALUES;
33913
34334
  exports.STYLES = STYLES;
33914
34335
  exports.STYLE_IDS = STYLE_IDS;
33915
34336
  exports.STYLE_PRESETS = STYLE_PRESETS;
34337
+ exports.STYLE_SECTION_GAP = STYLE_SECTION_GAP;
34338
+ exports.STYLE_SECTION_HEADER = STYLE_SECTION_HEADER;
33916
34339
  exports.STYLINGS = STYLINGS;
33917
34340
  exports.STYLING_DIMENSION_LABELS = STYLING_DIMENSION_LABELS;
33918
34341
  exports.STYLING_DIMENSION_ORDER = STYLING_DIMENSION_ORDER;
33919
34342
  exports.STYLING_FIELD_BY_DIMENSION = STYLING_FIELD_BY_DIMENSION;
33920
34343
  exports.STYLING_IDS = STYLING_IDS;
34344
+ exports.SUBJECT_ARRAY_CEILING = SUBJECT_ARRAY_CEILING;
34345
+ exports.SUBJECT_CUSTOM_AGE_KEY = SUBJECT_CUSTOM_AGE_KEY;
34346
+ exports.SUBJECT_FIELDS = SUBJECT_FIELDS;
34347
+ exports.SUBJECT_FOLD_KEYS = SUBJECT_FOLD_KEYS;
34348
+ exports.SUBJECT_ID_MAX_CHARS = SUBJECT_ID_MAX_CHARS;
34349
+ exports.SUBJECT_IMAGE_HINT_MODE_DEFAULT = SUBJECT_IMAGE_HINT_MODE_DEFAULT;
34350
+ exports.SUBJECT_KEYS = SUBJECT_KEYS;
34351
+ exports.SUBJECT_KEY_MAX_CHARS = SUBJECT_KEY_MAX_CHARS;
34352
+ exports.SUBJECT_VIDEO_HINT_MODE_DEFAULT = SUBJECT_VIDEO_HINT_MODE_DEFAULT;
33921
34353
  exports.TEMPORALS = TEMPORALS;
33922
34354
  exports.TEMPORAL_CATEGORY_LABELS = TEMPORAL_CATEGORY_LABELS;
33923
34355
  exports.TEMPORAL_CATEGORY_ORDER = TEMPORAL_CATEGORY_ORDER;
@@ -33956,6 +34388,7 @@ exports.applyPickerJson = applyPickerJson;
33956
34388
  exports.applyPromptAffixes = applyPromptAffixes;
33957
34389
  exports.applyReferenceOrderToVideo = applyReferenceOrderToVideo;
33958
34390
  exports.applyTemplate = applyTemplate;
34391
+ exports.asBodyClauses = asBodyClauses;
33959
34392
  exports.assembleImageInput = assembleImageInput;
33960
34393
  exports.assembleSunoInput = assembleSunoInput;
33961
34394
  exports.buildActionFxHints = buildActionFxHints;
@@ -33978,6 +34411,7 @@ exports.buildIdentityDirectives = buildIdentityDirectives;
33978
34411
  exports.buildIdentityLockLine = buildIdentityLockLine;
33979
34412
  exports.buildImagePrompt = buildImagePrompt;
33980
34413
  exports.buildImagePromptSegments = buildImagePromptSegments;
34414
+ exports.buildImagePromptWithOverflow = buildImagePromptWithOverflow;
33981
34415
  exports.buildInstrumentationHints = buildInstrumentationHints;
33982
34416
  exports.buildInstrumentationTerms = buildInstrumentationTerms;
33983
34417
  exports.buildLightingHints = buildLightingHints;
@@ -34028,6 +34462,7 @@ exports.composeCameraMotionTermFromConnections = composeCameraMotionTermFromConn
34028
34462
  exports.composeCharacterFxHintFromConnections = composeCharacterFxHintFromConnections;
34029
34463
  exports.composeNegative = composeNegative;
34030
34464
  exports.composePickerCatalogs = composePickerCatalogs;
34465
+ exports.composeSectionedPrompt = composeSectionedPrompt;
34031
34466
  exports.composeSoundHintFromConnections = composeSoundHintFromConnections;
34032
34467
  exports.composeTransitionHintFromConnections = composeTransitionHintFromConnections;
34033
34468
  exports.composeVideoPromptText = composeVideoPromptText;
@@ -34036,6 +34471,7 @@ exports.computeNodePrompt = computeNodePrompt;
34036
34471
  exports.computePackSidecarCoverage = computePackSidecarCoverage;
34037
34472
  exports.deriveTerm = deriveTerm;
34038
34473
  exports.directionFieldsForSurface = directionFieldsForSurface;
34474
+ exports.endsInsideStyleSection = endsInsideStyleSection;
34039
34475
  exports.expandImagePositionRefs = expandImagePositionRefs;
34040
34476
  exports.expandImageRefTokens = expandImageRefTokens;
34041
34477
  exports.filmStillPrefix = filmStillPrefix;
@@ -34168,6 +34604,7 @@ exports.getRegisteredPersonDimensionLabels = getRegisteredPersonDimensionLabels;
34168
34604
  exports.getRegisteredPersonDimensionOrder = getRegisteredPersonDimensionOrder;
34169
34605
  exports.getRegisteredPersonFieldByDimension = getRegisteredPersonFieldByDimension;
34170
34606
  exports.getRegisteredPickerCatalogs = getRegisteredPickerCatalogs;
34607
+ exports.getRegisteredSubjectKeys = getRegisteredSubjectKeys;
34171
34608
  exports.getRenderQuality = getRenderQuality;
34172
34609
  exports.getRenderQualityLabel = getRenderQualityLabel;
34173
34610
  exports.getRenderQualityPromptHint = getRenderQualityPromptHint;
@@ -34221,6 +34658,7 @@ exports.getWardrobePromptHint = getWardrobePromptHint;
34221
34658
  exports.groupFactoryPresets = groupFactoryPresets;
34222
34659
  exports.hasUpstreamCharacter = hasUpstreamCharacter;
34223
34660
  exports.identityRefsSentence = identityRefsSentence;
34661
+ exports.insertBeforeStyleSection = insertBeforeStyleSection;
34224
34662
  exports.isAnalyzablePicker = isAnalyzablePicker;
34225
34663
  exports.isInstrumentalVocal = isInstrumentalVocal;
34226
34664
  exports.isSuspiciousDerivedTerm = isSuspiciousDerivedTerm;
@@ -34228,12 +34666,15 @@ exports.isVantageFraming = isVantageFraming;
34228
34666
  exports.isWizardSupported = isWizardSupported;
34229
34667
  exports.joinPromptHints = joinPromptHints;
34230
34668
  exports.joinPromptParts = joinPromptParts;
34669
+ exports.keepableDirectionHints = keepableDirectionHints;
34231
34670
  exports.listPickerCatalogs = listPickerCatalogs;
34232
34671
  exports.migratePersonValue = migratePersonValue;
34233
34672
  exports.modeForFamily = modeForFamily;
34234
34673
  exports.nodeHasInlinePrompt = nodeHasInlinePrompt;
34235
34674
  exports.nodeHasPromptField = nodeHasPromptField;
34236
34675
  exports.nodeSupportsPromptAffixes = nodeSupportsPromptAffixes;
34676
+ exports.normalizeSubjectFields = normalizeSubjectFields;
34677
+ exports.partitionStyleClauses = partitionStyleClauses;
34237
34678
  exports.personPacksVersion = personPacksVersion;
34238
34679
  exports.pickerFanoutTargets = pickerFanoutTargets;
34239
34680
  exports.projectAllCatalogs = projectAllCatalogs;
@@ -34244,11 +34685,15 @@ exports.promptFieldCarriesAffixes = promptFieldCarriesAffixes;
34244
34685
  exports.promptPartSeparator = promptPartSeparator;
34245
34686
  exports.readDirectionFields = readDirectionFields;
34246
34687
  exports.readStructuredFields = readStructuredFields;
34688
+ exports.readSubjectFields = readSubjectFields;
34247
34689
  exports.referenceRulesBlock = referenceRulesBlock;
34248
34690
  exports.registerCatalogPack = registerCatalogPack;
34249
34691
  exports.registerPersonPack = registerPersonPack;
34692
+ exports.renderDirectionHintClauses = renderDirectionHintClauses;
34250
34693
  exports.renderDirectionHints = renderDirectionHints;
34251
34694
  exports.renderStructuredFields = renderStructuredFields;
34695
+ exports.renderStyleSection = renderStyleSection;
34696
+ exports.renderSubjectHints = renderSubjectHints;
34252
34697
  exports.resetCatalogPacks = resetCatalogPacks;
34253
34698
  exports.resetPersonPacks = resetPersonPacks;
34254
34699
  exports.resolveBrandInput = resolveBrandInput;
@@ -34263,6 +34708,11 @@ exports.resolveTemplate = resolveTemplate;
34263
34708
  exports.resolveTerm = resolveTerm;
34264
34709
  exports.resolveVeoI2vInputs = resolveVeoI2vInputs;
34265
34710
  exports.resolveVideoReferenceCore = resolveVideoReferenceCore;
34711
+ exports.sectionedClauseCosts = sectionedClauseCosts;
34712
+ exports.splitStyleSection = splitStyleSection;
34713
+ exports.styleSectionFromClauses = styleSectionFromClauses;
34714
+ exports.styleSlotFor = styleSlotFor;
34715
+ exports.subjectFieldsForSurface = subjectFieldsForSurface;
34266
34716
  exports.summarizePickerCatalogs = summarizePickerCatalogs;
34267
34717
  exports.toIdentityLockMode = toIdentityLockMode;
34268
34718
  exports.truncateForField = truncateForField;