@nodaro/prompts 1.38.0 → 1.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2EW5B7ZV.cjs +86 -0
- package/dist/chunk-2EW5B7ZV.cjs.map +1 -0
- package/dist/chunk-2P26SZX5.cjs +414 -0
- package/dist/chunk-2P26SZX5.cjs.map +1 -0
- package/dist/{chunk-7XKHDXCL.cjs → chunk-3U7X6JZN.cjs} +13 -12
- package/dist/chunk-3U7X6JZN.cjs.map +1 -0
- package/dist/chunk-4GKTOVFM.cjs +8 -0
- package/dist/chunk-4GKTOVFM.cjs.map +1 -0
- package/dist/{chunk-HRBBNODV.js → chunk-6GVFJLDL.js} +4 -3
- package/dist/chunk-6GVFJLDL.js.map +1 -0
- package/dist/{chunk-LVSH7K7R.cjs → chunk-C3NHNECH.cjs} +165 -88
- package/dist/chunk-C3NHNECH.cjs.map +1 -0
- package/dist/{chunk-CEPVQQVO.js → chunk-C46256GZ.js} +43 -5
- package/dist/chunk-C46256GZ.js.map +1 -0
- package/dist/{chunk-JKCR6WRZ.cjs → chunk-DQFQJECU.cjs} +43 -5
- package/dist/chunk-DQFQJECU.cjs.map +1 -0
- package/dist/{chunk-QDEWBBUR.js → chunk-DUXX6CBX.js} +12 -143
- package/dist/chunk-DUXX6CBX.js.map +1 -0
- package/dist/chunk-EAYEZXWT.js +388 -0
- package/dist/chunk-EAYEZXWT.js.map +1 -0
- package/dist/chunk-GILUVQQJ.js +6 -0
- package/dist/chunk-GILUVQQJ.js.map +1 -0
- package/dist/{chunk-KIYNOOPJ.js → chunk-GTDKDD7Y.js} +12 -6
- package/dist/chunk-GTDKDD7Y.js.map +1 -0
- package/dist/{chunk-QBEZMR2F.js → chunk-HQTHDTHN.js} +137 -62
- package/dist/chunk-HQTHDTHN.js.map +1 -0
- package/dist/{chunk-TLVX5PXD.cjs → chunk-IUCNJAHW.cjs} +13 -7
- package/dist/chunk-IUCNJAHW.cjs.map +1 -0
- package/dist/{chunk-ZFIXQRBZ.cjs → chunk-LXPKTI2K.cjs} +12 -83
- package/dist/chunk-LXPKTI2K.cjs.map +1 -0
- package/dist/{chunk-CVPB2N3Z.cjs → chunk-LZ53F5BN.cjs} +103 -5
- package/dist/chunk-LZ53F5BN.cjs.map +1 -0
- package/dist/{chunk-4FDNVNJE.js → chunk-N6VMPG5S.js} +12 -79
- package/dist/chunk-N6VMPG5S.js.map +1 -0
- package/dist/{chunk-LK2CZK5U.cjs → chunk-PWWM7Q3T.cjs} +7 -7
- package/dist/{chunk-LK2CZK5U.cjs.map → chunk-PWWM7Q3T.cjs.map} +1 -1
- package/dist/{chunk-N7TOWXDJ.js → chunk-QDUTMOF4.js} +3 -3
- package/dist/{chunk-N7TOWXDJ.js.map → chunk-QDUTMOF4.js.map} +1 -1
- package/dist/{chunk-NO6XHX4R.js → chunk-RB2F5JQF.js} +100 -7
- package/dist/chunk-RB2F5JQF.js.map +1 -0
- package/dist/{chunk-MENBHEAY.cjs → chunk-WRM52ZBN.cjs} +19 -161
- package/dist/chunk-WRM52ZBN.cjs.map +1 -0
- package/dist/chunk-YS6HOWMW.js +80 -0
- package/dist/chunk-YS6HOWMW.js.map +1 -0
- package/dist/entries/assembly.cjs +34 -25
- package/dist/entries/assembly.d.cts +121 -37
- package/dist/entries/assembly.d.ts +121 -37
- package/dist/entries/assembly.js +5 -4
- package/dist/entries/doctrine.cjs +46 -46
- package/dist/entries/doctrine.d.cts +3 -3
- package/dist/entries/doctrine.d.ts +3 -3
- package/dist/entries/doctrine.js +1 -1
- package/dist/entries/picker-catalogs.cjs +42 -42
- package/dist/entries/picker-catalogs.js +3 -3
- package/dist/entries/presets.cjs +49 -26
- package/dist/entries/presets.d.cts +53 -1
- package/dist/entries/presets.d.ts +53 -1
- package/dist/entries/presets.js +4 -1
- package/dist/entries/prompt-builder.cjs +135 -98
- package/dist/entries/prompt-builder.d.cts +22 -11
- package/dist/entries/prompt-builder.d.ts +22 -11
- package/dist/entries/prompt-builder.js +4 -3
- package/dist/entries/prompt-fields.cjs +41 -40
- package/dist/entries/prompt-fields.d.cts +2 -1
- package/dist/entries/prompt-fields.d.ts +2 -1
- package/dist/entries/prompt-fields.js +2 -1
- package/dist/index.cjs +339 -273
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +11 -9
- package/dist/{subject-registry-BUswuEVD.d.cts → subject-registry-CQi_CZfJ.d.cts} +78 -9
- package/dist/{subject-registry-D7V8PEG-.d.ts → subject-registry-DFiGx49v.d.ts} +78 -9
- package/package.json +2 -2
- package/src/__tests__/assemble-image-input-style-references.test.ts +173 -0
- package/src/__tests__/catalog-id-guard.test.ts +30 -0
- package/src/__tests__/factory-preset-previews.test.ts +123 -0
- package/src/__tests__/factory-preset-references.test.ts +222 -0
- package/src/__tests__/factory-presets-prototype-keys.test.ts +43 -0
- package/src/__tests__/frame-reference-slots.test.ts +43 -0
- package/src/__tests__/gemini-omni-inputs.test.ts +12 -10
- package/src/__tests__/provider-prompt-doctrine.test.ts +14 -0
- package/src/__tests__/user-direction-refs.test.ts +180 -0
- package/src/__tests__/veo-i2v-inputs.test.ts +16 -11
- package/src/assemble-image-input.ts +114 -13
- package/src/catalog-id-guard.ts +18 -2
- package/src/color-names.ts +192 -0
- package/src/direction-registry.ts +66 -5
- package/src/entries/assembly.ts +1 -0
- package/src/entries/prompt-builder.ts +2 -0
- package/src/factory-presets/generate-image.ts +29 -0
- package/src/factory-presets/index.ts +6 -1
- package/src/factory-presets/preview-files.generated.ts +4 -0
- package/src/factory-presets/previews.ts +40 -0
- package/src/factory-presets/references.ts +81 -0
- package/src/factory-presets/shared-image.ts +4 -0
- package/src/factory-presets/types.ts +8 -0
- package/src/frame-reference-slots.ts +92 -0
- package/src/gemini-omni-inputs.ts +23 -22
- package/src/image-reference-doctrine.ts +3 -3
- package/src/image-token-pattern.ts +13 -0
- package/src/node-prompt-fields.ts +5 -3
- package/src/own-entry.ts +11 -0
- package/src/prompt-builder.ts +4 -5
- package/src/prompt-style-section.ts +3 -3
- package/src/prompt-wizard-categories.ts +2 -0
- package/src/provider-prompt-doctrine.ts +45 -3
- package/src/ref-binding.ts +28 -3
- package/src/resolve-prompt.ts +2 -1
- package/src/seedance-2-inputs.ts +15 -40
- package/src/user-direction-refs.ts +110 -0
- package/src/veo-i2v-inputs.ts +24 -17
- package/src/video-reference-resolver.ts +1 -1
- package/dist/chunk-4FDNVNJE.js.map +0 -1
- package/dist/chunk-7XKHDXCL.cjs.map +0 -1
- package/dist/chunk-CEPVQQVO.js.map +0 -1
- package/dist/chunk-CVPB2N3Z.cjs.map +0 -1
- package/dist/chunk-HRBBNODV.js.map +0 -1
- package/dist/chunk-JKCR6WRZ.cjs.map +0 -1
- package/dist/chunk-KIYNOOPJ.js.map +0 -1
- package/dist/chunk-LVSH7K7R.cjs.map +0 -1
- package/dist/chunk-MENBHEAY.cjs.map +0 -1
- package/dist/chunk-NO6XHX4R.js.map +0 -1
- package/dist/chunk-QBEZMR2F.js.map +0 -1
- package/dist/chunk-QDEWBBUR.js.map +0 -1
- package/dist/chunk-TLVX5PXD.cjs.map +0 -1
- package/dist/chunk-UJKFHPZ7.js +0 -37
- package/dist/chunk-UJKFHPZ7.js.map +0 -1
- package/dist/chunk-W5SCZIOB.cjs +0 -43
- package/dist/chunk-W5SCZIOB.cjs.map +0 -1
- package/dist/chunk-ZFIXQRBZ.cjs.map +0 -1
package/dist/chunk-UJKFHPZ7.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { composedHas, composedOption } from './chunk-SXXW4LCR.js';
|
|
2
|
-
import { deriveTerm } from './chunk-XAMHEW76.js';
|
|
3
|
-
import { getAnimalPromptHint, getAnimalTerm, getVehicle, getWeapon, getFurniture } from '@nodaro/shared/catalogs';
|
|
4
|
-
|
|
5
|
-
function curatedAnimalPromptHint(id) {
|
|
6
|
-
if (!id) return "";
|
|
7
|
-
if (!composedHas("animals", id)) return "";
|
|
8
|
-
return composedOption("animals", id)?.promptHint ?? getAnimalPromptHint(id);
|
|
9
|
-
}
|
|
10
|
-
function curatedAnimalTerm(id) {
|
|
11
|
-
if (!id) return "";
|
|
12
|
-
if (!composedHas("animals", id)) return "";
|
|
13
|
-
return composedOption("animals", id)?.term ?? getAnimalTerm(id);
|
|
14
|
-
}
|
|
15
|
-
function objectEntityText(catalogId, id, stock, stockHint, stockTerm) {
|
|
16
|
-
if (!id || !composedHas(catalogId, id)) return { hint: "", term: "" };
|
|
17
|
-
const composed = composedOption(catalogId, id);
|
|
18
|
-
if (composed) return { hint: composed.promptHint, term: composed.term };
|
|
19
|
-
if (!stock) return { hint: "", term: "" };
|
|
20
|
-
return { hint: stockHint(stock), term: stockTerm(stock) };
|
|
21
|
-
}
|
|
22
|
-
function objectEntityTerm(entry) {
|
|
23
|
-
return entry.term ?? deriveTerm(entry.label);
|
|
24
|
-
}
|
|
25
|
-
function curatedVehicleText(id) {
|
|
26
|
-
return objectEntityText("vehicles", id, getVehicle(id), (e) => `featuring a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
|
|
27
|
-
}
|
|
28
|
-
function curatedWeaponText(id) {
|
|
29
|
-
return objectEntityText("weapons", id, getWeapon(id), (e) => `with a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
|
|
30
|
-
}
|
|
31
|
-
function curatedFurnitureText(id) {
|
|
32
|
-
return objectEntityText("furniture", id, getFurniture(id), (e) => `including a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export { curatedAnimalPromptHint, curatedAnimalTerm, curatedFurnitureText, curatedVehicleText, curatedWeaponText };
|
|
36
|
-
//# sourceMappingURL=chunk-UJKFHPZ7.js.map
|
|
37
|
-
//# sourceMappingURL=chunk-UJKFHPZ7.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared-catalog-overlay.ts"],"names":[],"mappings":";;;;AAwBO,SAAS,wBAAwB,EAAA,EAAoB;AAC1D,EAAA,IAAI,CAAC,IAAI,OAAO,EAAA;AAChB,EAAA,IAAI,CAAC,WAAA,CAAY,SAAA,EAAW,EAAE,GAAG,OAAO,EAAA;AACxC,EAAA,OAAO,eAAe,SAAA,EAAW,EAAE,CAAA,EAAG,UAAA,IAAc,oBAAoB,EAAE,CAAA;AAC5E;AACO,SAAS,kBAAkB,EAAA,EAAoB;AACpD,EAAA,IAAI,CAAC,IAAI,OAAO,EAAA;AAChB,EAAA,IAAI,CAAC,WAAA,CAAY,SAAA,EAAW,EAAE,GAAG,OAAO,EAAA;AACxC,EAAA,OAAO,eAAe,SAAA,EAAW,EAAE,CAAA,EAAG,IAAA,IAAQ,cAAc,EAAE,CAAA;AAChE;AAIA,SAAS,gBAAA,CACP,SAAA,EACA,EAAA,EACA,KAAA,EACA,WACA,SAAA,EACkD;AAClD,EAAA,IAAI,CAAC,EAAA,IAAM,CAAC,WAAA,CAAY,SAAA,EAAW,EAAE,CAAA,EAAG,OAAO,EAAE,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,EAAA,EAAG;AACpE,EAAA,MAAM,QAAA,GAAW,cAAA,CAAe,SAAA,EAAW,EAAE,CAAA;AAC7C,EAAA,IAAI,QAAA,SAAiB,EAAE,IAAA,EAAM,SAAS,UAAA,EAAY,IAAA,EAAM,SAAS,IAAA,EAAK;AACtE,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAE,IAAA,EAAM,EAAA,EAAI,MAAM,EAAA,EAAG;AACxC,EAAA,OAAO,EAAE,MAAM,SAAA,CAAU,KAAK,GAAG,IAAA,EAAM,SAAA,CAAU,KAAK,CAAA,EAAE;AAC1D;AAqBA,SAAS,iBAAiB,KAAA,EAA2C;AACnE,EAAA,OAAQ,KAAA,CAA4B,IAAA,IAAQ,UAAA,CAAW,KAAA,CAAM,KAAK,CAAA;AACpE;AAEO,SAAS,mBAAmB,EAAA,EAAY;AAC7C,EAAA,OAAO,iBAAiB,UAAA,EAAY,EAAA,EAAI,UAAA,CAAW,EAAE,GAAG,CAAC,CAAA,KAAM,CAAA,YAAA,EAAe,CAAA,CAAE,MAAM,WAAA,EAAa,KAAK,CAAA,CAAE,WAAW,IAAI,gBAAgB,CAAA;AAC3I;AACO,SAAS,kBAAkB,EAAA,EAAY;AAC5C,EAAA,OAAO,iBAAiB,SAAA,EAAW,EAAA,EAAI,SAAA,CAAU,EAAE,GAAG,CAAC,CAAA,KAAM,CAAA,OAAA,EAAU,CAAA,CAAE,MAAM,WAAA,EAAa,KAAK,CAAA,CAAE,WAAW,IAAI,gBAAgB,CAAA;AACpI;AACO,SAAS,qBAAqB,EAAA,EAAY;AAC/C,EAAA,OAAO,iBAAiB,WAAA,EAAa,EAAA,EAAI,YAAA,CAAa,EAAE,GAAG,CAAC,CAAA,KAAM,CAAA,YAAA,EAAe,CAAA,CAAE,MAAM,WAAA,EAAa,KAAK,CAAA,CAAE,WAAW,IAAI,gBAAgB,CAAA;AAC9I","file":"chunk-UJKFHPZ7.js","sourcesContent":["import { getAnimalPromptHint, getAnimalTerm, getVehicle, getWeapon, getFurniture } from \"@nodaro/shared/catalogs\"\nimport { composedHas, composedOption } from \"./catalog-overlay.js\"\nimport { deriveTerm } from \"./term.js\"\n\n/**\n * The four object-entity catalogs — animals, vehicles, weapons, furniture —\n * are OWNED by `@nodaro/shared`, which cannot depend on this package, so\n * their getters cannot carry the `overlayEntry` the other 34 catalogs do.\n * These wrappers are the overlay for them, and EVERY prompt-text read of\n * those catalogs goes through here: the parameter-node dispatcher and the\n * subject fold both. Two call sites resolving the same id to different text\n * — one curated, one stock — is exactly what a second, un-overlaid path\n * produced during review.\n *\n * Precedence per id, same four outcomes as catalog-overlay.ts:\n * - no pack on the catalog → the stock getter, untouched\n * - denied / not in the pack → \"\" (nothing reaches the prompt)\n * - composed option present → ITS promptHint / term. A pack author\n * who rewrote an entry wrote the hint they want injected; rebuilding it\n * from label + description would discard that\n * - pack-added id (no stock row) → the composed option (covered by the\n * branch above — the stock getter is never consulted when a pack exists)\n */\n\nexport function curatedAnimalPromptHint(id: string): string {\n if (!id) return \"\"\n if (!composedHas(\"animals\", id)) return \"\"\n return composedOption(\"animals\", id)?.promptHint ?? getAnimalPromptHint(id)\n}\nexport function curatedAnimalTerm(id: string): string {\n if (!id) return \"\"\n if (!composedHas(\"animals\", id)) return \"\"\n return composedOption(\"animals\", id)?.term ?? getAnimalTerm(id)\n}\n\ntype ObjectEntity = { readonly label: string; readonly description: string }\n\nfunction objectEntityText(\n catalogId: string,\n id: string,\n stock: ObjectEntity | undefined,\n stockHint: (e: ObjectEntity) => string,\n stockTerm: (e: ObjectEntity) => string,\n): { readonly hint: string; readonly term: string } {\n if (!id || !composedHas(catalogId, id)) return { hint: \"\", term: \"\" }\n const composed = composedOption(catalogId, id)\n if (composed) return { hint: composed.promptHint, term: composed.term }\n if (!stock) return { hint: \"\", term: \"\" }\n return { hint: stockHint(stock), term: stockTerm(stock) }\n}\n\n/**\n * The compact fragment for an OBJECT-entity entry (animal / vehicle / weapon /\n * furniture). Those catalogs carry no `promptHint` of their own — the full\n * fragment is synthesized as \"featuring a {label}, {description}\" — so the\n * compact form is the authored `term` when there is one and the derived label\n * otherwise (a concrete object's label IS its trade term). The framing verb\n * (\"featuring a\", \"with a\") belongs to the HINT; a term drops bare into\n * whatever sentence the consumer is building, and \"the object is in the scene\"\n * is precisely what these four nodes mean.\n *\n * The fallback MUST be `deriveTerm` and not a bare `toLowerCase()`: it is the\n * same fallback `objectOptions` uses to build the `/v1/catalogs` projection,\n * so a parenthetical label (\"Rifle (bolt-action)\") must strip identically here\n * or the injected fragment and the projected term would disagree.\n *\n * `term` is read structurally because it is being added to the shared entity\n * interfaces separately; this stays correct before and after that lands.\n */\n\nfunction objectEntityTerm(entry: { readonly label: string }): string {\n return (entry as { term?: string }).term ?? deriveTerm(entry.label)\n}\n\nexport function curatedVehicleText(id: string) {\n return objectEntityText(\"vehicles\", id, getVehicle(id), (e) => `featuring a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm)\n}\nexport function curatedWeaponText(id: string) {\n return objectEntityText(\"weapons\", id, getWeapon(id), (e) => `with a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm)\n}\nexport function curatedFurnitureText(id: string) {\n return objectEntityText(\"furniture\", id, getFurniture(id), (e) => `including a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm)\n}\n"]}
|
package/dist/chunk-W5SCZIOB.cjs
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkMQEXFCNU_cjs = require('./chunk-MQEXFCNU.cjs');
|
|
4
|
-
var chunkFY4AMIDB_cjs = require('./chunk-FY4AMIDB.cjs');
|
|
5
|
-
var catalogs = require('@nodaro/shared/catalogs');
|
|
6
|
-
|
|
7
|
-
function curatedAnimalPromptHint(id) {
|
|
8
|
-
if (!id) return "";
|
|
9
|
-
if (!chunkMQEXFCNU_cjs.composedHas("animals", id)) return "";
|
|
10
|
-
return chunkMQEXFCNU_cjs.composedOption("animals", id)?.promptHint ?? catalogs.getAnimalPromptHint(id);
|
|
11
|
-
}
|
|
12
|
-
function curatedAnimalTerm(id) {
|
|
13
|
-
if (!id) return "";
|
|
14
|
-
if (!chunkMQEXFCNU_cjs.composedHas("animals", id)) return "";
|
|
15
|
-
return chunkMQEXFCNU_cjs.composedOption("animals", id)?.term ?? catalogs.getAnimalTerm(id);
|
|
16
|
-
}
|
|
17
|
-
function objectEntityText(catalogId, id, stock, stockHint, stockTerm) {
|
|
18
|
-
if (!id || !chunkMQEXFCNU_cjs.composedHas(catalogId, id)) return { hint: "", term: "" };
|
|
19
|
-
const composed = chunkMQEXFCNU_cjs.composedOption(catalogId, id);
|
|
20
|
-
if (composed) return { hint: composed.promptHint, term: composed.term };
|
|
21
|
-
if (!stock) return { hint: "", term: "" };
|
|
22
|
-
return { hint: stockHint(stock), term: stockTerm(stock) };
|
|
23
|
-
}
|
|
24
|
-
function objectEntityTerm(entry) {
|
|
25
|
-
return entry.term ?? chunkFY4AMIDB_cjs.deriveTerm(entry.label);
|
|
26
|
-
}
|
|
27
|
-
function curatedVehicleText(id) {
|
|
28
|
-
return objectEntityText("vehicles", id, catalogs.getVehicle(id), (e) => `featuring a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
|
|
29
|
-
}
|
|
30
|
-
function curatedWeaponText(id) {
|
|
31
|
-
return objectEntityText("weapons", id, catalogs.getWeapon(id), (e) => `with a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
|
|
32
|
-
}
|
|
33
|
-
function curatedFurnitureText(id) {
|
|
34
|
-
return objectEntityText("furniture", id, catalogs.getFurniture(id), (e) => `including a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
exports.curatedAnimalPromptHint = curatedAnimalPromptHint;
|
|
38
|
-
exports.curatedAnimalTerm = curatedAnimalTerm;
|
|
39
|
-
exports.curatedFurnitureText = curatedFurnitureText;
|
|
40
|
-
exports.curatedVehicleText = curatedVehicleText;
|
|
41
|
-
exports.curatedWeaponText = curatedWeaponText;
|
|
42
|
-
//# sourceMappingURL=chunk-W5SCZIOB.cjs.map
|
|
43
|
-
//# sourceMappingURL=chunk-W5SCZIOB.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared-catalog-overlay.ts"],"names":["composedHas","composedOption","getAnimalPromptHint","getAnimalTerm","deriveTerm","getVehicle","getWeapon","getFurniture"],"mappings":";;;;;;AAwBO,SAAS,wBAAwB,EAAA,EAAoB;AAC1D,EAAA,IAAI,CAAC,IAAI,OAAO,EAAA;AAChB,EAAA,IAAI,CAACA,6BAAA,CAAY,SAAA,EAAW,EAAE,GAAG,OAAO,EAAA;AACxC,EAAA,OAAOC,iCAAe,SAAA,EAAW,EAAE,CAAA,EAAG,UAAA,IAAcC,6BAAoB,EAAE,CAAA;AAC5E;AACO,SAAS,kBAAkB,EAAA,EAAoB;AACpD,EAAA,IAAI,CAAC,IAAI,OAAO,EAAA;AAChB,EAAA,IAAI,CAACF,6BAAA,CAAY,SAAA,EAAW,EAAE,GAAG,OAAO,EAAA;AACxC,EAAA,OAAOC,iCAAe,SAAA,EAAW,EAAE,CAAA,EAAG,IAAA,IAAQE,uBAAc,EAAE,CAAA;AAChE;AAIA,SAAS,gBAAA,CACP,SAAA,EACA,EAAA,EACA,KAAA,EACA,WACA,SAAA,EACkD;AAClD,EAAA,IAAI,CAAC,EAAA,IAAM,CAACH,6BAAA,CAAY,SAAA,EAAW,EAAE,CAAA,EAAG,OAAO,EAAE,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,EAAA,EAAG;AACpE,EAAA,MAAM,QAAA,GAAWC,gCAAA,CAAe,SAAA,EAAW,EAAE,CAAA;AAC7C,EAAA,IAAI,QAAA,SAAiB,EAAE,IAAA,EAAM,SAAS,UAAA,EAAY,IAAA,EAAM,SAAS,IAAA,EAAK;AACtE,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAE,IAAA,EAAM,EAAA,EAAI,MAAM,EAAA,EAAG;AACxC,EAAA,OAAO,EAAE,MAAM,SAAA,CAAU,KAAK,GAAG,IAAA,EAAM,SAAA,CAAU,KAAK,CAAA,EAAE;AAC1D;AAqBA,SAAS,iBAAiB,KAAA,EAA2C;AACnE,EAAA,OAAQ,KAAA,CAA4B,IAAA,IAAQG,4BAAA,CAAW,KAAA,CAAM,KAAK,CAAA;AACpE;AAEO,SAAS,mBAAmB,EAAA,EAAY;AAC7C,EAAA,OAAO,iBAAiB,UAAA,EAAY,EAAA,EAAIC,mBAAA,CAAW,EAAE,GAAG,CAAC,CAAA,KAAM,CAAA,YAAA,EAAe,CAAA,CAAE,MAAM,WAAA,EAAa,KAAK,CAAA,CAAE,WAAW,IAAI,gBAAgB,CAAA;AAC3I;AACO,SAAS,kBAAkB,EAAA,EAAY;AAC5C,EAAA,OAAO,iBAAiB,SAAA,EAAW,EAAA,EAAIC,kBAAA,CAAU,EAAE,GAAG,CAAC,CAAA,KAAM,CAAA,OAAA,EAAU,CAAA,CAAE,MAAM,WAAA,EAAa,KAAK,CAAA,CAAE,WAAW,IAAI,gBAAgB,CAAA;AACpI;AACO,SAAS,qBAAqB,EAAA,EAAY;AAC/C,EAAA,OAAO,iBAAiB,WAAA,EAAa,EAAA,EAAIC,qBAAA,CAAa,EAAE,GAAG,CAAC,CAAA,KAAM,CAAA,YAAA,EAAe,CAAA,CAAE,MAAM,WAAA,EAAa,KAAK,CAAA,CAAE,WAAW,IAAI,gBAAgB,CAAA;AAC9I","file":"chunk-W5SCZIOB.cjs","sourcesContent":["import { getAnimalPromptHint, getAnimalTerm, getVehicle, getWeapon, getFurniture } from \"@nodaro/shared/catalogs\"\nimport { composedHas, composedOption } from \"./catalog-overlay.js\"\nimport { deriveTerm } from \"./term.js\"\n\n/**\n * The four object-entity catalogs — animals, vehicles, weapons, furniture —\n * are OWNED by `@nodaro/shared`, which cannot depend on this package, so\n * their getters cannot carry the `overlayEntry` the other 34 catalogs do.\n * These wrappers are the overlay for them, and EVERY prompt-text read of\n * those catalogs goes through here: the parameter-node dispatcher and the\n * subject fold both. Two call sites resolving the same id to different text\n * — one curated, one stock — is exactly what a second, un-overlaid path\n * produced during review.\n *\n * Precedence per id, same four outcomes as catalog-overlay.ts:\n * - no pack on the catalog → the stock getter, untouched\n * - denied / not in the pack → \"\" (nothing reaches the prompt)\n * - composed option present → ITS promptHint / term. A pack author\n * who rewrote an entry wrote the hint they want injected; rebuilding it\n * from label + description would discard that\n * - pack-added id (no stock row) → the composed option (covered by the\n * branch above — the stock getter is never consulted when a pack exists)\n */\n\nexport function curatedAnimalPromptHint(id: string): string {\n if (!id) return \"\"\n if (!composedHas(\"animals\", id)) return \"\"\n return composedOption(\"animals\", id)?.promptHint ?? getAnimalPromptHint(id)\n}\nexport function curatedAnimalTerm(id: string): string {\n if (!id) return \"\"\n if (!composedHas(\"animals\", id)) return \"\"\n return composedOption(\"animals\", id)?.term ?? getAnimalTerm(id)\n}\n\ntype ObjectEntity = { readonly label: string; readonly description: string }\n\nfunction objectEntityText(\n catalogId: string,\n id: string,\n stock: ObjectEntity | undefined,\n stockHint: (e: ObjectEntity) => string,\n stockTerm: (e: ObjectEntity) => string,\n): { readonly hint: string; readonly term: string } {\n if (!id || !composedHas(catalogId, id)) return { hint: \"\", term: \"\" }\n const composed = composedOption(catalogId, id)\n if (composed) return { hint: composed.promptHint, term: composed.term }\n if (!stock) return { hint: \"\", term: \"\" }\n return { hint: stockHint(stock), term: stockTerm(stock) }\n}\n\n/**\n * The compact fragment for an OBJECT-entity entry (animal / vehicle / weapon /\n * furniture). Those catalogs carry no `promptHint` of their own — the full\n * fragment is synthesized as \"featuring a {label}, {description}\" — so the\n * compact form is the authored `term` when there is one and the derived label\n * otherwise (a concrete object's label IS its trade term). The framing verb\n * (\"featuring a\", \"with a\") belongs to the HINT; a term drops bare into\n * whatever sentence the consumer is building, and \"the object is in the scene\"\n * is precisely what these four nodes mean.\n *\n * The fallback MUST be `deriveTerm` and not a bare `toLowerCase()`: it is the\n * same fallback `objectOptions` uses to build the `/v1/catalogs` projection,\n * so a parenthetical label (\"Rifle (bolt-action)\") must strip identically here\n * or the injected fragment and the projected term would disagree.\n *\n * `term` is read structurally because it is being added to the shared entity\n * interfaces separately; this stays correct before and after that lands.\n */\n\nfunction objectEntityTerm(entry: { readonly label: string }): string {\n return (entry as { term?: string }).term ?? deriveTerm(entry.label)\n}\n\nexport function curatedVehicleText(id: string) {\n return objectEntityText(\"vehicles\", id, getVehicle(id), (e) => `featuring a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm)\n}\nexport function curatedWeaponText(id: string) {\n return objectEntityText(\"weapons\", id, getWeapon(id), (e) => `with a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm)\n}\nexport function curatedFurnitureText(id: string) {\n return objectEntityText(\"furniture\", id, getFurniture(id), (e) => `including a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm)\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resolve-prompt.ts","../src/audio-tags.ts","../src/node-prompt-fields.ts","../src/node-send-text.ts"],"names":["resolveNodeRefs","joinSentences","SOCIAL_POST_NODE_TYPES","readPromptAffixes","present"],"mappings":"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","file":"chunk-ZFIXQRBZ.cjs","sourcesContent":["import { resolveNodeRefs } from \"@nodaro/shared/workflow\"\nimport { readPromptAffixes, type PromptAffixes } from \"@nodaro/shared/nodes\"\nimport { SOCIAL_POST_NODE_TYPES } from \"@nodaro/shared/social\"\nimport { joinSentences } from \"./hint-join.js\"\n\nexport interface ResolvePromptArgs {\n override?: string\n typed?: ReadonlyArray<string | undefined>\n wired?: string\n refMap: ReadonlyMap<string, string>\n /** Opt-in (generate-image / generate-video only): APPEND the wired\n * (connected-prompt) value to the TYPED base instead of treating wired as a\n * fallback, so a connected prompt auto-injects alongside the typed prompt. An\n * `override` (list fan-out item) still fully replaces — no wired append.\n * Off/undefined = exact legacy precedence (override > typed > wired) for every\n * other node type. */\n appendWired?: boolean\n /** Pre/post text wrapped around WHICHEVER core precedence produced (override,\n * typed, wired, or the appendWired combination). Applied last. */\n affixes?: PromptAffixes\n}\nconst present = (s?: string): s is string => typeof s === \"string\" && s.trim().length > 0\nconst rr = (s: string, m: ReadonlyMap<string, string>) => (m.size > 0 ? resolveNodeRefs(s, m) : s)\n\n// ---------------------------------------------------------------------------\n// Prompt affixes (pre/post text) — see docs/prompt-pre-post-text.md (join rule, empty-core, no-op guarantee)\n// ---------------------------------------------------------------------------\n\nconst GLUE_PUNCTUATION = new Set([\",\", \".\", \";\", \":\", \"!\", \"?\", \")\"])\n\n/** The ONE join rule between two non-blank prompt parts: a single space unless\n * the boundary already has whitespace on either side or the right part opens\n * with sentence punctuation. Shared by `joinPromptParts` and the editor's\n * Final-view segment builder so preview and run agree byte-for-byte. */\nexport function promptPartSeparator(left: string, right: string): \"\" | \" \" {\n if (left.length === 0 || right.length === 0) return \"\"\n if (/\\s$/.test(left) || /^\\s/.test(right)) return \"\"\n if (GLUE_PUNCTUATION.has(right[0])) return \"\"\n return \" \"\n}\n\n/** Join prompt parts with `promptPartSeparator`; blank parts are dropped,\n * non-blank parts are used verbatim (no trimming). */\nexport function joinPromptParts(parts: ReadonlyArray<string | undefined>): string {\n let out = \"\"\n for (const part of parts) {\n if (!present(part)) continue\n out = out.length === 0 ? part : out + promptPartSeparator(out, part) + part\n }\n return out\n}\n\n/**\n * Wrap `core` with the node's pre/post text. `{Label}` refs are resolved in the\n * AFFIXES only — the core arrives exactly as the caller's precedence produced it.\n * NO-OP GUARANTEE: with no non-blank affix the core is returned unchanged (same\n * reference) — every existing workflow stays byte-identical.\n * EMPTY CORE: a blank core with affixes yields the joined affixes alone.\n */\nexport function applyPromptAffixes(core: string, affixes: PromptAffixes | undefined, refMap: ReadonlyMap<string, string>): string\nexport function applyPromptAffixes(core: string | undefined, affixes: PromptAffixes | undefined, refMap: ReadonlyMap<string, string>): string | undefined\nexport function applyPromptAffixes(\n core: string | undefined,\n affixes: PromptAffixes | undefined,\n refMap: ReadonlyMap<string, string>,\n): string | undefined {\n const prefix = present(affixes?.prefix) ? rr(affixes!.prefix!, refMap) : undefined\n const suffix = present(affixes?.suffix) ? rr(affixes!.suffix!, refMap) : undefined\n if (prefix === undefined && suffix === undefined) return core\n return joinPromptParts([prefix, core, suffix])\n}\n\n/** SINGLE SOURCE OF TRUTH for prompt precedence across both DAG engines:\n * override (list fan-out) > first present typed candidate > wired > \"\".\n * \"present\" = non-empty after trim. {Label} refs are resolved on the chosen\n * branch via the shared resolveNodeRefs. With `appendWired`, the chosen base\n * AND the wired value are both emitted, joined as sentences (`joinSentences`:\n * \". \", or a space after one that already ends). Affixes wrap the result. */\nexport function resolvePrompt({ override, typed = [], wired, refMap, appendWired, affixes }: ResolvePromptArgs): string {\n return applyPromptAffixes(resolveCore({ override, typed, wired, refMap, appendWired }), affixes, refMap)\n}\n\nfunction resolveCore({ override, typed = [], wired, refMap, appendWired }: Omit<ResolvePromptArgs, \"affixes\">): string {\n // appendWired: a connected prompt APPENDS to the TYPED base. An `override`\n // (list fan-out item) still fully REPLACES — it never receives a wired append,\n // so per-item fan-out prompts are unchanged.\n if (appendWired && !present(override)) {\n const base = typed.find(present)\n return joinSentences([base, wired].filter(present).map((s) => rr(s, refMap)))\n }\n if (present(override)) return rr(override, refMap)\n for (const t of typed) if (present(t)) return rr(t, refMap)\n if (present(wired)) return rr(wired, refMap)\n return \"\"\n}\n\n/** Compose a final NEGATIVE prompt from a TYPED base + a WIRED (connected\n * negative-handle) value: both are emitted, joined as sentences (`joinSentences`, mirrors `appendWired`\n * for the positive prompt). Pure join — the caller resolves `{label}` refs on\n * the typed value first, and the wired value is already a resolved output that\n * the input-resolver has filtered (referenced / Inject-Negative-off dropped).\n * Generate-image / generate-video only; empty parts are dropped. */\nexport function composeNegative(typed?: string, wired?: string): string {\n return joinSentences([typed, wired])\n}\n\n/** Ordered typed-candidate fields per node type — the precedence source of\n * truth (NOT NODE_MAPPABLE_FIELDS, which is field-mapping eligibility, omits\n * video-retake, and orders llm-chat wrong). */\nexport const NODE_PROMPT_CANDIDATE_FIELDS: Readonly<Record<string, readonly string[]>> = {\n \"generate-image\": [\"prompt\"],\n // motionPrompt is here so generate-video re-typed to text-to-video keeps a\n // prompt stored in data.motionPrompt (the inline picker's legacy field). Safe\n // for a standalone text-to-video node — it never carries data.motionPrompt.\n \"text-to-video\": [\"prompt\", \"motionPrompt\"],\n \"video-to-video\": [\"prompt\"],\n \"generate-music\": [\"prompt\"],\n \"speech-to-video\": [\"prompt\"],\n \"cinematic-avatar\": [\"prompt\"],\n \"extend-video\": [\"prompt\"],\n \"video-retake\": [\"prompt\"],\n \"suno-replace-section\": [\"prompt\"],\n \"image-to-video\": [\"prompt\", \"motionPrompt\"],\n \"generate-video\": [\"prompt\", \"motionPrompt\"],\n \"text-to-audio\": [\"prompt\", \"text\"],\n // Social posts: the orchestrator + frontend executor match per-platform node\n // types (node.type is \"instagram-post\", \"telegram-post\", …), NOT a unified\n // \"social-publish\" type — so every platform needs its own [\"caption\"] entry.\n // Derived from the shared SOCIAL_POST_NODE_TYPES single source of truth so a\n // new platform can't drift out of the precedence map.\n ...Object.fromEntries(\n [...SOCIAL_POST_NODE_TYPES].map((t) => [t, [\"caption\"] as readonly string[]]),\n ),\n // Kept for any caller that passes the aggregate type instead of a platform.\n \"social-publish\": [\"caption\"],\n}\n\nexport interface ComputeNodePromptArgs {\n override?: string\n wired?: string\n refMap: ReadonlyMap<string, string>\n /** See ResolvePromptArgs.appendWired — generate-image / generate-video only. */\n appendWired?: boolean\n}\n/** Resolve a single-prompt node's final prompt (typed-primary). Both engines\n * call this so field-selection + precedence are structurally identical. */\nexport function computeNodePrompt(\n nodeType: string,\n data: Record<string, unknown>,\n { override, wired, refMap, appendWired }: ComputeNodePromptArgs,\n): string {\n let typed: ReadonlyArray<string | undefined>\n if (nodeType === \"text-to-speech\") {\n // data.text is a phantom field on TTS; only directText (gated) is real.\n //\n // The gate is a PREFERENCE, not a lock: when textSource is \"connected\" we\n // still fall back to typed text if nothing is wired. Writers flip the gate\n // (PromptFieldSpec.promptGate), but data reaches nodes from places no\n // writer touches — workflows saved before that fix, JSON imports, MCP\n // writes, templates — and there the text sat visible in the node while the\n // run failed with \"no text found\" (founder, 2026-08-14). Coerce rather\n // than reject, same principle as normalizeModelInput.\n typed =\n data.textSource === \"direct\" || !present(wired)\n ? [data.directText as string | undefined]\n : []\n } else {\n const fields = NODE_PROMPT_CANDIDATE_FIELDS[nodeType] ?? [\"prompt\"]\n typed = fields.map((f) => data[f] as string | undefined)\n }\n return resolvePrompt({ override, typed, wired, refMap, appendWired, affixes: readPromptAffixes(data) })\n}\n\nexport interface LlmChatFieldArgs {\n override?: string\n wiredUserInput?: string\n wiredSystemPrompt?: string\n refMap: ReadonlyMap<string, string>\n}\n/** llm-chat resolves TWO independent fields. override applies to userInput only. */\nexport function computeLlmChatFields(\n data: Record<string, unknown>,\n { override, wiredUserInput, wiredSystemPrompt, refMap }: LlmChatFieldArgs,\n): { userInput: string; systemPrompt: string } {\n return {\n userInput: resolvePrompt({ override, typed: [data.userInput as string | undefined], wired: wiredUserInput, refMap, affixes: readPromptAffixes(data) }),\n systemPrompt: resolvePrompt({ typed: [data.systemPrompt as string | undefined], wired: wiredSystemPrompt, refMap }),\n }\n}\n\n/** generate-script's topic. Both engines call this, so a wired Text node reads\n * the same on a single-node run and a server run.\n *\n * Precedence is override (a list fan-out item) > wired > `data.prompt`. Wired\n * comes BEFORE the typed field, the reverse of `computeNodePrompt`, because\n * `data.prompt` has no editor: the panel's text field is the style guide. A\n * value there was written by the old connect-time `{Label}` auto-fill or by an\n * agent, and it is kept only as the fallback that saved workflows rely on. */\nexport function computeScriptTopic(\n data: Record<string, unknown>,\n { override, wired, refMap }: Omit<ComputeNodePromptArgs, \"appendWired\">,\n): string {\n const core = [override, wired, data.prompt as string | undefined].find(present)\n return applyPromptAffixes(core === undefined ? \"\" : rr(core, refMap), readPromptAffixes(data), refMap)\n}\n","/**\n * Remove inline `[audio tags]` from speech text, for a model that does not\n * perform them (it would read them aloud). Pure, dependency-free: the\n * backend's credit counter (lib/speech-credits.ts), its payload builder and\n * the editor's estimate (frontend/src/lib/speech-estimate.ts) all read it, so\n * the two engines count exactly the same characters.\n *\n * A tag is `[`, one or more characters that are not `]`, then `]` — what\n * `/\\[[^\\]]+\\]/g` matches. That pattern is quadratic on an unclosed run of `[`\n * (every `[` rescans to the end of the text), and the credit counter runs this\n * on raw request text on the API and orchestrator event loops, so the scan is\n * written by hand: one `indexOf` per `[`, and when no `]` is left no later `[`\n * can match either, so the rest of the text is kept in one slice.\n */\nfunction removeBracketTags(text: string): string {\n let out = \"\"\n let from = 0\n let open = text.indexOf(\"[\")\n while (open !== -1) {\n const close = text.indexOf(\"]\", open + 1)\n if (close === -1) break\n if (close === open + 1) {\n // \"[]\" is not a tag (needs 1+ characters); the \"[\" stays and the scan moves on.\n open = text.indexOf(\"[\", open + 1)\n continue\n }\n out += text.slice(from, open)\n from = close + 1\n open = text.indexOf(\"[\", from)\n }\n return out + text.slice(from)\n}\n\nexport function stripAudioTags(text: string): string {\n return removeBracketTags(text).replace(/\\s{2,}/g, \" \").trim()\n}\n","/**\n * Single source of truth: which data field(s) hold a node's user-editable\n * prompt text. Drives the quick-edit Prompt modal so it can work generically\n * across every AI node without each call site hardcoding a field name.\n *\n * The registry LIVES HERE (`@nodaro/prompts`) so the backend orchestrator, the\n * `/v1/nodes` registry, the prompt-affix totality tests and the docs tooling\n * read the SAME list the editor does. `frontend/src/lib/prompt-fields.ts` is a\n * pure re-export of this module — frontend code keeps importing\n * `@/lib/prompt-fields` unchanged.\n *\n * Most nodes store the prompt in `data.prompt`, but several don't\n * (`text-prompt` → `text`, `image-to-text` → `customPrompt`, …) — this map is\n * where that knowledge lives, once.\n *\n * INVARIANT: every node that exposes a user-editable prompt MUST have an entry\n * here, or the quick-edit modal silently does nothing for it. A guard test\n * (`prompt-fields.test.ts`) keeps this in sync with the node set.\n *\n * This module is intentionally pure data (no React/lucide imports) so it can be\n * pulled into the app-runtime bundle and any test without dragging in the icon\n * library. The string `icon` kind is mapped to a concrete lucide component in\n * `prompt-edit-button.tsx` (`getPromptIcon`), the only place that renders it.\n */\n\nimport type { SnippetMedia } from \"./factory-snippets/types.js\"\nimport { NODE_PROMPT_CANDIDATE_FIELDS } from \"./resolve-prompt.js\"\n\n/** Which lucide glyph a node's prompt affordance uses. Kept as a string so this\n * module stays icon-library-free; mapped to a component in the strip button. */\nexport type PromptIconKind = \"pencil\" | \"paintbrush\"\n\nexport interface PromptFieldSpec {\n /** Data key holding the primary prompt (e.g. \"prompt\", \"text\", \"customPrompt\"). */\n readonly prompt: string\n /** Data key holding the negative prompt, when the node supports one. */\n readonly negative?: string\n /** Label override for the primary field (defaults to \"Prompt\"). */\n readonly promptLabel?: string\n /** Icon for the prompt affordance (strip button + modal title). Defaults to a\n * pencil; image-editing nodes use a paintbrush to read as \"edit\". */\n readonly icon?: PromptIconKind\n /** Node modality for prompt-snippet scoping — drives which snippet pool the\n * \"/\" menu and Snippets button show for this node's prompt fields. REQUIRED\n * so a new node cannot forget to declare it (compile error). */\n readonly media: SnippetMedia\n /** True when this node renders a media-result preview body and therefore\n * participates in inline-prompt mode (the `InlineNodePrompt` editor on the\n * node face, centralized in `BaseNode`). REQUIRED (like `media`) so a new\n * prompt node must consciously choose — compile error if omitted. The set of\n * `inline: true` types is guarded in `prompt-fields.test.ts`. */\n readonly inline: boolean\n /** Some nodes only READ their prompt field when a sibling discriminator\n * selects it — TTS keeps `directText` but resolves it only while\n * `textSource === \"direct\"`, defaulting to `\"connected\"`. Writing the text\n * without flipping the discriminator saved the value and then failed with\n * \"no text found\" (founder hit it live, 2026-08-14). Declare the pair here\n * and every writer flips it automatically. */\n readonly promptGate?: { readonly field: string; readonly value: string }\n /** Prompt pre/post text (`promptPrefix` / `promptSuffix`) support. Defaults to\n * ON for every prompt node; set `false` ONLY for a node whose \"prompt\" is not\n * a model prompt (the plain Text input node). Read via `nodeSupportsPromptAffixes`. */\n readonly affixes?: false\n}\n\nexport const NODE_PROMPT_FIELDS: Readonly<Record<string, PromptFieldSpec>> = {\n // ── Image ──\n // (`edit-image` / `image-to-image` are legacy types consolidated into\n // `modify-image`; they're not in the creatable node set, so a node of that\n // type never mounts and needs no entry here. The guard test enforces that.)\n \"generate-image\": { prompt: \"prompt\", negative: \"negativePrompt\", media: \"image\", inline: true },\n \"modify-image\": { prompt: \"prompt\", negative: \"negativePrompt\", icon: \"paintbrush\", media: \"image\", inline: true },\n \"generate-mask\": { prompt: \"prompt\", promptLabel: \"What to mask\", media: \"image\", inline: true },\n // ── Video ──\n \"generate-video\": { prompt: \"prompt\", negative: \"negativePrompt\", media: \"video\", inline: true },\n // Trimmed multi-segment stitch sibling of generate-video — no negativePrompt field.\n \"generate-video-pro\": { prompt: \"prompt\", media: \"video\", inline: true },\n // Span-replace sibling of generate-video-pro — no negativePrompt field.\n \"edit-video-pro\": { prompt: \"prompt\", media: \"video\", inline: true },\n \"text-to-video\": { prompt: \"prompt\", negative: \"negativePrompt\", media: \"video\", inline: true },\n \"image-to-video\": { prompt: \"prompt\", negative: \"negativePrompt\", promptLabel: \"Motion prompt\", media: \"video\", inline: true },\n \"video-to-video\": { prompt: \"prompt\", negative: \"negativePrompt\", media: \"video\", inline: true },\n \"switchx\": { prompt: \"prompt\", promptLabel: \"Look prompt\", media: \"video\", inline: true },\n \"extend-video\": { prompt: \"prompt\", negative: \"negativePrompt\", media: \"video\", inline: true },\n \"speech-to-video\": { prompt: \"prompt\", negative: \"negativePrompt\", media: \"video\", inline: true },\n \"motion-transfer\": { prompt: \"prompt\", negative: \"negativePrompt\", media: \"video\", inline: true },\n // Cinematic Avatar (HeyGen) — generative prompt (NOT a verbatim script),\n // so it participates in the quick-edit Prompt modal like other AI video nodes.\n \"cinematic-avatar\": { prompt: \"prompt\", media: \"video\", inline: true },\n \"video-sfx\": { prompt: \"prompt\", negative: \"negativePrompt\", promptLabel: \"Sound prompt\", media: \"audio\", inline: true },\n \"video-retake\": { prompt: \"prompt\", promptLabel: \"Retake prompt\", media: \"video\", inline: true },\n // Video Mask (SAM 3) — the subject to track, like Generate Mask's \"what to mask\".\n \"video-mask\": { prompt: \"prompt\", promptLabel: \"What to mask\", media: \"video\", inline: true },\n // ── Audio / music ──\n \"generate-music\": { prompt: \"prompt\", media: \"audio\", inline: true },\n \"suno-generate\": { prompt: \"prompt\", media: \"audio\", inline: true },\n \"text-to-audio\": { prompt: \"prompt\", media: \"audio\", inline: true },\n // ── Text / LLM (no media-result preview body → no inline editor) ──\n \"text-prompt\": { prompt: \"text\", promptLabel: \"Text\", media: \"text\", inline: false, affixes: false },\n \"image-to-text\": { prompt: \"customPrompt\", promptLabel: \"Question\", media: \"text\", inline: false },\n \"llm-chat\": { prompt: \"userInput\", promptLabel: \"Prompt\", media: \"text\", inline: false },\n // ── Speech / voice ──\n \"text-to-speech\": { prompt: \"directText\", promptLabel: \"Text\", media: \"audio\", inline: true, promptGate: { field: \"textSource\", value: \"direct\" } },\n \"voice-design\": { prompt: \"voiceDescription\", promptLabel: \"Voice description\", media: \"audio\", inline: true },\n \"voice-remix\": { prompt: \"voiceDescription\", promptLabel: \"Voice description\", media: \"audio\", inline: true },\n \"lip-sync\": { prompt: \"prompt\", media: \"audio\", inline: true },\n // ── Suno (music) ──\n \"suno-cover\": { prompt: \"prompt\", media: \"audio\", inline: true },\n \"suno-extend\": { prompt: \"prompt\", media: \"audio\", inline: true },\n \"suno-replace-section\": { prompt: \"prompt\", media: \"audio\", inline: true },\n \"suno-upload-extend\": { prompt: \"prompt\", media: \"audio\", inline: true },\n // NOTE: `suno-add-vocals` is intentionally absent — its node has no\n // user-editable prompt (SunoAddVocalsData declares only `model`; the config\n // panel + backend route take taskId/audioId/model, never a prompt). A stale\n // entry here rendered a phantom Prompt editor in the quick-edit modal that\n // wrote to a `data.prompt` key nothing ever reads (the dead-field class the\n // guard test now catches via defaultData ownership).\n \"suno-lyrics\": { prompt: \"prompt\", media: \"audio\", inline: false },\n \"suno-style-boost\": { prompt: \"content\", promptLabel: \"Style\", media: \"audio\", inline: false },\n // ── Composition / FX (compact, no media-result preview → no inline editor) ──\n \"image-critic\": { prompt: \"prompt\", promptLabel: \"Criteria\", media: \"image\", inline: false },\n \"motion-graphics\": { prompt: \"motionPrompt\", promptLabel: \"Motion prompt\", media: \"video\", inline: false },\n \"pro-3d-render\": { prompt: \"scenePrompt\", promptLabel: \"Scene\", media: \"text\", inline: false },\n \"generate-3d-scene\": { prompt: \"scenePrompt\", promptLabel: \"Scene\", media: \"text\", inline: false },\n \"edit-3d-scene\": { prompt: \"editPrompt\", promptLabel: \"Edit instruction\", media: \"text\", inline: false },\n \"3d-title\": { prompt: \"titlePrompt\", promptLabel: \"Title\", media: \"text\", inline: false },\n // ── Script / alignment (their primary text field) ──\n \"generate-script\": { prompt: \"styleGuide\", promptLabel: \"Style guide\", media: \"text\", inline: false },\n \"forced-alignment\": { prompt: \"transcript\", promptLabel: \"Transcript\", media: \"audio\", inline: false },\n // Video Analysis — its focus hint is the editable prompt (renders a JSON scene\n // table, not a media preview → no inline editor).\n \"video-analysis\": { prompt: \"analysisFocus\", promptLabel: \"Analysis focus\", media: \"video\", inline: false },\n // Edit Plan (podcast editing) — its free-text editing instructions are the\n // editable prompt; the affixes wrap them. Emits an EDL (JSON), not a media\n // preview, so no inline editor.\n \"edit-plan\": { prompt: \"instructions\", promptLabel: \"Editing instructions\", media: \"video\", inline: false },\n}\n\n/** The prompt-field spec for a node type, or undefined if it has none. */\nexport function getPromptFields(nodeType: string | undefined): PromptFieldSpec | undefined {\n return nodeType ? NODE_PROMPT_FIELDS[nodeType] : undefined\n}\n\n/** True when this node type has a registered, quick-editable prompt field. */\nexport function nodeHasPromptField(nodeType: string | undefined): boolean {\n return getPromptFields(nodeType) !== undefined\n}\n\n/** Snippet modality for a node type (drives the snippet pool), or undefined\n * when the node has no prompt field. */\nexport function getSnippetMedia(nodeType: string | undefined): SnippetMedia | undefined {\n return getPromptFields(nodeType)?.media\n}\n\n/** True when this node type renders the inline on-node prompt editor — the\n * media-preview nodes (image/video/audio result body). Single source for\n * BaseNode's centralized `InlineNodePrompt` rendering and the gold nodes'\n * `showInline` derivation (`useInlinePromptActive`). */\nexport function nodeHasInlinePrompt(nodeType: string | undefined): boolean {\n return getPromptFields(nodeType)?.inline === true\n}\n\n/** True when the node type carries `promptPrefix` / `promptSuffix` — every\n * registered prompt node except explicit opt-outs (`affixes: false`). The single\n * predicate the config panel, Final view, `/v1/nodes` registry, totality tests\n * and docs tooling all gate on. */\nexport function nodeSupportsPromptAffixes(nodeType: string | undefined): boolean {\n const spec = getPromptFields(nodeType)\n return spec !== undefined && spec.affixes !== false\n}\n\n/**\n * Where the RUN-TIME prompt lives when it differs from the editor's prompt\n * field (spec §7). `generate-script`'s editable prompt is its `styleGuide`, but\n * the run wraps the topic `prompt` with the affixes — so previewing the style\n * guide must NOT show them.\n */\nexport const PROMPT_AFFIX_CORE_FIELD_OVERRIDES: Readonly<Record<string, string>> = { \"generate-script\": \"prompt\" }\n\n/** The data key whose value the run wraps with promptPrefix/promptSuffix, or\n * undefined when the node has no affixes. */\nexport function promptAffixCoreField(nodeType: string | undefined): string | undefined {\n if (!nodeSupportsPromptAffixes(nodeType)) return undefined\n return PROMPT_AFFIX_CORE_FIELD_OVERRIDES[nodeType!] ?? getPromptFields(nodeType)!.prompt\n}\n\n/**\n * True when a preview of `promptField` on this node must show the affixes —\n * i.e. that key is what the run actually wraps. THE gate for the editor's Final\n * view, so a sibling field (llm-chat's `systemPrompt`, generate-script's\n * `styleGuide`) never renders a wrap the run doesn't perform.\n *\n * Two ways a key qualifies:\n * - it IS the core field ({@link promptAffixCoreField}), or\n * - it is a run-time FALLBACK candidate for the type\n * ({@link NODE_PROMPT_CANDIDATE_FIELDS}) — `computeNodePrompt` picks the\n * first present candidate and wraps THAT, so i2v's legacy `motionPrompt`\n * genuinely receives the affixes when `data.prompt` is empty.\n *\n * The two rules never collide: no override type declares candidates (guarded in\n * `node-prompt-fields.test.ts`).\n *\n * `undefined` means \"the node's primary prompt field\" — the common call.\n */\nexport function promptFieldCarriesAffixes(nodeType: string | undefined, promptField: string | undefined): boolean {\n const core = promptAffixCoreField(nodeType)\n if (core === undefined) return false\n if (promptField === undefined) return getPromptFields(nodeType)!.prompt === core\n return promptField === core || (NODE_PROMPT_CANDIDATE_FIELDS[nodeType!] ?? []).includes(promptField)\n}\n\n/** The affix-capable node types, derived from the registry (never hand-listed). */\nexport const PROMPT_AFFIX_NODE_TYPES: ReadonlySet<string> = new Set(\n Object.entries(NODE_PROMPT_FIELDS).filter(([, s]) => s.affixes !== false).map(([t]) => t),\n)\n","import { resolveNodeRefs } from \"@nodaro/shared/workflow\"\nimport { SOCIAL_POST_NODE_TYPES } from \"@nodaro/shared/social\"\nimport { computeLlmChatFields, computeNodePrompt, computeScriptTopic } from \"./resolve-prompt.js\"\n\n/**\n * Output nodes that send exactly what is WIRED into them — a webhook's\n * parameters, a social post's caption and media. When every wire produced\n * nothing in this run (a Filter List that kept no row, a node that was itself\n * skipped), there is nothing to send: the node is skipped with\n * `skipReason: \"empty_input\"` instead of posting an empty payload (a site that\n * validates its input answers 400 and alerts its owner) or failing the run\n * (a publish route refuses a post with no text and no media). A typed caption\n * with no wire at all is the author's choice and still runs, as before. ONE\n * list, read by the orchestrator's skip rule (`empty-input-skips.ts`).\n */\nexport const WIRED_OUTPUT_NODE_TYPES: ReadonlySet<string> = new Set([\"webhook-output\", ...SOCIAL_POST_NODE_TYPES])\n\n/**\n * Node types that send TEXT to a model or a voice and fail on an empty one —\n * `userInput: Too small` (llm-chat), \"no text found\" (text-to-speech) — so a\n * run with nothing upstream can skip them instead of failing. Never an image\n * or video node (an empty prompt is a legal request there) and never a social\n * post (an empty caption is the author's choice). ONE list, read by the\n * orchestrator's skip rule (`empty-input-skips.ts`); the engine never names a\n * type on its own.\n */\nexport const TEXT_REQUIRED_NODE_TYPES: ReadonlySet<string> = new Set([\n \"llm-chat\",\n \"ai-writer\",\n \"generate-script\",\n \"text-to-speech\",\n \"generate-music\",\n \"text-to-audio\",\n // Save to Collection refuses an empty record (\"nothing to save\"): a feed\n // with nothing new wired straight into it must skip it, not fail the run.\n \"collection-write\",\n // Generate Image: an empty prompt is a legal request in general (references\n // alone), but when the text it was WIRED produced nothing this run — the skip\n // rule's precondition — an empty prompt is a paid picture of nothing.\n \"generate-image\",\n])\n\n/**\n * The node's data without its prompt pre/post text: the skip decision is about\n * the CORE text. A prefix alone (\"Write a news item about:\") is not something\n * to send — with the affixes applied, an empty core read as non-empty and an\n * idle tick paid for a model call on the prefix.\n */\nconst withoutAffixes = (data: Record<string, unknown>): Record<string, unknown> => ({ ...data, promptPrefix: undefined, promptSuffix: undefined })\n\nexport interface NodeSendTextArgs {\n /** A list fan-out item (highest precedence). */\n override?: string\n /** The text wired into the node's main text input. */\n wired?: string\n /** llm-chat only: the text wired into `system-prompt`. */\n wiredSystemPrompt?: string\n refMap: ReadonlyMap<string, string>\n}\n\nconst present = (s?: string): s is string => typeof s === \"string\" && s.trim().length > 0\n\n/**\n * ai-writer's user input — the rule its sync-HTTP body applies: a fan-out\n * item, else the wired text, else the typed `userInput`, else the legacy\n * `prompt`. (ai-writer is renamed to llm-chat on load; saved graphs that\n * never reloaded still run it.)\n */\nexport function computeAiWriterInput(\n data: Record<string, unknown>,\n { override, wired }: Pick<NodeSendTextArgs, \"override\" | \"wired\">,\n): string {\n return [override, wired, data.userInput as string | undefined, data.prompt as string | undefined].find(present) ?? \"\"\n}\n\n/**\n * The text a text-requiring node would send, by the SAME rule its executor\n * uses (`computeLlmChatFields`, `computeScriptTopic`, `computeNodePrompt`,\n * `computeAiWriterInput`) — so \"would this node run on nothing?\" is answered\n * by the function that decides what it sends, not by a second reading of it.\n * Undefined for any other node type.\n */\nexport function computeNodeSendText(\n nodeType: string,\n data: Record<string, unknown>,\n args: NodeSendTextArgs,\n): string | undefined {\n const bare = withoutAffixes(data)\n switch (nodeType) {\n case \"llm-chat\":\n return computeLlmChatFields(bare, {\n override: args.override,\n wiredUserInput: args.wired,\n wiredSystemPrompt: args.wiredSystemPrompt,\n refMap: args.refMap,\n }).userInput\n case \"ai-writer\":\n return computeAiWriterInput(bare, args)\n case \"generate-script\":\n return computeScriptTopic(bare, { override: args.override, wired: args.wired, refMap: args.refMap })\n case \"generate-music\": {\n // A music node sings typed lyrics with no prompt; only both empty is nothing to send.\n const prompt = computeNodePrompt(nodeType, bare, { override: args.override, wired: args.wired, refMap: args.refMap })\n return present(prompt) ? prompt : present(data.lyrics as string | undefined) ? (data.lyrics as string) : \"\"\n }\n case \"text-to-speech\":\n case \"text-to-audio\":\n case \"generate-image\":\n return computeNodePrompt(nodeType, bare, { override: args.override, wired: args.wired, refMap: args.refMap })\n case \"collection-write\": {\n // The record's content: the item (a fan-out row, else what reached `in`),\n // else a title, text or link typed on the node. A picture alone is a\n // record too, but it never starves the node: only a wired TEXT input that\n // produced nothing can (the skip rule's precondition).\n // A typed field counts as the engines send it, its `{Node}` references\n // resolved: `{Feed || }` over a feed with nothing new is empty, so the\n // node skips instead of failing with an empty record (#1890).\n const typed = [data.title, data.text, data.link]\n .map((v) => (typeof v === \"string\" ? resolveNodeRefs(v, args.refMap) : v))\n .find((v): v is string => present(v as string | undefined))\n return [args.override, args.wired, typed].find(present) ?? \"\"\n }\n default:\n return undefined\n }\n}\n"]}
|