@nodaro/prompts 1.38.0 → 1.39.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 (109) hide show
  1. package/dist/{chunk-LVSH7K7R.cjs → chunk-22KZOAGL.cjs} +81 -42
  2. package/dist/chunk-22KZOAGL.cjs.map +1 -0
  3. package/dist/chunk-2P26SZX5.cjs +414 -0
  4. package/dist/chunk-2P26SZX5.cjs.map +1 -0
  5. package/dist/{chunk-CVPB2N3Z.cjs → chunk-3INXNDV7.cjs} +102 -4
  6. package/dist/chunk-3INXNDV7.cjs.map +1 -0
  7. package/dist/{chunk-7XKHDXCL.cjs → chunk-3U7X6JZN.cjs} +13 -12
  8. package/dist/chunk-3U7X6JZN.cjs.map +1 -0
  9. package/dist/chunk-4GKTOVFM.cjs +8 -0
  10. package/dist/chunk-4GKTOVFM.cjs.map +1 -0
  11. package/dist/{chunk-HRBBNODV.js → chunk-6GVFJLDL.js} +4 -3
  12. package/dist/chunk-6GVFJLDL.js.map +1 -0
  13. package/dist/{chunk-NO6XHX4R.js → chunk-6MPZHFIS.js} +99 -6
  14. package/dist/chunk-6MPZHFIS.js.map +1 -0
  15. package/dist/{chunk-4FDNVNJE.js → chunk-7XMTVYS5.js} +3 -76
  16. package/dist/chunk-7XMTVYS5.js.map +1 -0
  17. package/dist/{chunk-QDEWBBUR.js → chunk-DUXX6CBX.js} +12 -143
  18. package/dist/chunk-DUXX6CBX.js.map +1 -0
  19. package/dist/chunk-EAYEZXWT.js +388 -0
  20. package/dist/chunk-EAYEZXWT.js.map +1 -0
  21. package/dist/chunk-GILUVQQJ.js +6 -0
  22. package/dist/chunk-GILUVQQJ.js.map +1 -0
  23. package/dist/{chunk-KIYNOOPJ.js → chunk-GTDKDD7Y.js} +12 -6
  24. package/dist/chunk-GTDKDD7Y.js.map +1 -0
  25. package/dist/{chunk-TLVX5PXD.cjs → chunk-IUCNJAHW.cjs} +13 -7
  26. package/dist/chunk-IUCNJAHW.cjs.map +1 -0
  27. package/dist/{chunk-ZFIXQRBZ.cjs → chunk-KUBZVWME.cjs} +2 -80
  28. package/dist/chunk-KUBZVWME.cjs.map +1 -0
  29. package/dist/{chunk-JKCR6WRZ.cjs → chunk-NMWKXAYA.cjs} +2 -2
  30. package/dist/{chunk-JKCR6WRZ.cjs.map → chunk-NMWKXAYA.cjs.map} +1 -1
  31. package/dist/{chunk-LK2CZK5U.cjs → chunk-PWWM7Q3T.cjs} +7 -7
  32. package/dist/{chunk-LK2CZK5U.cjs.map → chunk-PWWM7Q3T.cjs.map} +1 -1
  33. package/dist/{chunk-N7TOWXDJ.js → chunk-QDUTMOF4.js} +3 -3
  34. package/dist/{chunk-N7TOWXDJ.js.map → chunk-QDUTMOF4.js.map} +1 -1
  35. package/dist/{chunk-MENBHEAY.cjs → chunk-WRM52ZBN.cjs} +19 -161
  36. package/dist/chunk-WRM52ZBN.cjs.map +1 -0
  37. package/dist/chunk-XIWWDGT5.cjs +86 -0
  38. package/dist/chunk-XIWWDGT5.cjs.map +1 -0
  39. package/dist/{chunk-CEPVQQVO.js → chunk-XRLOMTBZ.js} +2 -2
  40. package/dist/{chunk-CEPVQQVO.js.map → chunk-XRLOMTBZ.js.map} +1 -1
  41. package/dist/{chunk-QBEZMR2F.js → chunk-XU54YJXQ.js} +54 -15
  42. package/dist/chunk-XU54YJXQ.js.map +1 -0
  43. package/dist/chunk-Z3SZXWRU.js +80 -0
  44. package/dist/chunk-Z3SZXWRU.js.map +1 -0
  45. package/dist/entries/assembly.cjs +26 -25
  46. package/dist/entries/assembly.d.cts +32 -4
  47. package/dist/entries/assembly.d.ts +32 -4
  48. package/dist/entries/assembly.js +5 -4
  49. package/dist/entries/doctrine.cjs +46 -46
  50. package/dist/entries/doctrine.d.cts +3 -3
  51. package/dist/entries/doctrine.d.ts +3 -3
  52. package/dist/entries/doctrine.js +1 -1
  53. package/dist/entries/picker-catalogs.cjs +42 -42
  54. package/dist/entries/picker-catalogs.js +3 -3
  55. package/dist/entries/presets.cjs +49 -26
  56. package/dist/entries/presets.d.cts +51 -1
  57. package/dist/entries/presets.d.ts +51 -1
  58. package/dist/entries/presets.js +4 -1
  59. package/dist/entries/prompt-builder.cjs +135 -98
  60. package/dist/entries/prompt-builder.d.cts +22 -11
  61. package/dist/entries/prompt-builder.d.ts +22 -11
  62. package/dist/entries/prompt-builder.js +4 -3
  63. package/dist/entries/prompt-fields.cjs +41 -40
  64. package/dist/entries/prompt-fields.js +2 -1
  65. package/dist/index.cjs +331 -273
  66. package/dist/index.d.cts +4 -4
  67. package/dist/index.d.ts +4 -4
  68. package/dist/index.js +11 -9
  69. package/dist/{subject-registry-BUswuEVD.d.cts → subject-registry-CQi_CZfJ.d.cts} +78 -9
  70. package/dist/{subject-registry-D7V8PEG-.d.ts → subject-registry-DFiGx49v.d.ts} +78 -9
  71. package/package.json +2 -2
  72. package/src/__tests__/assemble-image-input-style-references.test.ts +173 -0
  73. package/src/__tests__/catalog-id-guard.test.ts +30 -0
  74. package/src/__tests__/factory-preset-previews.test.ts +123 -0
  75. package/src/__tests__/factory-preset-references.test.ts +222 -0
  76. package/src/__tests__/user-direction-refs.test.ts +180 -0
  77. package/src/assemble-image-input.ts +114 -13
  78. package/src/catalog-id-guard.ts +18 -2
  79. package/src/color-names.ts +192 -0
  80. package/src/direction-registry.ts +66 -5
  81. package/src/entries/prompt-builder.ts +2 -0
  82. package/src/factory-presets/generate-image.ts +29 -0
  83. package/src/factory-presets/index.ts +2 -0
  84. package/src/factory-presets/preview-files.generated.ts +4 -0
  85. package/src/factory-presets/previews.ts +40 -0
  86. package/src/factory-presets/references.ts +80 -0
  87. package/src/factory-presets/shared-image.ts +4 -0
  88. package/src/factory-presets/types.ts +8 -0
  89. package/src/image-reference-doctrine.ts +3 -3
  90. package/src/image-token-pattern.ts +13 -0
  91. package/src/prompt-builder.ts +4 -5
  92. package/src/prompt-style-section.ts +3 -3
  93. package/src/user-direction-refs.ts +110 -0
  94. package/dist/chunk-4FDNVNJE.js.map +0 -1
  95. package/dist/chunk-7XKHDXCL.cjs.map +0 -1
  96. package/dist/chunk-CVPB2N3Z.cjs.map +0 -1
  97. package/dist/chunk-HRBBNODV.js.map +0 -1
  98. package/dist/chunk-KIYNOOPJ.js.map +0 -1
  99. package/dist/chunk-LVSH7K7R.cjs.map +0 -1
  100. package/dist/chunk-MENBHEAY.cjs.map +0 -1
  101. package/dist/chunk-NO6XHX4R.js.map +0 -1
  102. package/dist/chunk-QBEZMR2F.js.map +0 -1
  103. package/dist/chunk-QDEWBBUR.js.map +0 -1
  104. package/dist/chunk-TLVX5PXD.cjs.map +0 -1
  105. package/dist/chunk-UJKFHPZ7.js +0 -37
  106. package/dist/chunk-UJKFHPZ7.js.map +0 -1
  107. package/dist/chunk-W5SCZIOB.cjs +0 -43
  108. package/dist/chunk-W5SCZIOB.cjs.map +0 -1
  109. package/dist/chunk-ZFIXQRBZ.cjs.map +0 -1
