@nodaro/prompts 1.27.0 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +915 -603
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +251 -17
- package/dist/index.d.ts +251 -17
- package/dist/index.js +896 -604
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/fixtures/blended-cut-approved.json +712 -0
- package/src/__tests__/fixtures/parameter-hint-golden.json +33 -1
- package/src/__tests__/hint-join.test.ts +64 -1
- package/src/__tests__/motion-hint.test.ts +20 -0
- package/src/__tests__/parameter-registry-sync.test.ts +1 -0
- package/src/__tests__/person-mixed-heritage.test.ts +43 -0
- package/src/__tests__/resolve-prompt.test.ts +13 -1
- package/src/__tests__/transition-options.test.ts +223 -0
- package/src/__tests__/transition-styles.test.ts +202 -0
- package/src/__tests__/transition-timing-catalogs.test.ts +41 -0
- package/src/__tests__/transitions-blended-cut.test.ts +325 -0
- package/src/__tests__/transitions-instant.test.ts +9 -5
- package/src/__tests__/transitions-scope.test.ts +420 -4
- package/src/__tests__/video-own-motion.test.ts +26 -0
- package/src/hint-join.ts +33 -1
- package/src/index.ts +1 -0
- package/src/parameter-prompt-hint.ts +20 -3
- package/src/person.ts +24 -8
- package/src/picker-art/character-art-files.generated.ts +536 -536
- package/src/picker-catalogs.ts +83 -7
- package/src/prompt-builder.ts +2 -1
- package/src/prompt-hint-join.ts +8 -2
- package/src/prompt-style-section.ts +3 -5
- package/src/resolve-prompt.ts +22 -4
- package/src/transitions.ts +408 -52
- package/src/video-own-motion.ts +29 -0
package/src/picker-catalogs.ts
CHANGED
|
@@ -124,13 +124,41 @@ export interface PickerOption {
|
|
|
124
124
|
/**
|
|
125
125
|
* Transitions only: present (`true`) on a row whose mechanism is a CUT, so
|
|
126
126
|
* it takes no duration and no intensity — see `Transition.instant` /
|
|
127
|
-
* `isInstantTransition`. A consumer that only reads this
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* it only when the pack's own option says so, and
|
|
131
|
-
* duration" — the safe reading, since the composer then
|
|
127
|
+
* `isInstantTransition`. A consumer that only reads this catalog (Studio
|
|
128
|
+
* builds the transition levers from `getPickerCatalog("transition")`) hides
|
|
129
|
+
* those levers for such a row. Carried from the base catalog; a row a
|
|
130
|
+
* catalog pack ADDS has it only when the pack's own option says so, and
|
|
131
|
+
* absent means "has a duration" — the safe reading, since the composer then
|
|
132
|
+
* behaves as before. On this in-memory catalog only: the wire projection
|
|
133
|
+
* (`projectPickerCatalog`) does not carry it.
|
|
132
134
|
*/
|
|
133
135
|
readonly instant?: true
|
|
136
|
+
/**
|
|
137
|
+
* Transitions only: present (`true`) on a CUT that a `blendsCut` duration
|
|
138
|
+
* turns into a BLENDED CUT — see `Transition.blendable` /
|
|
139
|
+
* `isBlendableTransition`. Always beside `instant`. A consumer offers such a
|
|
140
|
+
* row's Duration as a Blend lever (hard cut, or one of the `blendsCut`
|
|
141
|
+
* steps of the catalog's `duration` dimension). Carried like `instant`, and
|
|
142
|
+
* like it on this in-memory catalog only.
|
|
143
|
+
*/
|
|
144
|
+
readonly blendable?: true
|
|
145
|
+
/**
|
|
146
|
+
* Transition `duration` dimension only: present (`true`) on the step that
|
|
147
|
+
* blends a `blendable` cut (`short`) — see `TransitionTimingOption.blendsCut`
|
|
148
|
+
* / `blendedCutClause`. On this in-memory catalog only.
|
|
149
|
+
*/
|
|
150
|
+
readonly blendsCut?: true
|
|
151
|
+
/**
|
|
152
|
+
* PER-OPTION PARAMETERS — extra node-data fields that apply only while THIS
|
|
153
|
+
* option is picked, each with its own rows (Transitions: a wipe's
|
|
154
|
+
* `wipeDirection`; see `Transition.options`). Same shape as the catalog's
|
|
155
|
+
* `dimensions`, but scoped to the row that declares them: a consumer shows
|
|
156
|
+
* the control while the row is picked and stores the chosen row id under
|
|
157
|
+
* `field` beside the pick. The `auto` row leads each list and means "send
|
|
158
|
+
* nothing"; a row's `promptHint` is the phrase it writes into the option's
|
|
159
|
+
* description, not a clause of its own. Absent on every other option.
|
|
160
|
+
*/
|
|
161
|
+
readonly params?: readonly PickerDimension[]
|
|
134
162
|
/** Only present if the source catalog entry already carries a data icon/emoji/thumbnail field. */
|
|
135
163
|
readonly icon?: string
|
|
136
164
|
}
|
|
@@ -186,6 +214,22 @@ interface BaseCatalogEntry {
|
|
|
186
214
|
readonly adultOnly?: true
|
|
187
215
|
/** Transitions: see `Transition.instant`. Propagated into the flattened option as `instant: true`. */
|
|
188
216
|
readonly instant?: boolean
|
|
217
|
+
/** Transitions: see `Transition.blendable`. Propagated into the flattened option as `blendable: true`. */
|
|
218
|
+
readonly blendable?: boolean
|
|
219
|
+
/** Transition durations: see `TransitionTimingOption.blendsCut`. Propagated as `blendsCut: true`. */
|
|
220
|
+
readonly blendsCut?: boolean
|
|
221
|
+
/** Transitions: see `Transition.options`. Propagated as the option's `params`. */
|
|
222
|
+
readonly options?: ReadonlyArray<{
|
|
223
|
+
readonly field: string
|
|
224
|
+
readonly label: string
|
|
225
|
+
readonly choices: ReadonlyArray<{
|
|
226
|
+
readonly id: string
|
|
227
|
+
readonly label: string
|
|
228
|
+
readonly description: string
|
|
229
|
+
readonly phrase: string
|
|
230
|
+
readonly term: string
|
|
231
|
+
}>
|
|
232
|
+
}>
|
|
189
233
|
}
|
|
190
234
|
|
|
191
235
|
/**
|
|
@@ -215,6 +259,21 @@ function toOptions<T extends BaseCatalogEntry>(
|
|
|
215
259
|
if (categoryField) opt.category = e[categoryField] as unknown as string
|
|
216
260
|
if (e.adultOnly) opt.adultOnly = true
|
|
217
261
|
if (e.instant) opt.instant = true
|
|
262
|
+
if (e.blendable) opt.blendable = true
|
|
263
|
+
if (e.blendsCut) opt.blendsCut = true
|
|
264
|
+
if (e.options?.length) {
|
|
265
|
+
opt.params = e.options.map((o) => ({
|
|
266
|
+
field: o.field,
|
|
267
|
+
label: o.label,
|
|
268
|
+
options: o.choices.map((c) => ({
|
|
269
|
+
id: c.id,
|
|
270
|
+
label: c.label,
|
|
271
|
+
description: c.description,
|
|
272
|
+
promptHint: c.phrase,
|
|
273
|
+
term: c.term,
|
|
274
|
+
})),
|
|
275
|
+
}))
|
|
276
|
+
}
|
|
218
277
|
return opt as PickerOption
|
|
219
278
|
})
|
|
220
279
|
}
|
|
@@ -975,8 +1034,25 @@ export type ProjectedPickerCatalog = ProjectedCatalog
|
|
|
975
1034
|
function projectOption(o: PickerOption, detail: PickerCatalogDetail, imageUrl?: string): ProjectedPickerOption {
|
|
976
1035
|
// `term` rides at BOTH detail levels: a thin client renders `label` and
|
|
977
1036
|
// injects `term`, so compact must carry it — it is what makes compact hint
|
|
978
|
-
// mode possible without a second round-trip for the full payload.
|
|
979
|
-
|
|
1037
|
+
// mode possible without a second round-trip for the full payload.
|
|
1038
|
+
const projected = projectOptionFields(o, detail, imageUrl)
|
|
1039
|
+
// Per-option params (a wipe's direction) ride at both detail levels too: an
|
|
1040
|
+
// id-only client needs the rows to offer the control at all.
|
|
1041
|
+
return o.params
|
|
1042
|
+
? {
|
|
1043
|
+
...projected,
|
|
1044
|
+
params: o.params.map((d) => ({
|
|
1045
|
+
field: d.field,
|
|
1046
|
+
label: d.label,
|
|
1047
|
+
options: d.options.map((p) => projectOption(p, detail)),
|
|
1048
|
+
})),
|
|
1049
|
+
}
|
|
1050
|
+
: projected
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
function projectOptionFields(o: PickerOption, detail: PickerCatalogDetail, imageUrl?: string): ProjectedPickerOption {
|
|
1054
|
+
// `imageUrl` (display data) rides at both detail levels too, present only
|
|
1055
|
+
// when the option has a picture.
|
|
980
1056
|
const extras = { ...(o.motion ? { motion: o.motion } : {}), ...(imageUrl ? { imageUrl } : {}) }
|
|
981
1057
|
return detail === "full"
|
|
982
1058
|
? {
|
package/src/prompt-builder.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* provider-aware prompt truncation (default 5000 chars), and reference image filtering by model support.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { joinSentences } from "./hint-join.js"
|
|
7
8
|
import { resolveTemplate, applyTemplate } from "./prompt-templates.js"
|
|
8
9
|
import { NATIVE_NEGATIVE_PROMPT_MODELS, MODELS_WITH_REFERENCE_IMAGE_SUPPORT, imageReferenceLimit, getMaxImagePromptChars, getMaxNegativePromptChars } from "@nodaro/shared"
|
|
9
10
|
import { getStylePromptHint, isDeniedStyleId } from "./style.js"
|
|
@@ -60,7 +61,7 @@ function composeIdentityDescPart(
|
|
|
60
61
|
if (c) parts.push(c)
|
|
61
62
|
const e = elementInjection?.trim()
|
|
62
63
|
if (e) parts.push(e)
|
|
63
|
-
return parts.length > 0 ? `${subject} — ${parts
|
|
64
|
+
return parts.length > 0 ? `${subject} — ${joinSentences(parts)}` : subject
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
/**
|
package/src/prompt-hint-join.ts
CHANGED
|
@@ -22,7 +22,11 @@
|
|
|
22
22
|
* Never mutates its inputs.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
/** The measured separator between the user's prompt and each folded hint.
|
|
25
|
+
/** The measured separator between the user's prompt and each folded hint.
|
|
26
|
+
* Pieces that already end a sentence are followed by a single space instead
|
|
27
|
+
* (`joinSentences`), so this is the longest separator a join can add. */
|
|
28
|
+
import { joinSentences } from "./hint-join.js"
|
|
29
|
+
|
|
26
30
|
export const PROMPT_HINT_SEPARATOR = ". "
|
|
27
31
|
|
|
28
32
|
/**
|
|
@@ -35,5 +39,7 @@ export const PROMPT_HINT_SEPARATOR = ". "
|
|
|
35
39
|
*/
|
|
36
40
|
export function joinPromptHints(userPrompt: string, hints: readonly string[]): string {
|
|
37
41
|
if (hints.length === 0) return userPrompt
|
|
38
|
-
|
|
42
|
+
// Sentence-aware: a prompt or hint that already ends a sentence is followed
|
|
43
|
+
// by a space, not by PROMPT_HINT_SEPARATOR (which made "watermark.. hint").
|
|
44
|
+
return joinSentences([userPrompt.trim(), ...hints])
|
|
39
45
|
}
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
* section is written flush-left rather than relying on that collapse to be
|
|
46
46
|
* harmless.
|
|
47
47
|
*/
|
|
48
|
-
import { joinPromptHints
|
|
48
|
+
import { joinPromptHints } from "./prompt-hint-join.js"
|
|
49
|
+
import { joinSentences } from "./hint-join.js"
|
|
49
50
|
import {
|
|
50
51
|
renderDirectionHintClauses,
|
|
51
52
|
type DirectionFamily,
|
|
@@ -173,10 +174,7 @@ export function partitionStyleClauses(
|
|
|
173
174
|
*/
|
|
174
175
|
export function styleSectionFromClauses(clauses: readonly SlottedPromptClause[]): string {
|
|
175
176
|
const line = (slot: PromptClauseSlot) =>
|
|
176
|
-
clauses
|
|
177
|
-
.filter((c) => c.slot === slot)
|
|
178
|
-
.map((c) => c.text)
|
|
179
|
-
.join(PROMPT_HINT_SEPARATOR)
|
|
177
|
+
joinSentences(clauses.filter((c) => c.slot === slot).map((c) => c.text))
|
|
180
178
|
const lines = [line("film"), line("scene")].filter((l) => l.length > 0)
|
|
181
179
|
return lines.length === 0 ? "" : `${STYLE_SECTION_HEADER}\n${lines.join("\n")}`
|
|
182
180
|
}
|
package/src/resolve-prompt.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveNodeRefs, readPromptAffixes, type PromptAffixes } from "@nodaro/shared"
|
|
2
2
|
import { SOCIAL_POST_NODE_TYPES } from "@nodaro/shared"
|
|
3
|
+
import { joinSentences } from "./hint-join.js"
|
|
3
4
|
|
|
4
5
|
export interface ResolvePromptArgs {
|
|
5
6
|
override?: string
|
|
@@ -72,7 +73,8 @@ export function applyPromptAffixes(
|
|
|
72
73
|
* override (list fan-out) > first present typed candidate > wired > "".
|
|
73
74
|
* "present" = non-empty after trim. {Label} refs are resolved on the chosen
|
|
74
75
|
* branch via the shared resolveNodeRefs. With `appendWired`, the chosen base
|
|
75
|
-
* AND the wired value are both emitted
|
|
76
|
+
* AND the wired value are both emitted, joined as sentences (`joinSentences`:
|
|
77
|
+
* ". ", or a space after one that already ends). Affixes wrap the result. */
|
|
76
78
|
export function resolvePrompt({ override, typed = [], wired, refMap, appendWired, affixes }: ResolvePromptArgs): string {
|
|
77
79
|
return applyPromptAffixes(resolveCore({ override, typed, wired, refMap, appendWired }), affixes, refMap)
|
|
78
80
|
}
|
|
@@ -83,7 +85,7 @@ function resolveCore({ override, typed = [], wired, refMap, appendWired }: Omit<
|
|
|
83
85
|
// so per-item fan-out prompts are unchanged.
|
|
84
86
|
if (appendWired && !present(override)) {
|
|
85
87
|
const base = typed.find(present)
|
|
86
|
-
return [base, wired].filter(present).map((s) => rr(s, refMap))
|
|
88
|
+
return joinSentences([base, wired].filter(present).map((s) => rr(s, refMap)))
|
|
87
89
|
}
|
|
88
90
|
if (present(override)) return rr(override, refMap)
|
|
89
91
|
for (const t of typed) if (present(t)) return rr(t, refMap)
|
|
@@ -92,13 +94,13 @@ function resolveCore({ override, typed = [], wired, refMap, appendWired }: Omit<
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
/** Compose a final NEGATIVE prompt from a TYPED base + a WIRED (connected
|
|
95
|
-
* negative-handle) value: both are emitted, joined
|
|
97
|
+
* negative-handle) value: both are emitted, joined as sentences (`joinSentences`, mirrors `appendWired`
|
|
96
98
|
* for the positive prompt). Pure join — the caller resolves `{label}` refs on
|
|
97
99
|
* the typed value first, and the wired value is already a resolved output that
|
|
98
100
|
* the input-resolver has filtered (referenced / Inject-Negative-off dropped).
|
|
99
101
|
* Generate-image / generate-video only; empty parts are dropped. */
|
|
100
102
|
export function composeNegative(typed?: string, wired?: string): string {
|
|
101
|
-
return [typed, wired]
|
|
103
|
+
return joinSentences([typed, wired])
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
/** Ordered typed-candidate fields per node type — the precedence source of
|
|
@@ -184,3 +186,19 @@ export function computeLlmChatFields(
|
|
|
184
186
|
systemPrompt: resolvePrompt({ typed: [data.systemPrompt as string | undefined], wired: wiredSystemPrompt, refMap }),
|
|
185
187
|
}
|
|
186
188
|
}
|
|
189
|
+
|
|
190
|
+
/** generate-script's topic. Both engines call this, so a wired Text node reads
|
|
191
|
+
* the same on a single-node run and a server run.
|
|
192
|
+
*
|
|
193
|
+
* Precedence is override (a list fan-out item) > wired > `data.prompt`. Wired
|
|
194
|
+
* comes BEFORE the typed field, the reverse of `computeNodePrompt`, because
|
|
195
|
+
* `data.prompt` has no editor: the panel's text field is the style guide. A
|
|
196
|
+
* value there was written by the old connect-time `{Label}` auto-fill or by an
|
|
197
|
+
* agent, and it is kept only as the fallback that saved workflows rely on. */
|
|
198
|
+
export function computeScriptTopic(
|
|
199
|
+
data: Record<string, unknown>,
|
|
200
|
+
{ override, wired, refMap }: Omit<ComputeNodePromptArgs, "appendWired">,
|
|
201
|
+
): string {
|
|
202
|
+
const core = [override, wired, data.prompt as string | undefined].find(present)
|
|
203
|
+
return applyPromptAffixes(core === undefined ? "" : rr(core, refMap), readPromptAffixes(data), refMap)
|
|
204
|
+
}
|