@nodaro/prompts 1.42.1 → 1.45.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 (96) hide show
  1. package/dist/{chunk-PHFOKGYS.js → chunk-2HJJBXGA.js} +6 -6
  2. package/dist/{chunk-PHFOKGYS.js.map → chunk-2HJJBXGA.js.map} +1 -1
  3. package/dist/{chunk-HL6PZ4A6.js → chunk-2OIKQ4WB.js} +5 -5
  4. package/dist/{chunk-HL6PZ4A6.js.map → chunk-2OIKQ4WB.js.map} +1 -1
  5. package/dist/{chunk-ZFOXGNLK.js → chunk-57PTYJJX.js} +4 -4
  6. package/dist/{chunk-ZFOXGNLK.js.map → chunk-57PTYJJX.js.map} +1 -1
  7. package/dist/{chunk-ZGA72NCT.cjs → chunk-C4FDHCEZ.cjs} +125 -125
  8. package/dist/{chunk-ZGA72NCT.cjs.map → chunk-C4FDHCEZ.cjs.map} +1 -1
  9. package/dist/chunk-CLHIPYTC.js +10913 -0
  10. package/dist/chunk-CLHIPYTC.js.map +1 -0
  11. package/dist/{chunk-QJACEKMZ.js → chunk-CPKYIVWP.js} +3 -3
  12. package/dist/{chunk-QJACEKMZ.js.map → chunk-CPKYIVWP.js.map} +1 -1
  13. package/dist/{chunk-JDNHMVEV.cjs → chunk-FCTK7RGO.cjs} +35 -35
  14. package/dist/{chunk-JDNHMVEV.cjs.map → chunk-FCTK7RGO.cjs.map} +1 -1
  15. package/dist/{chunk-IFRFYBGE.js → chunk-IKHRZ5UQ.js} +3 -3
  16. package/dist/{chunk-IFRFYBGE.js.map → chunk-IKHRZ5UQ.js.map} +1 -1
  17. package/dist/{chunk-SB7AYE6S.cjs → chunk-JHEZJKTX.cjs} +250 -6
  18. package/dist/chunk-JHEZJKTX.cjs.map +1 -0
  19. package/dist/{chunk-GIO77YH5.js → chunk-NN4FL7SB.js} +4 -4
  20. package/dist/{chunk-GIO77YH5.js.map → chunk-NN4FL7SB.js.map} +1 -1
  21. package/dist/{chunk-I3QS7BSY.cjs → chunk-OSKABA64.cjs} +8 -8
  22. package/dist/{chunk-I3QS7BSY.cjs.map → chunk-OSKABA64.cjs.map} +1 -1
  23. package/dist/{chunk-AXQK7UH7.cjs → chunk-OY7XOPDG.cjs} +49 -49
  24. package/dist/{chunk-AXQK7UH7.cjs.map → chunk-OY7XOPDG.cjs.map} +1 -1
  25. package/dist/{chunk-ZLRB5BVT.cjs → chunk-PHR4UETI.cjs} +18 -18
  26. package/dist/{chunk-ZLRB5BVT.cjs.map → chunk-PHR4UETI.cjs.map} +1 -1
  27. package/dist/chunk-S3H4TI6N.cjs +10948 -0
  28. package/dist/chunk-S3H4TI6N.cjs.map +1 -0
  29. package/dist/{chunk-6JABZ5OC.js → chunk-SMKIVT7P.js} +4 -4
  30. package/dist/{chunk-6JABZ5OC.js.map → chunk-SMKIVT7P.js.map} +1 -1
  31. package/dist/{chunk-CYMIXKFT.js → chunk-TXHFL725.js} +247 -3
  32. package/dist/chunk-TXHFL725.js.map +1 -0
  33. package/dist/{chunk-WARNUGXT.cjs → chunk-UNR34GYU.cjs} +191 -2
  34. package/dist/chunk-UNR34GYU.cjs.map +1 -0
  35. package/dist/{chunk-4YUXR23B.cjs → chunk-VAK4RUNU.cjs} +13 -13
  36. package/dist/{chunk-4YUXR23B.cjs.map → chunk-VAK4RUNU.cjs.map} +1 -1
  37. package/dist/{chunk-JCYK7NSR.js → chunk-XZXUU3YH.js} +191 -2
  38. package/dist/chunk-XZXUU3YH.js.map +1 -0
  39. package/dist/{chunk-H4JMZWOE.cjs → chunk-YCCQXX3U.cjs} +39 -39
  40. package/dist/{chunk-H4JMZWOE.cjs.map → chunk-YCCQXX3U.cjs.map} +1 -1
  41. package/dist/entries/assembly.cjs +29 -29
  42. package/dist/entries/assembly.js +6 -6
  43. package/dist/entries/picker-art.cjs +19 -19
  44. package/dist/entries/picker-art.d.cts +188 -0
  45. package/dist/entries/picker-art.d.ts +188 -0
  46. package/dist/entries/picker-art.js +1 -1
  47. package/dist/entries/picker-catalogs.cjs +57 -57
  48. package/dist/entries/picker-catalogs.js +5 -5
  49. package/dist/entries/pickers.cjs +374 -374
  50. package/dist/entries/pickers.d.cts +1 -1
  51. package/dist/entries/pickers.d.ts +1 -1
  52. package/dist/entries/pickers.js +3 -3
  53. package/dist/entries/presets.cjs +47 -31
  54. package/dist/entries/presets.d.cts +37 -1
  55. package/dist/entries/presets.d.ts +37 -1
  56. package/dist/entries/presets.js +1 -1
  57. package/dist/entries/prompt-builder.cjs +76 -76
  58. package/dist/entries/prompt-builder.js +5 -5
  59. package/dist/index.cjs +591 -575
  60. package/dist/index.d.cts +1 -1
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.js +10 -10
  63. package/package.json +2 -2
  64. package/src/__tests__/factory-preset-previews.test.ts +98 -25
  65. package/src/__tests__/factory-preset-references.test.ts +4 -0
  66. package/src/__tests__/fixtures/parameter-hint-golden.json +6 -6
  67. package/src/__tests__/preset-tiles-generated.test.ts +88 -0
  68. package/src/factory-presets/generate-image.ts +8 -0
  69. package/src/factory-presets/index.ts +10 -1
  70. package/src/factory-presets/preview-files.generated.ts +639 -1
  71. package/src/factory-presets/previews.ts +34 -2
  72. package/src/factory-presets/tiles/business-and-marketing.generated.ts +426 -0
  73. package/src/factory-presets/tiles/business.generated.ts +261 -0
  74. package/src/factory-presets/tiles/content-and-creative.generated.ts +286 -0
  75. package/src/factory-presets/tiles/family-and-events.generated.ts +222 -0
  76. package/src/factory-presets/tiles/home-and-spaces.generated.ts +231 -0
  77. package/src/factory-presets/tiles/index.generated.ts +32 -0
  78. package/src/factory-presets/tiles/layout.generated.ts +110 -0
  79. package/src/factory-presets/tiles/life-and-events.generated.ts +246 -0
  80. package/src/factory-presets/tiles/personal-photos.generated.ts +306 -0
  81. package/src/factory-presets/tiles/products-and-fashion.generated.ts +303 -0
  82. package/src/factory-presets/tiles/shorts-tiktok.generated.ts +156 -0
  83. package/src/factory-presets/tiles/social-and-ads.generated.ts +606 -0
  84. package/src/factory-presets/tiles/text.generated.ts +591 -0
  85. package/src/factory-presets/tiles/youtube.generated.ts +906 -0
  86. package/src/factory-presets/types.ts +14 -0
  87. package/src/photographer.ts +277 -0
  88. package/src/picker-art/look-preview-sets.ts +189 -0
  89. package/dist/chunk-CYMIXKFT.js.map +0 -1
  90. package/dist/chunk-JCYK7NSR.js.map +0 -1
  91. package/dist/chunk-LZ53F5BN.cjs +0 -5652
  92. package/dist/chunk-LZ53F5BN.cjs.map +0 -1
  93. package/dist/chunk-RB2F5JQF.js +0 -5621
  94. package/dist/chunk-RB2F5JQF.js.map +0 -1
  95. package/dist/chunk-SB7AYE6S.cjs.map +0 -1
  96. package/dist/chunk-WARNUGXT.cjs.map +0 -1