@@ -1,21 +1,22 @@
1
1
  'use strict';
2
2
 
3
- var chunkMENBHEAY_cjs = require('./chunk-MENBHEAY.cjs');
3
+ var chunkWRM52ZBN_cjs = require('./chunk-WRM52ZBN.cjs');
4
+ var chunk2P26SZX5_cjs = require('./chunk-2P26SZX5.cjs');
4
5
 
5
6
  // src/read-node-direction.ts
6
7
  function readDirectionFields(value) {
7
8
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
8
9
  const src = value;
9
10
  const out = {};
10
- for (const spec of chunkMENBHEAY_cjs.DIRECTION_FIELDS) {
11
+ for (const spec of chunk2P26SZX5_cjs.DIRECTION_FIELDS) {
11
12
  const v = src[spec.key];
12
13
  if (typeof v === "string") {
13
- if (v.length > 0 && v.length <= chunkMENBHEAY_cjs.DIRECTION_ID_MAX_CHARS) out[spec.key] = v;
14
+ if (v.length > 0 && v.length <= chunk2P26SZX5_cjs.DIRECTION_ID_MAX_CHARS) out[spec.key] = v;
14
15
  } else if (Array.isArray(v)) {
15
16
  const kept = [];
16
17
  for (const x of v) {
17
- if (kept.length >= chunkMENBHEAY_cjs.DIRECTION_ARRAY_CEILING) break;
18
- if (typeof x === "string" && x.length > 0 && x.length <= chunkMENBHEAY_cjs.DIRECTION_ID_MAX_CHARS) {
18
+ if (kept.length >= chunk2P26SZX5_cjs.DIRECTION_ARRAY_CEILING) break;
19
+ if (typeof x === "string" && x.length > 0 && x.length <= chunk2P26SZX5_cjs.DIRECTION_ID_MAX_CHARS) {
19
20
  kept.push(x);
20
21
  }
21
22
  }
@@ -28,19 +29,19 @@ function readSubjectFields(value) {
28
29
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
29
30
  const src = value;
30
31
  const out = {};
31
- for (const key of chunkMENBHEAY_cjs.getRegisteredSubjectKeys()) {
32
+ for (const key of chunkWRM52ZBN_cjs.getRegisteredSubjectKeys()) {
32
33
  const v = src[key];
33
- if (key === chunkMENBHEAY_cjs.SUBJECT_CUSTOM_AGE_KEY) {
34
+ if (key === chunkWRM52ZBN_cjs.SUBJECT_CUSTOM_AGE_KEY) {
34
35
  if (typeof v === "number" && Number.isFinite(v)) out[key] = v;
35
36
  continue;
36
37
  }
37
38
  if (typeof v === "string") {
38
- if (v.length > 0 && v.length <= chunkMENBHEAY_cjs.SUBJECT_ID_MAX_CHARS) out[key] = v;
39
+ if (v.length > 0 && v.length <= chunkWRM52ZBN_cjs.SUBJECT_ID_MAX_CHARS) out[key] = v;
39
40
  } else if (Array.isArray(v)) {
40
41
  const kept = [];
41
42
  for (const x of v) {
42
- if (kept.length >= chunkMENBHEAY_cjs.SUBJECT_ARRAY_CEILING) break;
43
- if (typeof x === "string" && x.length > 0 && x.length <= chunkMENBHEAY_cjs.SUBJECT_ID_MAX_CHARS) {
43
+ if (kept.length >= chunkWRM52ZBN_cjs.SUBJECT_ARRAY_CEILING) break;
44
+ if (typeof x === "string" && x.length > 0 && x.length <= chunkWRM52ZBN_cjs.SUBJECT_ID_MAX_CHARS) {
44
45
  kept.push(x);
45
46
  }
46
47
  }
@@ -117,5 +118,5 @@ function readStructuredFields(value) {
117
118
  exports.readDirectionFields = readDirectionFields;
118
119
  exports.readStructuredFields = readStructuredFields;
119
120
  exports.readSubjectFields = readSubjectFields;
120
- //# sourceMappingURL=chunk-7XKHDXCL.cjs.map
121
- //# sourceMappingURL=chunk-7XKHDXCL.cjs.map
121
+ //# sourceMappingURL=chunk-3U7X6JZN.cjs.map
122
+ //# sourceMappingURL=chunk-3U7X6JZN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/read-node-direction.ts"],"names":["DIRECTION_FIELDS","DIRECTION_ID_MAX_CHARS","DIRECTION_ARRAY_CEILING","getRegisteredSubjectKeys","SUBJECT_CUSTOM_AGE_KEY","SUBJECT_ID_MAX_CHARS","SUBJECT_ARRAY_CEILING"],"mappings":"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","file":"chunk-3U7X6JZN.cjs","sourcesContent":["/**\n * Narrow readers turning UNTRUSTED persisted node data (`workflows.nodes`\n * JSONB — import, MCP write, node preset, a Studio-emitted graph) into the\n * typed `subject` / `direction` / `structured` levers `assembleImageInput`\n * accepts.\n * `buildPayload` has no zod and workflow writes are\n * `z.record(z.string(), z.unknown())`, so this blob may have been written years\n * ago by any client.\n *\n * Used by ALL THREE image-assembly sites so what the canvas accepts cannot\n * drift between them: the frontend single-node executor (`execute-node.ts`),\n * the orchestrator (`payload-builder.ts`), and the config-panel final-prompt\n * preview (`build-image-assemble-input.ts`).\n *\n * `readDirectionFields` is DERIVED FROM `DIRECTION_FIELDS`, never a hand list:\n * a dimension added to the registry is honored here by construction, so the\n * reader can never silently drop a key the wire schema and the renderer both\n * know about. Anything unrecognised is DROPPED, never thrown on — a malformed\n * blob must not fail a canvas run, and an unknown catalog ID already degrades\n * to `\"\"` inside each `get*PromptHint`, so this validates SHAPE only.\n *\n * BOUNDS MATCH THE WIRE SCHEMA where both exist, by SHARED CONSTANT:\n * `DIRECTION_ID_MAX_CHARS` and `DIRECTION_ARRAY_CEILING` are the same two\n * literals the `generate-image` route's `directionSchema` enforces, imported\n * from the registry rather than re-typed here — a body the route accepts and a\n * node the canvas re-runs must not disagree about which strings are ids.\n *\n * DELIBERATELY STRICTER THAN THE WIRE SCHEMA on `structured` values: the\n * route's `structuredPromptFieldsSchema` declares every field as a bare\n * `z.string().optional()` (unbounded), while `MAX_STRUCTURED_VALUE_CHARS` below\n * bounds them at 200. Do not \"fix\" that asymmetry by loosening HERE — this\n * reads a blob any client may have written years ago, and the values land\n * verbatim in `jobs.input_data.prompt`. Tightening the ROUTE instead would be a\n * new 400 on currently-accepted input, so a >200-char structured value stored\n * on a node is dropped on a canvas run while `POST /v1/generate-image` still\n * renders it. That is the one known divergence; it is bounded to a field the\n * canvas UI never writes that long.\n *\n * RETURNS `undefined`, NEVER `{}`: an empty object would still be a *defined*\n * `direction`, and the call sites' `...(x !== undefined ? { x } : {})` spread\n * would then hand `assembleImageInput` a defined-but-empty lever. Returning\n * `undefined` keeps that spread honest and the exact no-op branch taken.\n */\nimport {\n DIRECTION_ARRAY_CEILING,\n DIRECTION_FIELDS,\n DIRECTION_ID_MAX_CHARS,\n type DirectionFields,\n} from \"./direction-registry.js\"\nimport {\n SUBJECT_ARRAY_CEILING,\n SUBJECT_CUSTOM_AGE_KEY,\n SUBJECT_ID_MAX_CHARS,\n getRegisteredSubjectKeys,\n type SubjectFields,\n} from \"./subject-registry.js\"\nimport type { StructuredPromptFields } from \"./prompt-builder-structured-fields.js\"\n\n/**\n * Read a node's stored cinematic-direction ids. Accepts a single id OR an array\n * on every key (multi-pick dimensions carry arrays; a single-pick key may\n * legitimately carry one).\n *\n * The SEMANTIC per-dimension cap stays the renderer's slice (`maxPicks`) — this\n * reader does not know a row's pick budget and must not guess it. But it DOES\n * bound cardinality at `DIRECTION_ARRAY_CEILING`, the same ceiling the wire\n * schema enforces: the value is untrusted persisted JSONB (a node blob is\n * validated only as `z.record(z.string(), z.unknown())` on write), and handing\n * an unbounded array to `renderDirectionHints` would put an `includes`-dedupe\n * scan in front of that slice. Keeping the FIRST `DIRECTION_ARRAY_CEILING`\n * survivors is what the wire door already does to the same input.\n *\n * Junk is filtered BEFORE the cap, so valid ids sitting behind malformed\n * entries survive rather than being crowded out by them.\n */\nexport function readDirectionFields(value: unknown): DirectionFields | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined\n const src = value as Record<string, unknown>\n const out: Record<string, string | string[]> = {}\n for (const spec of DIRECTION_FIELDS) {\n const v = src[spec.key]\n if (typeof v === \"string\") {\n if (v.length > 0 && v.length <= DIRECTION_ID_MAX_CHARS) out[spec.key] = v\n } else if (Array.isArray(v)) {\n const kept: string[] = []\n for (const x of v) {\n if (kept.length >= DIRECTION_ARRAY_CEILING) break\n if (typeof x === \"string\" && x.length > 0 && x.length <= DIRECTION_ID_MAX_CHARS) {\n kept.push(x)\n }\n }\n if (kept.length > 0) out[spec.key] = kept\n }\n }\n return Object.keys(out).length > 0 ? (out as DirectionFields) : undefined\n}\n\n// ── subject ──────────────────────────────────────────────────────────────────\n\n/**\n * Read a node's stored SUBJECT ids — the flat Person / Styling / prop bag the\n * `subject` channel carries. Same three contracts as `readDirectionFields`:\n * drop-never-throw, `undefined` never `{}`, and bounds shared with the wire\n * door by CONSTANT (`SUBJECT_ID_MAX_CHARS` / `SUBJECT_ARRAY_CEILING`, which are\n * defined AS the direction constants) so a body the route accepts and the same\n * node re-run from the canvas cannot disagree about which strings are ids.\n *\n * DERIVED from `getRegisteredSubjectKeys()`, never a hand-authored field list —\n * that is the whole lesson of `readStructuredFields` below, whose hand table is\n * only viable because `StructuredPromptFields` is a small hand-authored type.\n * The subject vocabulary is ~54 catalog-derived fields PLUS deployment-registered\n * pack dimensions unknown at compile time, so it is read from the registry at\n * call time. Iterating the KEY SET (never `Object.keys(value)`) is also what\n * keeps an adversarial blob's key count off the hot path.\n *\n * The SEMANTIC per-dimension cap stays the renderer's job\n * (`normalizeSubjectFields`), exactly as `maxPicks` does for direction: this\n * reader bounds cardinality only. `customAge` is the one number on the wire and\n * is passed through finite-only — the renderer clamps it.\n */\nexport function readSubjectFields(value: unknown): SubjectFields | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined\n const src = value as Record<string, unknown>\n const out: Record<string, string | string[] | number> = {}\n for (const key of getRegisteredSubjectKeys()) {\n const v = src[key]\n if (key === SUBJECT_CUSTOM_AGE_KEY) {\n if (typeof v === \"number\" && Number.isFinite(v)) out[key] = v\n continue\n }\n if (typeof v === \"string\") {\n if (v.length > 0 && v.length <= SUBJECT_ID_MAX_CHARS) out[key] = v\n } else if (Array.isArray(v)) {\n // Junk is filtered BEFORE the cap, so valid ids sitting behind malformed\n // entries survive rather than being crowded out by them.\n const kept: string[] = []\n for (const x of v) {\n if (kept.length >= SUBJECT_ARRAY_CEILING) break\n if (typeof x === \"string\" && x.length > 0 && x.length <= SUBJECT_ID_MAX_CHARS) {\n kept.push(x)\n }\n }\n if (kept.length > 0) out[key] = kept\n }\n }\n return Object.keys(out).length > 0 ? (out as SubjectFields) : undefined\n}\n\n// ── structured ───────────────────────────────────────────────────────────────\n\ntype FieldKind = \"string\" | \"number\" | \"gender\"\nconst GENDERS = [\"man\", \"woman\", \"child\", \"non-binary\"] as const\nconst MAX_STRUCTURED_VALUE_CHARS = 200\n\ntype Group = Exclude<keyof StructuredPromptFields, \"mood\">\ntype GroupFields<K extends Group> = Record<keyof NonNullable<StructuredPromptFields[K]>, FieldKind>\n\n/**\n * A FIELD-BY-FIELD table (not a registry walk) on purpose:\n * `StructuredPromptFields` is a small HAND-AUTHORED type, not catalog-derived,\n * and the table is what blocks `person: { age: \"drop table\" }` from rendering\n * verbatim into `jobs.input_data.prompt` — `renderStructuredFields` never\n * throws on junk, but it does render it. Totality is enforced by the\n * `GroupFields<K>` / `{ [K in Group]: … }` mapped types: a new field or group\n * on the published type fails to typecheck here.\n */\nconst PERSON_FIELDS: GroupFields<\"person\"> = {\n age: \"number\",\n gender: \"gender\",\n hair: \"string\",\n eyes: \"string\",\n expression: \"string\",\n profession: \"string\",\n warriorType: \"string\",\n}\nconst STYLING_FIELDS: GroupFields<\"styling\"> = {\n mood: \"string\",\n lighting: \"string\",\n aesthetic: \"string\",\n colorLook: \"string\",\n}\nconst SETTING_FIELDS: GroupFields<\"setting\"> = {\n era: \"string\",\n atmosphere: \"string\",\n backdrop: \"string\",\n}\nconst CAMERA_FIELDS: GroupFields<\"camera\"> = {\n framing: \"string\",\n motion: \"string\",\n format: \"string\",\n}\nconst LENS_FIELDS: GroupFields<\"lens\"> = { focalLength: \"string\", aperture: \"string\" }\n\nconst STRUCTURED_GROUPS: { [K in Group]: GroupFields<K> } = {\n person: PERSON_FIELDS,\n styling: STYLING_FIELDS,\n setting: SETTING_FIELDS,\n camera: CAMERA_FIELDS,\n lens: LENS_FIELDS,\n}\n\nfunction readField(v: unknown, kind: FieldKind): string | number | undefined {\n if (kind === \"number\") return typeof v === \"number\" && Number.isFinite(v) ? v : undefined\n if (typeof v !== \"string\" || v.length === 0 || v.length > MAX_STRUCTURED_VALUE_CHARS) {\n return undefined\n }\n if (kind === \"gender\") return (GENDERS as readonly string[]).includes(v) ? v : undefined\n return v\n}\n\n/** Read a node's stored Path-1 structured prompt fields. Same drop-never-throw\n * and `undefined`-never-`{}` contract as `readDirectionFields`. */\nexport function readStructuredFields(value: unknown): StructuredPromptFields | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined\n const src = value as Record<string, unknown>\n const out: Record<string, unknown> = {}\n for (const group of Object.keys(STRUCTURED_GROUPS) as Group[]) {\n const raw = src[group]\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) continue\n const rawRec = raw as Record<string, unknown>\n const kept: Record<string, unknown> = {}\n for (const [field, kind] of Object.entries(\n STRUCTURED_GROUPS[group] as Record<string, FieldKind>,\n )) {\n const v = readField(rawRec[field], kind)\n if (v !== undefined) kept[field] = v\n }\n if (Object.keys(kept).length > 0) out[group] = kept\n }\n const mood = readField(src.mood, \"string\")\n if (mood !== undefined) out.mood = mood\n return Object.keys(out).length > 0 ? (out as StructuredPromptFields) : undefined\n}\n"]}
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ // src/image-token-pattern.ts
4
+ var IMAGE_TOKEN_PATTERN = /\{image:(\d+)(?::([^}\n]+))?\}/gi;
5
+
6
+ exports.IMAGE_TOKEN_PATTERN = IMAGE_TOKEN_PATTERN;
7
+ //# sourceMappingURL=chunk-4GKTOVFM.cjs.map
8
+ //# sourceMappingURL=chunk-4GKTOVFM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/image-token-pattern.ts"],"names":[],"mappings":";;;AAYO,IAAM,mBAAA,GAAsB","file":"chunk-4GKTOVFM.cjs","sourcesContent":["/**\n * The `{image:N}` / `{image:N:label}` reference-token grammar — one pattern for\n * the image prompt assembly (`prompt-builder.ts`) and for the preset metadata\n * that reads which references a template names (`factory-presets/references.ts`).\n *\n * Group 1 = position (1-based), group 2 = optional label. The label allows\n * spaces (multi-word labels like \"clothes and shoes\") but never a newline or\n * `}`, so a match can't run away across lines or token boundaries.\n *\n * Global on purpose (callers use `.replace` / `.matchAll`, which never leave a\n * `lastIndex` behind); never call `.test` / `.exec` on it.\n */\nexport const IMAGE_TOKEN_PATTERN = /\\{image:(\\d+)(?::([^}\\n]+))?\\}/gi\n"]}
@@ -1,4 +1,5 @@
1
- import { DIRECTION_FIELDS, DIRECTION_ID_MAX_CHARS, DIRECTION_ARRAY_CEILING, getRegisteredSubjectKeys, SUBJECT_CUSTOM_AGE_KEY, SUBJECT_ID_MAX_CHARS, SUBJECT_ARRAY_CEILING } from './chunk-QDEWBBUR.js';
1
+ import { getRegisteredSubjectKeys, SUBJECT_CUSTOM_AGE_KEY, SUBJECT_ID_MAX_CHARS, SUBJECT_ARRAY_CEILING } from './chunk-DUXX6CBX.js';
2
+ import { DIRECTION_FIELDS, DIRECTION_ID_MAX_CHARS, DIRECTION_ARRAY_CEILING } from './chunk-EAYEZXWT.js';
2
3
 
3
4
  // src/read-node-direction.ts
4
5
  function readDirectionFields(value) {
@@ -113,5 +114,5 @@ function readStructuredFields(value) {
113
114
  }
114
115
 
115
116
  export { readDirectionFields, readStructuredFields, readSubjectFields };
116
- //# sourceMappingURL=chunk-HRBBNODV.js.map
117
- //# sourceMappingURL=chunk-HRBBNODV.js.map
117
+ //# sourceMappingURL=chunk-6GVFJLDL.js.map
118
+ //# sourceMappingURL=chunk-6GVFJLDL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/read-node-direction.ts"],"names":[],"mappings":"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","file":"chunk-6GVFJLDL.js","sourcesContent":["/**\n * Narrow readers turning UNTRUSTED persisted node data (`workflows.nodes`\n * JSONB — import, MCP write, node preset, a Studio-emitted graph) into the\n * typed `subject` / `direction` / `structured` levers `assembleImageInput`\n * accepts.\n * `buildPayload` has no zod and workflow writes are\n * `z.record(z.string(), z.unknown())`, so this blob may have been written years\n * ago by any client.\n *\n * Used by ALL THREE image-assembly sites so what the canvas accepts cannot\n * drift between them: the frontend single-node executor (`execute-node.ts`),\n * the orchestrator (`payload-builder.ts`), and the config-panel final-prompt\n * preview (`build-image-assemble-input.ts`).\n *\n * `readDirectionFields` is DERIVED FROM `DIRECTION_FIELDS`, never a hand list:\n * a dimension added to the registry is honored here by construction, so the\n * reader can never silently drop a key the wire schema and the renderer both\n * know about. Anything unrecognised is DROPPED, never thrown on — a malformed\n * blob must not fail a canvas run, and an unknown catalog ID already degrades\n * to `\"\"` inside each `get*PromptHint`, so this validates SHAPE only.\n *\n * BOUNDS MATCH THE WIRE SCHEMA where both exist, by SHARED CONSTANT:\n * `DIRECTION_ID_MAX_CHARS` and `DIRECTION_ARRAY_CEILING` are the same two\n * literals the `generate-image` route's `directionSchema` enforces, imported\n * from the registry rather than re-typed here — a body the route accepts and a\n * node the canvas re-runs must not disagree about which strings are ids.\n *\n * DELIBERATELY STRICTER THAN THE WIRE SCHEMA on `structured` values: the\n * route's `structuredPromptFieldsSchema` declares every field as a bare\n * `z.string().optional()` (unbounded), while `MAX_STRUCTURED_VALUE_CHARS` below\n * bounds them at 200. Do not \"fix\" that asymmetry by loosening HERE — this\n * reads a blob any client may have written years ago, and the values land\n * verbatim in `jobs.input_data.prompt`. Tightening the ROUTE instead would be a\n * new 400 on currently-accepted input, so a >200-char structured value stored\n * on a node is dropped on a canvas run while `POST /v1/generate-image` still\n * renders it. That is the one known divergence; it is bounded to a field the\n * canvas UI never writes that long.\n *\n * RETURNS `undefined`, NEVER `{}`: an empty object would still be a *defined*\n * `direction`, and the call sites' `...(x !== undefined ? { x } : {})` spread\n * would then hand `assembleImageInput` a defined-but-empty lever. Returning\n * `undefined` keeps that spread honest and the exact no-op branch taken.\n */\nimport {\n DIRECTION_ARRAY_CEILING,\n DIRECTION_FIELDS,\n DIRECTION_ID_MAX_CHARS,\n type DirectionFields,\n} from \"./direction-registry.js\"\nimport {\n SUBJECT_ARRAY_CEILING,\n SUBJECT_CUSTOM_AGE_KEY,\n SUBJECT_ID_MAX_CHARS,\n getRegisteredSubjectKeys,\n type SubjectFields,\n} from \"./subject-registry.js\"\nimport type { StructuredPromptFields } from \"./prompt-builder-structured-fields.js\"\n\n/**\n * Read a node's stored cinematic-direction ids. Accepts a single id OR an array\n * on every key (multi-pick dimensions carry arrays; a single-pick key may\n * legitimately carry one).\n *\n * The SEMANTIC per-dimension cap stays the renderer's slice (`maxPicks`) — this\n * reader does not know a row's pick budget and must not guess it. But it DOES\n * bound cardinality at `DIRECTION_ARRAY_CEILING`, the same ceiling the wire\n * schema enforces: the value is untrusted persisted JSONB (a node blob is\n * validated only as `z.record(z.string(), z.unknown())` on write), and handing\n * an unbounded array to `renderDirectionHints` would put an `includes`-dedupe\n * scan in front of that slice. Keeping the FIRST `DIRECTION_ARRAY_CEILING`\n * survivors is what the wire door already does to the same input.\n *\n * Junk is filtered BEFORE the cap, so valid ids sitting behind malformed\n * entries survive rather than being crowded out by them.\n */\nexport function readDirectionFields(value: unknown): DirectionFields | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined\n const src = value as Record<string, unknown>\n const out: Record<string, string | string[]> = {}\n for (const spec of DIRECTION_FIELDS) {\n const v = src[spec.key]\n if (typeof v === \"string\") {\n if (v.length > 0 && v.length <= DIRECTION_ID_MAX_CHARS) out[spec.key] = v\n } else if (Array.isArray(v)) {\n const kept: string[] = []\n for (const x of v) {\n if (kept.length >= DIRECTION_ARRAY_CEILING) break\n if (typeof x === \"string\" && x.length > 0 && x.length <= DIRECTION_ID_MAX_CHARS) {\n kept.push(x)\n }\n }\n if (kept.length > 0) out[spec.key] = kept\n }\n }\n return Object.keys(out).length > 0 ? (out as DirectionFields) : undefined\n}\n\n// ── subject ──────────────────────────────────────────────────────────────────\n\n/**\n * Read a node's stored SUBJECT ids — the flat Person / Styling / prop bag the\n * `subject` channel carries. Same three contracts as `readDirectionFields`:\n * drop-never-throw, `undefined` never `{}`, and bounds shared with the wire\n * door by CONSTANT (`SUBJECT_ID_MAX_CHARS` / `SUBJECT_ARRAY_CEILING`, which are\n * defined AS the direction constants) so a body the route accepts and the same\n * node re-run from the canvas cannot disagree about which strings are ids.\n *\n * DERIVED from `getRegisteredSubjectKeys()`, never a hand-authored field list —\n * that is the whole lesson of `readStructuredFields` below, whose hand table is\n * only viable because `StructuredPromptFields` is a small hand-authored type.\n * The subject vocabulary is ~54 catalog-derived fields PLUS deployment-registered\n * pack dimensions unknown at compile time, so it is read from the registry at\n * call time. Iterating the KEY SET (never `Object.keys(value)`) is also what\n * keeps an adversarial blob's key count off the hot path.\n *\n * The SEMANTIC per-dimension cap stays the renderer's job\n * (`normalizeSubjectFields`), exactly as `maxPicks` does for direction: this\n * reader bounds cardinality only. `customAge` is the one number on the wire and\n * is passed through finite-only — the renderer clamps it.\n */\nexport function readSubjectFields(value: unknown): SubjectFields | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined\n const src = value as Record<string, unknown>\n const out: Record<string, string | string[] | number> = {}\n for (const key of getRegisteredSubjectKeys()) {\n const v = src[key]\n if (key === SUBJECT_CUSTOM_AGE_KEY) {\n if (typeof v === \"number\" && Number.isFinite(v)) out[key] = v\n continue\n }\n if (typeof v === \"string\") {\n if (v.length > 0 && v.length <= SUBJECT_ID_MAX_CHARS) out[key] = v\n } else if (Array.isArray(v)) {\n // Junk is filtered BEFORE the cap, so valid ids sitting behind malformed\n // entries survive rather than being crowded out by them.\n const kept: string[] = []\n for (const x of v) {\n if (kept.length >= SUBJECT_ARRAY_CEILING) break\n if (typeof x === \"string\" && x.length > 0 && x.length <= SUBJECT_ID_MAX_CHARS) {\n kept.push(x)\n }\n }\n if (kept.length > 0) out[key] = kept\n }\n }\n return Object.keys(out).length > 0 ? (out as SubjectFields) : undefined\n}\n\n// ── structured ───────────────────────────────────────────────────────────────\n\ntype FieldKind = \"string\" | \"number\" | \"gender\"\nconst GENDERS = [\"man\", \"woman\", \"child\", \"non-binary\"] as const\nconst MAX_STRUCTURED_VALUE_CHARS = 200\n\ntype Group = Exclude<keyof StructuredPromptFields, \"mood\">\ntype GroupFields<K extends Group> = Record<keyof NonNullable<StructuredPromptFields[K]>, FieldKind>\n\n/**\n * A FIELD-BY-FIELD table (not a registry walk) on purpose:\n * `StructuredPromptFields` is a small HAND-AUTHORED type, not catalog-derived,\n * and the table is what blocks `person: { age: \"drop table\" }` from rendering\n * verbatim into `jobs.input_data.prompt` — `renderStructuredFields` never\n * throws on junk, but it does render it. Totality is enforced by the\n * `GroupFields<K>` / `{ [K in Group]: … }` mapped types: a new field or group\n * on the published type fails to typecheck here.\n */\nconst PERSON_FIELDS: GroupFields<\"person\"> = {\n age: \"number\",\n gender: \"gender\",\n hair: \"string\",\n eyes: \"string\",\n expression: \"string\",\n profession: \"string\",\n warriorType: \"string\",\n}\nconst STYLING_FIELDS: GroupFields<\"styling\"> = {\n mood: \"string\",\n lighting: \"string\",\n aesthetic: \"string\",\n colorLook: \"string\",\n}\nconst SETTING_FIELDS: GroupFields<\"setting\"> = {\n era: \"string\",\n atmosphere: \"string\",\n backdrop: \"string\",\n}\nconst CAMERA_FIELDS: GroupFields<\"camera\"> = {\n framing: \"string\",\n motion: \"string\",\n format: \"string\",\n}\nconst LENS_FIELDS: GroupFields<\"lens\"> = { focalLength: \"string\", aperture: \"string\" }\n\nconst STRUCTURED_GROUPS: { [K in Group]: GroupFields<K> } = {\n person: PERSON_FIELDS,\n styling: STYLING_FIELDS,\n setting: SETTING_FIELDS,\n camera: CAMERA_FIELDS,\n lens: LENS_FIELDS,\n}\n\nfunction readField(v: unknown, kind: FieldKind): string | number | undefined {\n if (kind === \"number\") return typeof v === \"number\" && Number.isFinite(v) ? v : undefined\n if (typeof v !== \"string\" || v.length === 0 || v.length > MAX_STRUCTURED_VALUE_CHARS) {\n return undefined\n }\n if (kind === \"gender\") return (GENDERS as readonly string[]).includes(v) ? v : undefined\n return v\n}\n\n/** Read a node's stored Path-1 structured prompt fields. Same drop-never-throw\n * and `undefined`-never-`{}` contract as `readDirectionFields`. */\nexport function readStructuredFields(value: unknown): StructuredPromptFields | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined\n const src = value as Record<string, unknown>\n const out: Record<string, unknown> = {}\n for (const group of Object.keys(STRUCTURED_GROUPS) as Group[]) {\n const raw = src[group]\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) continue\n const rawRec = raw as Record<string, unknown>\n const kept: Record<string, unknown> = {}\n for (const [field, kind] of Object.entries(\n STRUCTURED_GROUPS[group] as Record<string, FieldKind>,\n )) {\n const v = readField(rawRec[field], kind)\n if (v !== undefined) kept[field] = v\n }\n if (Object.keys(kept).length > 0) out[group] = kept\n }\n const mood = readField(src.mood, \"string\")\n if (mood !== undefined) out.mood = mood\n return Object.keys(out).length > 0 ? (out as StructuredPromptFields) : undefined\n}\n"]}
@@ -1,5 +1,7 @@
1
+ import { getPromptFields, NODE_PROMPT_CANDIDATE_FIELDS, promptAffixCoreField, nodeSupportsPromptAffixes } from './chunk-7XMTVYS5.js';
2
+ import { IMAGE_TOKEN_PATTERN } from './chunk-GILUVQQJ.js';
1
3
  import { realismEmptyHands, NEUTRAL_PERSON, realismViewpoint, REFERENCE_RULES, SCENE_FRAME_RULE, CINEMATIC_LOOK_TAIL, FILM_STILL_PREFIX, REALISM_PASS_INSTRUCTION, REALISM_FINISH, REALISM_SKIN } from './chunk-T64D26P4.js';
2
- import { T2I_TO_I2I_VARIANT } from '@nodaro/shared/models';
4
+ import { MODELS_WITH_REFERENCE_IMAGE_SUPPORT, imageReferenceLimit, T2I_TO_I2I_VARIANT } from '@nodaro/shared/models';
3
5
 
4
6
  // src/brand-tokens.ts
5
7
  var BRAND_PRESET_IDS = [
@@ -135,7 +137,9 @@ function stylizedSubjectFor(nodeType) {
135
137
  name: p.name,
136
138
  description: p.description,
137
139
  group: "Stylized Subject",
138
- data: { provider: editProviderFor(nodeType), ...p.data }
140
+ data: { provider: editProviderFor(nodeType), ...p.data },
141
+ // Restyles the person in the connected photo ("the reference image").
142
+ minReferences: 1
139
143
  }));
140
144
  }
141
145
  var IMAGE_EDITS = [
@@ -259,7 +263,9 @@ function editsFor(nodeType) {
259
263
  name: p.name,
260
264
  description: p.description,
261
265
  group: "Edits",
262
- data: { provider: editProviderFor(nodeType), ...p.data }
266
+ data: { provider: editProviderFor(nodeType), ...p.data },
267
+ // Every edit operates on the connected photo.
268
+ minReferences: 1
263
269
  }));
264
270
  }
265
271
 
@@ -268,12 +274,17 @@ var sentences = (...parts) => parts.join(" ");
268
274
  var REALISM_MODEL = { provider: "gpt-image-2", resolution: "2K" };
269
275
  var SKIN_STUDY_MODEL = { provider: "seedream-5-pro", quality: "high" };
270
276
  var GENERATE_IMAGE_PRESETS = [
277
+ // `minReferences` marks a preset that works from connected photos its prompt
278
+ // describes in words ("the attached photo"); `{image:N}` tokens count on their
279
+ // own, so a tokenized preset declares nothing. The groups stay all-or-nothing
280
+ // (factory-preset-references.test.ts), so a new preset copies the line along.
271
281
  // ── Reference Sheet ──────────────────────────────────────────────────────
272
282
  {
273
283
  id: "generate-image/character-board",
274
284
  name: "Character Board",
275
285
  description: "Connect a sharp, well-lit photo \u2192 dense character sheet; reuse it as a reference for consistent shots.",
276
286
  group: "Reference Sheet",
287
+ minReferences: 1,
277
288
  data: {
278
289
  provider: "gpt-image-2",
279
290
  aspectRatio: "16:9",
@@ -288,6 +299,7 @@ var GENERATE_IMAGE_PRESETS = [
288
299
  name: "Pose Board",
289
300
  description: "Connect a sharp photo \u2192 animation-ready pose & expression sheet; reuse it as a reference for consistent action.",
290
301
  group: "Reference Sheet",
302
+ minReferences: 1,
291
303
  data: {
292
304
  provider: "gpt-image-2",
293
305
  aspectRatio: "16:9",
@@ -302,6 +314,7 @@ var GENERATE_IMAGE_PRESETS = [
302
314
  name: "Location Board",
303
315
  description: "Connect a photo \u2192 dense location sheet; reuse it as a reference for consistent scenes.",
304
316
  group: "Reference Sheet",
317
+ minReferences: 1,
305
318
  data: {
306
319
  provider: "gpt-image-2",
307
320
  aspectRatio: "16:9",
@@ -316,6 +329,7 @@ var GENERATE_IMAGE_PRESETS = [
316
329
  name: "Product Board",
317
330
  description: "Connect a photo \u2192 dense product sheet; reuse it as a reference for consistent shots.",
318
331
  group: "Reference Sheet",
332
+ minReferences: 1,
319
333
  data: {
320
334
  provider: "gpt-image-2",
321
335
  aspectRatio: "16:9",
@@ -330,6 +344,7 @@ var GENERATE_IMAGE_PRESETS = [
330
344
  name: "Outfit Board",
331
345
  description: "Connect a photo \u2192 dense wardrobe sheet; reuse it as a reference for consistent looks.",
332
346
  group: "Reference Sheet",
347
+ minReferences: 1,
333
348
  data: {
334
349
  provider: "gpt-image-2",
335
350
  aspectRatio: "16:9",
@@ -344,6 +359,7 @@ var GENERATE_IMAGE_PRESETS = [
344
359
  name: "Scene Board",
345
360
  description: "Connect a photo \u2192 set-dressing & props study; reuse it as a reference for consistent sets.",
346
361
  group: "Reference Sheet",
362
+ minReferences: 1,
347
363
  data: {
348
364
  provider: "gpt-image-2",
349
365
  aspectRatio: "16:9",
@@ -358,6 +374,7 @@ var GENERATE_IMAGE_PRESETS = [
358
374
  name: "Creature Board",
359
375
  description: "Connect a sharp photo \u2192 dense creature sheet; reuse it as a reference for consistent shots.",
360
376
  group: "Reference Sheet",
377
+ minReferences: 1,
361
378
  data: {
362
379
  provider: "gpt-image-2",
363
380
  aspectRatio: "16:9",
@@ -372,6 +389,7 @@ var GENERATE_IMAGE_PRESETS = [
372
389
  name: "Vehicle Board",
373
390
  description: "Connect a photo \u2192 dense vehicle sheet; reuse it as a reference for consistent shots.",
374
391
  group: "Reference Sheet",
392
+ minReferences: 1,
375
393
  data: {
376
394
  provider: "gpt-image-2",
377
395
  aspectRatio: "16:9",
@@ -386,6 +404,7 @@ var GENERATE_IMAGE_PRESETS = [
386
404
  name: "Food Board",
387
405
  description: "Connect a photo \u2192 dense dish sheet; reuse it as a reference for consistent food shots.",
388
406
  group: "Reference Sheet",
407
+ minReferences: 1,
389
408
  data: {
390
409
  provider: "gpt-image-2",
391
410
  aspectRatio: "16:9",
@@ -400,6 +419,7 @@ var GENERATE_IMAGE_PRESETS = [
400
419
  name: "Mascot Board",
401
420
  description: "Connect a mascot image \u2192 brand-character sheet with brand applications; reuse it for a consistent mascot.",
402
421
  group: "Reference Sheet",
422
+ minReferences: 1,
403
423
  data: {
404
424
  provider: "gpt-image-2",
405
425
  aspectRatio: "16:9",
@@ -414,6 +434,7 @@ var GENERATE_IMAGE_PRESETS = [
414
434
  name: "Pet Board",
415
435
  description: "Connect a sharp photo \u2192 dense pet sheet; reuse it as a reference for consistent shots.",
416
436
  group: "Reference Sheet",
437
+ minReferences: 1,
417
438
  data: {
418
439
  provider: "gpt-image-2",
419
440
  aspectRatio: "16:9",
@@ -428,6 +449,7 @@ var GENERATE_IMAGE_PRESETS = [
428
449
  name: "Skin Detail Sheet",
429
450
  description: "Connect a sharp face photo \u2192 a six-panel skin reference.",
430
451
  group: "Reference Sheet",
452
+ minReferences: 1,
431
453
  data: {
432
454
  ...SKIN_STUDY_MODEL,
433
455
  aspectRatio: "3:2",
@@ -450,6 +472,7 @@ var GENERATE_IMAGE_PRESETS = [
450
472
  name: "Character Reference Grid",
451
473
  description: "Connect a photo \u2192 clean 4-angle identity grid (no decorations) \u2014 the strongest consistency anchor.",
452
474
  group: "Cast & Consistency",
475
+ minReferences: 1,
453
476
  data: {
454
477
  provider: "gpt-image-2",
455
478
  aspectRatio: "3:4",
@@ -464,6 +487,8 @@ var GENERATE_IMAGE_PRESETS = [
464
487
  name: "Cast Mega Grid",
465
488
  description: "Connect 2\u20134 character refs \u2192 one labeled cast sheet; then reference cast members by name in scenes.",
466
489
  group: "Cast & Consistency",
490
+ // A cast sheet is two characters or more ("Connect 2–4 character refs").
491
+ minReferences: 2,
467
492
  data: {
468
493
  provider: "gpt-image-2",
469
494
  aspectRatio: "3:4",
@@ -477,6 +502,7 @@ var GENERATE_IMAGE_PRESETS = [
477
502
  name: "Cast Scene (by name)",
478
503
  description: "Connect a cast grid \u2192 stage a scene naming the characters; never re-describe their looks.",
479
504
  group: "Cast & Consistency",
505
+ minReferences: 1,
480
506
  data: {
481
507
  provider: "gpt-image-2",
482
508
  aspectRatio: "16:9",
@@ -491,6 +517,7 @@ var GENERATE_IMAGE_PRESETS = [
491
517
  name: "Label Elements",
492
518
  description: "Step 1 \xB7 connect a photo \u2192 the same image with a numbered label on each changeable element.",
493
519
  group: "Edit by Name",
520
+ minReferences: 1,
494
521
  data: {
495
522
  provider: "gpt-image-2",
496
523
  quality: "high",
@@ -503,6 +530,7 @@ var GENERATE_IMAGE_PRESETS = [
503
530
  name: "Apply Named Edit",
504
531
  description: 'Step 2 \xB7 connect the labeled sheet \u2192 type your change in the prompt (e.g. change "drink 1" to blue); the labels are removed from the result.',
505
532
  group: "Edit by Name",
533
+ minReferences: 1,
506
534
  data: {
507
535
  provider: "gpt-image-2",
508
536
  quality: "high",
@@ -1711,6 +1739,7 @@ var GENERATE_IMAGE_PRESETS = [
1711
1739
  name: "Five Ages (Timeless Soul)",
1712
1740
  description: "One person at 5 ages, studio line-up. Needs a reference photo.",
1713
1741
  group: "Portrait Transformations",
1742
+ minReferences: 1,
1714
1743
  data: {
1715
1744
  provider: "gpt-image-2",
1716
1745
  aspectRatio: "3:4",
@@ -1723,6 +1752,7 @@ var GENERATE_IMAGE_PRESETS = [
1723
1752
  name: "Three-Age Triptych",
1724
1753
  description: "Same person at 3 ages (~10 / 40 / 75). Needs a reference photo.",
1725
1754
  group: "Portrait Transformations",
1755
+ minReferences: 1,
1726
1756
  data: {
1727
1757
  provider: "gpt-image-2",
1728
1758
  aspectRatio: "3:4",
@@ -1735,6 +1765,7 @@ var GENERATE_IMAGE_PRESETS = [
1735
1765
  name: "Five Ages \u2014 B&W",
1736
1766
  description: "Monochrome age line-up. Needs a reference photo.",
1737
1767
  group: "Portrait Transformations",
1768
+ minReferences: 1,
1738
1769
  data: {
1739
1770
  provider: "gpt-image-2",
1740
1771
  aspectRatio: "3:4",
@@ -1747,6 +1778,7 @@ var GENERATE_IMAGE_PRESETS = [
1747
1778
  name: "Decade Timeline",
1748
1779
  description: "Same person across the '80s\u20132020s. Needs a reference photo.",
1749
1780
  group: "Portrait Transformations",
1781
+ minReferences: 1,
1750
1782
  data: {
1751
1783
  provider: "gpt-image-2",
1752
1784
  aspectRatio: "3:4",
@@ -1759,6 +1791,7 @@ var GENERATE_IMAGE_PRESETS = [
1759
1791
  name: "Four Seasons",
1760
1792
  description: "Same person in spring / summer / autumn / winter. Needs a reference photo.",
1761
1793
  group: "Portrait Transformations",
1794
+ minReferences: 1,
1762
1795
  data: {
1763
1796
  provider: "gpt-image-2",
1764
1797
  aspectRatio: "3:4",
@@ -1771,6 +1804,7 @@ var GENERATE_IMAGE_PRESETS = [
1771
1804
  name: "Times of Day",
1772
1805
  description: "Same person at dawn / midday / golden hour / night. Needs a reference photo.",
1773
1806
  group: "Portrait Transformations",
1807
+ minReferences: 1,
1774
1808
  data: {
1775
1809
  provider: "gpt-image-2",
1776
1810
  aspectRatio: "3:4",
@@ -1783,6 +1817,7 @@ var GENERATE_IMAGE_PRESETS = [
1783
1817
  name: "Sports Jersey Portrait",
1784
1818
  description: "You in an oversized team jersey, studio editorial. Needs a reference photo.",
1785
1819
  group: "Portrait Transformations",
1820
+ minReferences: 1,
1786
1821
  data: {
1787
1822
  provider: "nano-banana-2-1",
1788
1823
  aspectRatio: "4:5",
@@ -5114,6 +5149,64 @@ function groupFactoryPresets(presets) {
5114
5149
  }
5115
5150
  return buckets;
5116
5151
  }
5152
+ var MAX_PRESET_REFERENCES = Math.max(
5153
+ 1,
5154
+ ...[...MODELS_WITH_REFERENCE_IMAGE_SUPPORT].map((provider) => imageReferenceLimit(provider))
5155
+ );
5156
+ function promptFieldsOf(nodeType) {
5157
+ const spec = getPromptFields(nodeType);
5158
+ const candidates = NODE_PROMPT_CANDIDATE_FIELDS[nodeType] ?? [];
5159
+ if (!spec && candidates.length === 0) return [];
5160
+ const fields = new Set(candidates);
5161
+ if (spec) {
5162
+ fields.add(spec.prompt);
5163
+ if (spec.negative) fields.add(spec.negative);
5164
+ }
5165
+ const core = promptAffixCoreField(nodeType);
5166
+ if (core) fields.add(core);
5167
+ if (nodeSupportsPromptAffixes(nodeType)) {
5168
+ fields.add("promptPrefix");
5169
+ fields.add("promptSuffix");
5170
+ }
5171
+ return [...fields];
5172
+ }
5173
+ function highestImageToken(text) {
5174
+ let highest = 0;
5175
+ for (const match of text.matchAll(IMAGE_TOKEN_PATTERN)) {
5176
+ const n = Number.parseInt(match[1] ?? "", 10);
5177
+ if (Number.isInteger(n) && n > highest) highest = n;
5178
+ }
5179
+ return highest;
5180
+ }
5181
+ function presetMinReferences(nodeType, data, declared) {
5182
+ let count = typeof declared === "number" && Number.isInteger(declared) && declared > 0 ? declared : 0;
5183
+ for (const field of promptFieldsOf(nodeType)) {
5184
+ const value = data[field];
5185
+ if (typeof value === "string") count = Math.max(count, highestImageToken(value));
5186
+ }
5187
+ return Math.min(count, MAX_PRESET_REFERENCES);
5188
+ }
5189
+
5190
+ // src/factory-presets/preview-files.generated.ts
5191
+ var FACTORY_PRESET_PREVIEW_FILES = {};
5192
+
5193
+ // src/factory-presets/previews.ts
5194
+ var PRESET_PREVIEW_PATH = "/preset-previews/";
5195
+ function originOf(baseUrl) {
5196
+ try {
5197
+ const url = new URL(baseUrl);
5198
+ if (url.protocol !== "https:" && url.protocol !== "http:") return void 0;
5199
+ return url.origin + url.pathname.replace(/\/+$/, "");
5200
+ } catch {
5201
+ return void 0;
5202
+ }
5203
+ }
5204
+ function factoryPresetPreviewUrl(presetId, baseUrl, files = FACTORY_PRESET_PREVIEW_FILES) {
5205
+ const file = Object.prototype.hasOwnProperty.call(files, presetId) ? files[presetId] : void 0;
5206
+ if (!file) return null;
5207
+ const origin = originOf(baseUrl);
5208
+ return origin ? `${origin}${PRESET_PREVIEW_PATH}${file}` : null;
5209
+ }
5117
5210
 
5118
5211
  // src/factory-presets/index.ts
5119
5212
  function forModifyImage(p) {
@@ -5523,6 +5616,6 @@ function getFactorySnippets(target, media) {
5523
5616
  );
5524
5617
  }
5525
5618
 
5526
- export { BODY_CAPTIONS_PRESET_ID, BRAND_PRESETS, BRAND_PRESET_IDS, BRAND_PRESET_META, CAPTION_LEVERS_BY_LANGUAGE, CAPTION_SEGMENT_LEVER_KEYS, FACTORY_PRESETS, FACTORY_SNIPPETS, HOOK_PLATE_PRESET_ID, OBJECT_ANGLE_PRESETS, OBJECT_ANGLE_PROMPTS, OBJECT_ASSET_PRESETS, OBJECT_ASSET_PROMPTS, OBJECT_MATERIAL_PRESETS, OBJECT_MATERIAL_PROMPTS, OBJECT_VARIATION_PRESETS, OBJECT_VARIATION_PROMPTS, SNIPPET_MEDIA_VALUES, STYLE_PRESETS, getFactoryPresets, getFactorySnippets, getStylePreset, groupFactoryPresets, hookPlateCaptionSegments, resolveBrandInput };
5527
- //# sourceMappingURL=chunk-NO6XHX4R.js.map
5528
- //# sourceMappingURL=chunk-NO6XHX4R.js.map
5619
+ export { BODY_CAPTIONS_PRESET_ID, BRAND_PRESETS, BRAND_PRESET_IDS, BRAND_PRESET_META, CAPTION_LEVERS_BY_LANGUAGE, CAPTION_SEGMENT_LEVER_KEYS, FACTORY_PRESETS, FACTORY_PRESET_PREVIEW_FILES, FACTORY_SNIPPETS, HOOK_PLATE_PRESET_ID, MAX_PRESET_REFERENCES, OBJECT_ANGLE_PRESETS, OBJECT_ANGLE_PROMPTS, OBJECT_ASSET_PRESETS, OBJECT_ASSET_PROMPTS, OBJECT_MATERIAL_PRESETS, OBJECT_MATERIAL_PROMPTS, OBJECT_VARIATION_PRESETS, OBJECT_VARIATION_PROMPTS, PRESET_PREVIEW_PATH, SNIPPET_MEDIA_VALUES, STYLE_PRESETS, factoryPresetPreviewUrl, getFactoryPresets, getFactorySnippets, getStylePreset, groupFactoryPresets, hookPlateCaptionSegments, presetMinReferences, resolveBrandInput };
5620
+ //# sourceMappingURL=chunk-6MPZHFIS.js.map
5621
+ //# sourceMappingURL=chunk-6MPZHFIS.js.map