@@ -1,22 +1,22 @@
1
1
  'use strict';
2
2
 
3
- var chunkZLRB5BVT_cjs = require('./chunk-ZLRB5BVT.cjs');
4
- var chunkJDNHMVEV_cjs = require('./chunk-JDNHMVEV.cjs');
3
+ var chunkPHR4UETI_cjs = require('./chunk-PHR4UETI.cjs');
4
+ var chunkFCTK7RGO_cjs = require('./chunk-FCTK7RGO.cjs');
5
5
 
6
6
  // src/read-node-direction.ts
7
7
  function readDirectionFields(value) {
8
8
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
9
9
  const src = value;
10
10
  const out = {};
11
- for (const spec of chunkJDNHMVEV_cjs.DIRECTION_FIELDS) {
11
+ for (const spec of chunkFCTK7RGO_cjs.DIRECTION_FIELDS) {
12
12
  const v = src[spec.key];
13
13
  if (typeof v === "string") {
14
- if (v.length > 0 && v.length <= chunkJDNHMVEV_cjs.DIRECTION_ID_MAX_CHARS) out[spec.key] = v;
14
+ if (v.length > 0 && v.length <= chunkFCTK7RGO_cjs.DIRECTION_ID_MAX_CHARS) out[spec.key] = v;
15
15
  } else if (Array.isArray(v)) {
16
16
  const kept = [];
17
17
  for (const x of v) {
18
- if (kept.length >= chunkJDNHMVEV_cjs.DIRECTION_ARRAY_CEILING) break;
19
- if (typeof x === "string" && x.length > 0 && x.length <= chunkJDNHMVEV_cjs.DIRECTION_ID_MAX_CHARS) {
18
+ if (kept.length >= chunkFCTK7RGO_cjs.DIRECTION_ARRAY_CEILING) break;
19
+ if (typeof x === "string" && x.length > 0 && x.length <= chunkFCTK7RGO_cjs.DIRECTION_ID_MAX_CHARS) {
20
20
  kept.push(x);
21
21
  }
22
22
  }
@@ -29,19 +29,19 @@ function readSubjectFields(value) {
29
29
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
30
30
  const src = value;
31
31
  const out = {};
32
- for (const key of chunkZLRB5BVT_cjs.getRegisteredSubjectKeys()) {
32
+ for (const key of chunkPHR4UETI_cjs.getRegisteredSubjectKeys()) {
33
33
  const v = src[key];
34
- if (key === chunkZLRB5BVT_cjs.SUBJECT_CUSTOM_AGE_KEY) {
34
+ if (key === chunkPHR4UETI_cjs.SUBJECT_CUSTOM_AGE_KEY) {
35
35
  if (typeof v === "number" && Number.isFinite(v)) out[key] = v;
36
36
  continue;
37
37
  }
38
38
  if (typeof v === "string") {
39
- if (v.length > 0 && v.length <= chunkZLRB5BVT_cjs.SUBJECT_ID_MAX_CHARS) out[key] = v;
39
+ if (v.length > 0 && v.length <= chunkPHR4UETI_cjs.SUBJECT_ID_MAX_CHARS) out[key] = v;
40
40
  } else if (Array.isArray(v)) {
41
41
  const kept = [];
42
42
  for (const x of v) {
43
- if (kept.length >= chunkZLRB5BVT_cjs.SUBJECT_ARRAY_CEILING) break;
44
- if (typeof x === "string" && x.length > 0 && x.length <= chunkZLRB5BVT_cjs.SUBJECT_ID_MAX_CHARS) {
43
+ if (kept.length >= chunkPHR4UETI_cjs.SUBJECT_ARRAY_CEILING) break;
44
+ if (typeof x === "string" && x.length > 0 && x.length <= chunkPHR4UETI_cjs.SUBJECT_ID_MAX_CHARS) {
45
45
  kept.push(x);
46
46
  }
47
47
  }
@@ -118,5 +118,5 @@ function readStructuredFields(value) {
118
118
  exports.readDirectionFields = readDirectionFields;
119
119
  exports.readStructuredFields = readStructuredFields;
120
120
  exports.readSubjectFields = readSubjectFields;
121
- //# sourceMappingURL=chunk-4YUXR23B.cjs.map
122
- //# sourceMappingURL=chunk-4YUXR23B.cjs.map
121
+ //# sourceMappingURL=chunk-VAK4RUNU.cjs.map
122
+ //# sourceMappingURL=chunk-VAK4RUNU.cjs.map
@@ -1 +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-4YUXR23B.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"]}
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-VAK4RUNU.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"]}
@@ -48,6 +48,7 @@ var LOOK_PREVIEW_SETS = {
48
48
  pastel: "https://cdn.nodaro.ai/picker-renders/6a1a90ae-c9ea-4f2e-842b-f5572215a373.png",
49
49
  "acrylic-paint": "https://cdn.nodaro.ai/picker-renders/23191845-ef25-4152-8877-2e63daf2f2ac.png",
50
50
  "mixed-media": "https://cdn.nodaro.ai/picker-renders/d36ab150-9cd6-47ac-bdd9-3b26990a4fa9.png",
51
+ "early-color-photo": "https://cdn.nodaro.ai/picker-renders/1bf61f3d-c174-466f-9092-5de72dc7abea.png",
51
52
  manga: "https://cdn.nodaro.ai/picker-renders/3d6bbfd8-8c62-4769-b62e-11d2a7e43b4b.png"
52
53
  },
53
54
  "color-look": {
@@ -469,6 +470,194 @@ var LOOK_PREVIEW_SETS = {
469
470
  "phone-flip": "https://cdn.nodaro.ai/picker-renders/884eb274-2c24-46ca-bf32-1e1833c7e1ba.mp4",
470
471
  "gentle-drift": "https://cdn.nodaro.ai/picker-renders/b8d23d14-5089-4f10-87a6-622a388d4edc.mp4",
471
472
  parallax: "https://cdn.nodaro.ai/picker-renders/c6a97b45-ff45-48cb-b8be-37af726ff485.mp4"
473
+ },
474
+ // Photographers, directors, photo genres and aesthetics (2026-10-26): one shared scene per catalog, so the options compare side by side.
475
+ "photographer": {
476
+ "wes-anderson": "https://cdn.nodaro.ai/picker-renders/16e6abb2-53ae-41e3-b8f7-3f1541dcefdc.png",
477
+ "wong-kar-wai": "https://cdn.nodaro.ai/picker-renders/5b45c4cc-a81c-4c1d-b95e-7ba8a92a7c4b.png",
478
+ "christopher-nolan": "https://cdn.nodaro.ai/picker-renders/c74cd2c1-a156-454f-8778-9ab2732f170d.png",
479
+ "denis-villeneuve": "https://cdn.nodaro.ai/picker-renders/f48ff50d-c264-421d-8651-495fbaf55b28.png",
480
+ "stanley-kubrick": "https://cdn.nodaro.ai/picker-renders/f7036957-85e3-42b4-a180-74cd5d379738.png",
481
+ "quentin-tarantino": "https://cdn.nodaro.ai/picker-renders/9811fdb1-b89b-4d62-9ec3-b2cdec94c598.png",
482
+ "david-fincher": "https://cdn.nodaro.ai/picker-renders/509eb6f5-b8d2-441c-8d88-3842957b4d9e.png",
483
+ "tim-burton": "https://cdn.nodaro.ai/picker-renders/e7c5c3f7-43e7-4125-b73d-87bbda117703.png",
484
+ "ridley-scott": "https://cdn.nodaro.ai/picker-renders/89a0cbdb-378f-4024-8f8d-feaf7daf5df4.png",
485
+ "david-lynch": "https://cdn.nodaro.ai/picker-renders/0bce1595-4605-410c-a278-ea481c04ec57.png",
486
+ "terrence-malick": "https://cdn.nodaro.ai/picker-renders/5a591488-4f16-4b41-9d42-977229b176ab.png",
487
+ "hayao-miyazaki": "https://cdn.nodaro.ai/picker-renders/ebbd1784-cfe7-4029-a869-037286be781f.png",
488
+ "bong-joon-ho": "https://cdn.nodaro.ai/picker-renders/e11eb871-2dae-497f-a4ed-96fc42d1349a.png",
489
+ "park-chan-wook": "https://cdn.nodaro.ai/picker-renders/03d87feb-ef1e-4996-9cc7-a410f2721278.png",
490
+ "paul-thomas-anderson": "https://cdn.nodaro.ai/picker-renders/bf63d708-105d-463d-958f-3d56fa816db2.png",
491
+ "coen-brothers": "https://cdn.nodaro.ai/picker-renders/f27352a0-ea4a-4541-a494-977a89ffa38e.png",
492
+ "nicolas-winding-refn": "https://cdn.nodaro.ai/picker-renders/dff1d04d-f7f9-41d6-b4ee-9b6b180739ad.png",
493
+ "baz-luhrmann": "https://cdn.nodaro.ai/picker-renders/ea2d50b3-0fbd-40b9-b2cb-ca3399d6739d.png",
494
+ "alfred-hitchcock": "https://cdn.nodaro.ai/picker-renders/5d66f6a6-202a-4205-bed2-795fcaec75fe.png",
495
+ "yorgos-lanthimos": "https://cdn.nodaro.ai/picker-renders/fddb54bc-9a69-4087-9e3c-4e081f3db626.png",
496
+ "guillermo-del-toro": "https://cdn.nodaro.ai/picker-renders/55fae819-87b6-44d7-8215-8a160c694d99.png",
497
+ "sofia-coppola": "https://cdn.nodaro.ai/picker-renders/5a0b6b07-3159-421a-8d6a-2cb49d4d5dc8.png",
498
+ "hoyte-van-hoytema": "https://cdn.nodaro.ai/picker-renders/05bd0919-3511-4025-8ae5-e67bf4efe8bb.png",
499
+ "gordon-willis": "https://cdn.nodaro.ai/picker-renders/d3788d7d-8821-4579-8041-79a93b04ab68.png",
500
+ "bradford-young": "https://cdn.nodaro.ai/picker-renders/4f15c594-0a00-4000-ba09-4fc107129020.png",
501
+ "robert-richardson": "https://cdn.nodaro.ai/picker-renders/2905572f-21a4-45d4-ba53-ca861a37a7c3.png",
502
+ "janusz-kaminski": "https://cdn.nodaro.ai/picker-renders/ce117ad3-7700-4f3c-a616-ddcfad1017c3.png",
503
+ "vittorio-storaro": "https://cdn.nodaro.ai/picker-renders/6d81c236-5df9-4fd5-b736-5c8b92de3bf4.png",
504
+ "linus-sandgren": "https://cdn.nodaro.ai/picker-renders/e58665cb-dcde-4b25-b291-93c71d2860b0.png",
505
+ "darius-khondji": "https://cdn.nodaro.ai/picker-renders/3f15f58b-63fe-4dc4-b1cc-5f4f1aaccf68.png",
506
+ "tim-walker": "https://cdn.nodaro.ai/picker-renders/321356b7-1aaf-44d4-8bdd-28458f7e1d0c.png",
507
+ "paolo-roversi": "https://cdn.nodaro.ai/picker-renders/4f6f8e2f-ca7b-445c-881d-81ee619a3e18.png",
508
+ "marta-bevacqua": "https://cdn.nodaro.ai/picker-renders/041a2a1a-cce6-4347-a323-dfb8c59b0ba6.png",
509
+ "patrick-demarchelier": "https://cdn.nodaro.ai/picker-renders/dac7ef9f-04a2-4930-a2a0-cfb65ca33a14.png",
510
+ "nick-knight": "https://cdn.nodaro.ai/picker-renders/fb54182b-ec5e-4505-9cca-69406f9e63ea.png",
511
+ "mario-testino": "https://cdn.nodaro.ai/picker-renders/cb94a583-2e6f-42db-8627-928eecbcd1aa.png",
512
+ "steven-meisel": "https://cdn.nodaro.ai/picker-renders/ee65583a-e0d9-4b65-91e5-5898ff81014a.png",
513
+ "helmut-newton": "https://cdn.nodaro.ai/picker-renders/97faa3cf-f289-420a-b8ea-0d1eedd6a4ef.png",
514
+ "mario-sorrenti": "https://cdn.nodaro.ai/picker-renders/8f315f17-22dd-49e9-b5e6-6eb18c2b202d.png",
515
+ "annie-leibovitz": "https://cdn.nodaro.ai/picker-renders/d20f6e6e-ad03-4e74-9db7-639c11666e6d.png",
516
+ "felicia-simion": "https://cdn.nodaro.ai/picker-renders/1c919f80-20e6-4d8b-836b-e10ba3219454.png",
517
+ "oleg-oprisco": "https://cdn.nodaro.ai/picker-renders/052bf99a-71a8-4e8b-9149-1928c0a47f63.png",
518
+ "bella-kotak": "https://cdn.nodaro.ai/picker-renders/fd4d2a4c-37b3-4e76-b42e-ff16ff54ccf3.png",
519
+ "yigal-ozeri": "https://cdn.nodaro.ai/picker-renders/d8a03822-8256-4001-9e53-d647f005f17f.png",
520
+ "jimmy-marble": "https://cdn.nodaro.ai/picker-renders/e2c0f1dd-037a-4c0e-ad6b-e9eec1ad58fd.png",
521
+ "rinko-kawauchi": "https://cdn.nodaro.ai/picker-renders/b1bf82d8-dec5-4fbc-b0a3-89fe749845da.png",
522
+ "ellen-von-unwerth": "https://cdn.nodaro.ai/picker-renders/636be511-73da-4068-92c6-7c9a5fd0bdeb.png",
523
+ mapplethorpe: "https://cdn.nodaro.ai/picker-renders/6cb3fc00-5602-4c1f-a250-564051763e09.png",
524
+ sherman: "https://cdn.nodaro.ai/picker-renders/5efdaac4-7daf-44b2-9af8-ab6c1938c8cb.png",
525
+ crewdson: "https://cdn.nodaro.ai/picker-renders/5ff8e514-42a2-4399-9898-e0d02f5fe22a.png",
526
+ lachapelle: "https://cdn.nodaro.ai/picker-renders/7c6a6959-227e-461d-b824-1648561e36ad.png",
527
+ klein: "https://cdn.nodaro.ai/picker-renders/8e195064-2f42-4b33-aef2-6cc0adeb22db.png",
528
+ lindbergh: "https://cdn.nodaro.ai/picker-renders/cd2b571f-23c9-4e22-bf96-2dfa41dc343e.png",
529
+ tillmans: "https://cdn.nodaro.ai/picker-renders/d41deb72-48c7-4883-b22b-0b4aed2c876c.png",
530
+ teller: "https://cdn.nodaro.ai/picker-renders/72765c40-262a-4ee0-bdfe-f1bb02e87154.png",
531
+ penn: "https://cdn.nodaro.ai/picker-renders/c359d48d-429a-4e2f-a322-051be83e233b.png",
532
+ mcginley: "https://cdn.nodaro.ai/picker-renders/0a245a85-0562-42c2-b54d-a0074e497152.png",
533
+ mitchell: "https://cdn.nodaro.ai/picker-renders/5e3134ef-e7d3-4396-ab73-4da593279ba6.png",
534
+ collins: "https://cdn.nodaro.ai/picker-renders/ede69fb6-9fbe-48eb-821e-301f8a4cfa5b.png",
535
+ weston: "https://cdn.nodaro.ai/picker-renders/78ba0fdb-2a5e-4342-9251-da76be4dacb7.png",
536
+ beaton: "https://cdn.nodaro.ai/picker-renders/13053951-77b1-44e0-abf6-6cf8c471c17e.png",
537
+ "henri-cartier-bresson": "https://cdn.nodaro.ai/picker-renders/24234577-3721-4b9f-901d-8f05777e1465.png",
538
+ "vivian-maier": "https://cdn.nodaro.ai/picker-renders/b39d1d53-b3da-4f00-9f55-423b8f358721.png",
539
+ "saul-leiter": "https://cdn.nodaro.ai/picker-renders/6f1a1e2f-3311-457c-830c-f83186270d3b.png",
540
+ "daido-moriyama": "https://cdn.nodaro.ai/picker-renders/4320e542-5dac-4970-9b33-e218f0b8871e.png",
541
+ "robert-capa": "https://cdn.nodaro.ai/picker-renders/b892b5b1-8a48-4e6a-8f8f-c62e6ecfec07.png",
542
+ "sebastiao-salgado": "https://cdn.nodaro.ai/picker-renders/6a386278-923a-4b0f-b631-7746aecd142e.png",
543
+ "diane-arbus": "https://cdn.nodaro.ai/picker-renders/b287de74-9bc3-4f85-9d31-ece513dbfdc0.png",
544
+ "roger-deakins": "https://cdn.nodaro.ai/picker-renders/3701db00-eef8-4512-9b46-2a7513d64f49.png",
545
+ "emmanuel-lubezki": "https://cdn.nodaro.ai/picker-renders/dd7fee63-afb9-4ff8-8b3f-882bf06a2f1b.png",
546
+ "greig-fraser": "https://cdn.nodaro.ai/picker-renders/00eace4b-f866-4abc-9c5c-58b7d170274d.png",
547
+ "christopher-doyle": "https://cdn.nodaro.ai/picker-renders/e4ef15fa-8bd1-48a3-ade1-161d6f1f394b.png",
548
+ "greg-rutkowski": "https://cdn.nodaro.ai/picker-renders/51667584-ba89-4c25-aba4-18983fd18d64.png",
549
+ "magali-villeneuve": "https://cdn.nodaro.ai/picker-renders/04309561-182a-48d6-92bf-937834700d5b.png",
550
+ "charlie-bowater": "https://cdn.nodaro.ai/picker-renders/2bad3290-4433-4781-a77a-bdfd0d0801c0.png",
551
+ "sam-spratt": "https://cdn.nodaro.ai/picker-renders/69a6f59e-ae2b-4e56-8dc2-415c3fa33a6e.png",
552
+ "ruan-jia": "https://cdn.nodaro.ai/picker-renders/06f3fe9e-298b-4857-aec3-06baef5c92dd.png",
553
+ "ilya-kuvshinov": "https://cdn.nodaro.ai/picker-renders/51a79686-8ed8-41f2-9b07-68bbfe957e6c.png",
554
+ wlop: "https://cdn.nodaro.ai/picker-renders/ac58a67d-4d22-4f6e-ac16-003fce12e34d.png",
555
+ artgerm: "https://cdn.nodaro.ai/picker-renders/1cc58c63-280d-4c1d-bc28-704e4919592b.png",
556
+ "makoto-shinkai": "https://cdn.nodaro.ai/picker-renders/6537da46-5da9-4396-abd6-64102399139f.png",
557
+ "studio-ghibli": "https://cdn.nodaro.ai/picker-renders/ddb76bdf-c4b2-404f-ba74-138b94195927.png",
558
+ "alphonse-mucha": "https://cdn.nodaro.ai/picker-renders/bb032d83-7f3d-4e95-90bf-858f05ae9a62.png",
559
+ "carne-griffiths": "https://cdn.nodaro.ai/picker-renders/a44b66c1-11fe-457d-bb25-a4e2305db43f.png",
560
+ "conrad-roset": "https://cdn.nodaro.ai/picker-renders/f077b8ba-fccc-44d2-b075-e733b79688fa.png",
561
+ "akihito-yoshida": "https://cdn.nodaro.ai/picker-renders/d8ec1b78-2759-498c-abf0-c68d5ad17c67.png",
562
+ "karol-bak": "https://cdn.nodaro.ai/picker-renders/5d58a213-04fa-4d77-8300-7030a22512b5.png",
563
+ "ismail-inceoglu": "https://cdn.nodaro.ai/picker-renders/fb531f75-ccb5-4281-a0eb-65f9c56ede4e.png",
564
+ "stefan-gesell": "https://cdn.nodaro.ai/picker-renders/57092b8d-1208-4c57-b18a-03832f545126.png",
565
+ "andrew-atroshenko": "https://cdn.nodaro.ai/picker-renders/fa6c8f58-f072-4e62-8365-52307a49d280.png",
566
+ "peter-gric": "https://cdn.nodaro.ai/picker-renders/a1840721-6b81-481c-9cdc-e86566359d5a.png",
567
+ "ingrid-baars": "https://cdn.nodaro.ai/picker-renders/a9fe3d36-3f54-4f18-9994-6352837aebee.png",
568
+ "guido-van-helten": "https://cdn.nodaro.ai/picker-renders/11e0ec67-a538-4f92-bf86-a3a8f302efd2.png"
569
+ },
570
+ "photo-genre": {
571
+ "fashion-editorial": "https://cdn.nodaro.ai/picker-renders/30fb59c7-34b5-4a04-bc88-fab101d6c185.png",
572
+ "vogue-editorial": "https://cdn.nodaro.ai/picker-renders/a13515c4-96f0-4946-90c1-d4c34beff9b7.png",
573
+ "magazine-cover": "https://cdn.nodaro.ai/picker-renders/97e6a9af-f39d-434a-a0f3-274fb38e9a8e.png",
574
+ lookbook: "https://cdn.nodaro.ai/picker-renders/7373e4fe-80ba-480c-923c-59ca5a4e5642.png",
575
+ "ecommerce-flatlay": "https://cdn.nodaro.ai/picker-renders/5c569c0a-c8a0-420a-bba9-0647f818f920.png",
576
+ "beauty-editorial": "https://cdn.nodaro.ai/picker-renders/926272df-29bd-4125-921c-8cb4cf628ca8.png",
577
+ "campaign-advertising": "https://cdn.nodaro.ai/picker-renders/7ae2854b-d4c5-443c-b248-780cee183184.png",
578
+ "brand-vogue": "https://cdn.nodaro.ai/picker-renders/42f8a830-7804-4f33-8e2a-de5cb9ad179d.png",
579
+ "brand-dior": "https://cdn.nodaro.ai/picker-renders/2b9e8fdb-8f3c-4401-ae64-a7590fa5d03f.png",
580
+ "brand-jil-sander": "https://cdn.nodaro.ai/picker-renders/b8d9b796-0879-4d8b-b96b-945e72849af1.png",
581
+ "brand-vivienne-tam": "https://cdn.nodaro.ai/picker-renders/f4d1ba4d-6960-4f78-a76d-e1373742342a.png",
582
+ "brand-jacquemus": "https://cdn.nodaro.ai/picker-renders/0ca41289-717d-4055-9988-b2d5bb66e256.png",
583
+ "brand-helmut-newton": "https://cdn.nodaro.ai/picker-renders/f43f3adb-8bda-4c09-adbd-0af2835e8377.png",
584
+ "brand-harpers-bazaar": "https://cdn.nodaro.ai/picker-renders/143672ba-fcb7-4db1-b71e-eceedd950352.png",
585
+ paparazzi: "https://cdn.nodaro.ai/picker-renders/58ed479f-a2c0-49c6-bd80-425b843a396e.png",
586
+ "street-photography": "https://cdn.nodaro.ai/picker-renders/ff18935c-8d05-4c5d-a56c-dd3f2da73cf0.png",
587
+ "candid-journalism": "https://cdn.nodaro.ai/picker-renders/6788f545-634c-4607-b5ff-96617cd43e19.png",
588
+ photojournalism: "https://cdn.nodaro.ai/picker-renders/f40cef85-bacf-4f70-8fab-f87dd012f5fe.png",
589
+ documentary: "https://cdn.nodaro.ai/picker-renders/b362bcc7-22ad-486f-9089-c879dfdd858d.png",
590
+ snapshot: "https://cdn.nodaro.ai/picker-renders/03beb44a-7187-424e-957e-28b6efbfb84d.png",
591
+ "corporate-headshot": "https://cdn.nodaro.ai/picker-renders/0adc3299-6890-418c-ae4e-74969ecf8f67.png",
592
+ "personal-branding": "https://cdn.nodaro.ai/picker-renders/fd28370a-06c5-433b-a6e3-3306bf92272c.png",
593
+ yearbook: "https://cdn.nodaro.ai/picker-renders/62200298-7d08-4fbe-a312-1ed8a0d15318.png",
594
+ "id-passport": "https://cdn.nodaro.ai/picker-renders/6e4e7a24-b2fb-4fbd-a417-5a86538fe259.png",
595
+ mugshot: "https://cdn.nodaro.ai/picker-renders/f164fab3-9ecd-45f6-97fc-1dc0f8828903.png",
596
+ "wedding-portrait": "https://cdn.nodaro.ai/picker-renders/8e62096f-3e83-4161-819d-21e31b8bc686.png",
597
+ "family-portrait": "https://cdn.nodaro.ai/picker-renders/986ab439-08f9-4f22-9eb5-f790b88ca766.png",
598
+ "glamour-portrait": "https://cdn.nodaro.ai/picker-renders/ea90737d-9326-4c6a-9604-a6c9d84b1217.png",
599
+ "film-noir": "https://cdn.nodaro.ai/picker-renders/1a299f95-8aa3-42c8-a487-5112fbf011b4.png",
600
+ "mirror-selfie": "https://cdn.nodaro.ai/picker-renders/cfd1198d-f486-4ae9-93ef-e6a8e0682097.png",
601
+ "gym-mirror-selfie": "https://cdn.nodaro.ai/picker-renders/4035f8e1-4823-463c-91de-266ae859af3a.png",
602
+ "front-cam-selfie": "https://cdn.nodaro.ai/picker-renders/5d315603-956c-4267-b54f-c521b54b5bc5.png",
603
+ "bathroom-mirror-selfie": "https://cdn.nodaro.ai/picker-renders/8be20087-2181-419a-b348-b77b92447360.png",
604
+ "bereal-dual": "https://cdn.nodaro.ai/picker-renders/d5739c3b-59f6-4dac-a3da-2a274f950099.png",
605
+ "flip-cam-selfie": "https://cdn.nodaro.ai/picker-renders/fb9c39b3-3f39-4c39-97fa-473ad118756c.png",
606
+ "group-selfie": "https://cdn.nodaro.ai/picker-renders/57058342-89aa-4290-a383-f1016119cb71.png",
607
+ "lofi-baddie-selfie": "https://cdn.nodaro.ai/picker-renders/3c23b09f-5792-4b46-bb3d-f6de439088b8.png",
608
+ "album-cover": "https://cdn.nodaro.ai/picker-renders/778d5190-0477-4075-b788-c4a8d1097394.png",
609
+ "movie-poster": "https://cdn.nodaro.ai/picker-renders/16c59451-5470-4a06-a9c2-bfc3b9be9c65.png",
610
+ advertising: "https://cdn.nodaro.ai/picker-renders/83a97312-6326-43ac-8a6d-c769b221abd7.png",
611
+ "food-photography": "https://cdn.nodaro.ai/picker-renders/d433fbc1-b1e0-4350-b7e3-a384f2f343cf.png",
612
+ "real-estate": "https://cdn.nodaro.ai/picker-renders/672647d8-2e26-4801-9fde-89da8de144bd.png",
613
+ "sports-action": "https://cdn.nodaro.ai/picker-renders/6d87fe5d-b925-421d-aa91-f1d57335d810.png",
614
+ "point-and-shoot": "https://cdn.nodaro.ai/picker-renders/8cbed8d9-6d0e-4e89-9423-3245ecb02db3.png",
615
+ "lifestyle-blog": "https://cdn.nodaro.ai/picker-renders/9fab192f-bb07-4876-8313-5330f5b96052.png",
616
+ "product-shot": "https://cdn.nodaro.ai/picker-renders/e7b81986-ad54-49fa-a8ce-5ce80018e9cc.png"
617
+ },
618
+ "aesthetic": {
619
+ y2k: "https://cdn.nodaro.ai/picker-renders/b15382cc-9616-4959-8fcc-18020ca96558.png",
620
+ cottagecore: "https://cdn.nodaro.ai/picker-renders/3c25e839-34b7-4662-b42a-8f83af90e2b2.png",
621
+ "dark-academia": "https://cdn.nodaro.ai/picker-renders/8075f26d-3ef3-455e-933f-65dc5a4bbb75.png",
622
+ "light-academia": "https://cdn.nodaro.ai/picker-renders/8755af2b-01d6-43a5-8a8d-4b97983549ee.png",
623
+ techwear: "https://cdn.nodaro.ai/picker-renders/035a1836-53c2-4b22-b71b-0dcd3a151004.png",
624
+ "old-money": "https://cdn.nodaro.ai/picker-renders/22e6e0d4-8356-4bf9-9069-4171ca13c460.png",
625
+ streetwear: "https://cdn.nodaro.ai/picker-renders/0a01ffc5-612e-4400-9336-e7537fb64d0e.png",
626
+ fairycore: "https://cdn.nodaro.ai/picker-renders/928a03d6-b573-4769-91d1-6e539d30d51c.png",
627
+ goblincore: "https://cdn.nodaro.ai/picker-renders/bf8aaaa1-bd2d-45e0-bc47-da14e12be795.png",
628
+ normcore: "https://cdn.nodaro.ai/picker-renders/d3ec73e0-4d20-4f0b-9a35-405d14baf1da.png",
629
+ balletcore: "https://cdn.nodaro.ai/picker-renders/e6e86b7e-84d5-40e0-9ee8-ffb574522abc.png",
630
+ "e-girl": "https://cdn.nodaro.ai/picker-renders/30e47dfb-c60e-4353-8cc7-8e5c58ac2556.png",
631
+ "soft-girl": "https://cdn.nodaro.ai/picker-renders/801cda5f-d99c-4ba1-903b-9a6c7703639f.png",
632
+ "indie-sleaze": "https://cdn.nodaro.ai/picker-renders/0b3f71bc-6d87-472f-a18a-994566712e0e.png",
633
+ baddie: "https://cdn.nodaro.ai/picker-renders/32a1004c-fffa-441c-8f80-ee405681e840.png",
634
+ "vacation-dadcore": "https://cdn.nodaro.ai/picker-renders/bcde5e4e-d045-4290-9431-963eecc9ef87.png",
635
+ womancore: "https://cdn.nodaro.ai/picker-renders/1084cd75-96f5-4b96-ac4b-a638fbfdef77.png",
636
+ hikecore: "https://cdn.nodaro.ai/picker-renders/1e82743e-b6f2-4393-b30c-353311f9b20f.png",
637
+ "mote-kei": "https://cdn.nodaro.ai/picker-renders/d25987ec-fefc-44d2-a6b0-23e82030c9cc.png",
638
+ kombuchapunk: "https://cdn.nodaro.ai/picker-renders/4ecacd9b-7263-4a06-beee-9b7c2d9e7a2a.png",
639
+ chinapunk: "https://cdn.nodaro.ai/picker-renders/ac7372b4-d30c-4539-9746-72b8ef8d21d0.png",
640
+ karencore: "https://cdn.nodaro.ai/picker-renders/2b781211-88fe-4a4b-aac5-06abacedeb2f.png",
641
+ catcore: "https://cdn.nodaro.ai/picker-renders/f73cdf85-8490-453b-9e79-3b8d5f782b15.png",
642
+ weirdcore: "https://cdn.nodaro.ai/picker-renders/ee67e392-097e-45a6-9395-aa0899aa776c.png",
643
+ "alt-girl": "https://cdn.nodaro.ai/picker-renders/c7771e00-b5fc-4394-901e-d1009795910e.png",
644
+ blokecore: "https://cdn.nodaro.ai/picker-renders/aa70cfae-4288-4cf2-ae26-8cc3baf02ea1.png",
645
+ minimalism: "https://cdn.nodaro.ai/picker-renders/b654d4d4-4d52-4582-9360-f37f40f3708d.png",
646
+ maximalism: "https://cdn.nodaro.ai/picker-renders/7fc041ab-ae07-4e57-8e6d-d2d36326d229.png",
647
+ "avant-garde": "https://cdn.nodaro.ai/picker-renders/1d0b0e3b-6417-4f33-b74a-de43f5141b39.png",
648
+ "old-hollywood-glam": "https://cdn.nodaro.ai/picker-renders/8da044e9-c628-405a-b2d9-af8e438c37ce.png",
649
+ "main-character-energy": "https://cdn.nodaro.ai/picker-renders/d91a6fd4-cc5f-4e1a-a7ae-8d157e043e8d.png",
650
+ "soft-easygoing": "https://cdn.nodaro.ai/picker-renders/4a9c0a98-fedf-46c4-a180-c56ec5785226.png",
651
+ "casual-lived-in": "https://cdn.nodaro.ai/picker-renders/61febc19-60a3-4802-b9f7-7284297e83d1.png",
652
+ "effortless-cool": "https://cdn.nodaro.ai/picker-renders/3fcfc78c-4661-4599-90d0-d77456a90346.png",
653
+ "retro-90s": "https://cdn.nodaro.ai/picker-renders/e3d34d5b-7b0e-4bb2-aae2-c5ebb96c03b3.png",
654
+ "y2k-rave": "https://cdn.nodaro.ai/picker-renders/85b2becd-4cde-433f-a027-90176227e877.png",
655
+ babycore: "https://cdn.nodaro.ai/picker-renders/4dae4873-d272-4f9f-a35d-acf9c8bd1841.png",
656
+ coquette: "https://cdn.nodaro.ai/picker-renders/88781f19-be05-44cd-b3d9-a2a0f988b9d1.png",
657
+ candycore: "https://cdn.nodaro.ai/picker-renders/d7157d3e-7f3b-43e7-b0b2-8f4373e13ad1.png",
658
+ traumacore: "https://cdn.nodaro.ai/picker-renders/eb0842dc-48e5-4b16-8ced-e8d21129faa6.png",
659
+ dreamcore: "https://cdn.nodaro.ai/picker-renders/665494ef-6aa2-4f3d-8048-ec67e5eeac6d.png",
660
+ angelcore: "https://cdn.nodaro.ai/picker-renders/bdcc01ad-b62d-42de-a180-ae65e78837cc.png"
472
661
  }
473
662
  };
474
663
 
@@ -3380,5 +3569,5 @@ function pickerSectionImageUrl(sectionLabel, images) {
3380
3569
  }
3381
3570
 
3382
3571
  export { CHARACTER_ART_FAMILIES, CHARACTER_ART_FILES, CHARACTER_ART_PATH, LOOK_PREVIEW_SETS, PICKER_ART_PATH, RENDER_WIDTH_LADDER, SOUND_ART, SOUND_ART_FILES, SOUND_RENDER_SETS, characterArtPath, characterSectionArtPath, characterSectionSlug, lookPreviewStillUrl, pickerOptionImage, pickerOptionImageUrl, pickerSectionImageUrl, soundArtFilePath, soundArtPath };
3383
- //# sourceMappingURL=chunk-JCYK7NSR.js.map
3384
- //# sourceMappingURL=chunk-JCYK7NSR.js.map
3572
+ //# sourceMappingURL=chunk-XZXUU3YH.js.map
3573
+ //# sourceMappingURL=chunk-XZXUU3YH.js.map