@nodaro/prompts 1.26.1 → 1.28.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 +3127 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1776 -10
- package/dist/index.d.ts +1776 -10
- package/dist/index.js +3094 -93
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- 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__/picker-art-images.test.ts +191 -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__/transitions-instant.test.ts +9 -5
- package/src/__tests__/transitions-scope.test.ts +593 -8
- package/src/__tests__/video-own-motion.test.ts +26 -0
- package/src/catalog-sections.ts +24 -0
- package/src/hint-join.ts +33 -1
- package/src/index.ts +4 -0
- package/src/parameter-prompt-hint.ts +20 -3
- package/src/person.ts +24 -8
- package/src/picker-art/character-art-files.generated.ts +1118 -0
- package/src/picker-art/images.ts +83 -0
- package/src/picker-art/index.ts +13 -0
- package/src/picker-art/look-preview-sets.ts +489 -0
- package/src/picker-art/paths.ts +53 -0
- package/src/picker-art/sound-art-files.generated.ts +319 -0
- package/src/picker-art/sound-art-map.ts +722 -0
- package/src/picker-art/sound-art-types.ts +19 -0
- package/src/picker-catalogs.ts +125 -24
- 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/styling.ts +18 -0
- package/src/transitions.ts +320 -51
- package/src/video-own-motion.ts +29 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PERSON_DIMENSION_SECTIONS, PERSON_FIELD_BY_DIMENSION } from "./person.js"
|
|
2
|
+
import { STYLING_DIMENSION_SECTIONS, STYLING_FIELD_BY_DIMENSION } from "./styling.js"
|
|
3
|
+
|
|
4
|
+
/** One of the topics the editor groups a catalog's settings under, as node-data fields. */
|
|
5
|
+
export interface CatalogTopic {
|
|
6
|
+
readonly label: string
|
|
7
|
+
readonly fields: ReadonlyArray<string>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The editor's topics for a catalog (Person, Styling), in order, with their
|
|
12
|
+
* settings as node-data fields — or undefined for every other catalog. Kept
|
|
13
|
+
* out of the picker-catalogs funnel on purpose: the topic lists are layout
|
|
14
|
+
* metadata, not catalog entries.
|
|
15
|
+
*/
|
|
16
|
+
export function catalogTopics(catalogId: string): ReadonlyArray<CatalogTopic> | undefined {
|
|
17
|
+
if (catalogId === "person") {
|
|
18
|
+
return PERSON_DIMENSION_SECTIONS.map((s) => ({ label: s.label, fields: s.dimensions.map((d) => PERSON_FIELD_BY_DIMENSION[d] as string) }))
|
|
19
|
+
}
|
|
20
|
+
if (catalogId === "styling") {
|
|
21
|
+
return STYLING_DIMENSION_SECTIONS.map((s) => ({ label: s.label, fields: s.dimensions.map((d) => STYLING_FIELD_BY_DIMENSION[d] as string) }))
|
|
22
|
+
}
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
package/src/hint-join.ts
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
* approved, and a user's preText/postText is never rewritten.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/** A piece that already closes a sentence: a Latin or CJK full stop, question
|
|
17
|
+
* or exclamation mark, or an ellipsis. */
|
|
18
|
+
const ENDS_SENTENCE = /[.!?…。!?]$/
|
|
17
19
|
|
|
18
20
|
export function joinHintFragments(fragments: ReadonlyArray<string>): string {
|
|
19
21
|
let out = ""
|
|
@@ -24,3 +26,33 @@ export function joinHintFragments(fragments: ReadonlyArray<string>): string {
|
|
|
24
26
|
}
|
|
25
27
|
return out
|
|
26
28
|
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Join prompt pieces as sentences: `". "` between two pieces, or a single
|
|
32
|
+
* space when the piece before already ends a sentence. Without that check a
|
|
33
|
+
* prompt typed with a closing period came out as
|
|
34
|
+
* "no watermark.. butterfly portrait lighting". Blank pieces are dropped. The
|
|
35
|
+
* pieces are otherwise kept verbatim, except that trailing space before a join
|
|
36
|
+
* is not carried into it ("cat " + "hint" gives "cat. hint").
|
|
37
|
+
*/
|
|
38
|
+
export function joinSentences(pieces: ReadonlyArray<string | undefined>): string {
|
|
39
|
+
let out = ""
|
|
40
|
+
for (const piece of pieces) {
|
|
41
|
+
if (!piece || !piece.trim()) continue
|
|
42
|
+
if (!out) { out = piece; continue }
|
|
43
|
+
const head = out.trimEnd()
|
|
44
|
+
out = ENDS_SENTENCE.test(head) ? `${head} ${piece}` : `${head}. ${piece}`
|
|
45
|
+
}
|
|
46
|
+
return out
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Append wired picker hints to a prompt, the way both DAG engines fold them:
|
|
51
|
+
* the hints join as clauses (`joinHintFragments`), then follow the prompt as a
|
|
52
|
+
* new sentence (`joinSentences`). No hints (or only blank ones) gives the
|
|
53
|
+
* prompt back unchanged.
|
|
54
|
+
*/
|
|
55
|
+
export function appendPromptHints(prompt: string | undefined, hints: ReadonlyArray<string>): string {
|
|
56
|
+
const joined = joinHintFragments(hints)
|
|
57
|
+
return joined.trim() ? joinSentences([prompt, joined]) : (prompt ?? "")
|
|
58
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from "./identity-lock.js"
|
|
|
14
14
|
export * from "./reference-rules.js"
|
|
15
15
|
export * from "./term.js"
|
|
16
16
|
export * from "./parameter-prompt-hint.js"
|
|
17
|
+
export * from "./video-own-motion.js"
|
|
17
18
|
export * from "./entity-prompts.js"
|
|
18
19
|
export * from "./brand-tokens.js"
|
|
19
20
|
export * from "./described-references.js"
|
|
@@ -99,3 +100,6 @@ export * from "./character-motion-diagnostics.js"
|
|
|
99
100
|
|
|
100
101
|
// --- Per-ad creative analysis for the social scraper nodes ---
|
|
101
102
|
export * from "./ad-creative-analysis.js"
|
|
103
|
+
|
|
104
|
+
// --- Picker art: option pictures + topic icons (editor and API) ---
|
|
105
|
+
export * from "./picker-art/index.js"
|
|
@@ -36,7 +36,7 @@ import { buildStylingHints } from "./styling.js"
|
|
|
36
36
|
import { buildTemporalHints } from "./temporal.js"
|
|
37
37
|
import { composeCameraMotionHintFromConnections } from "./camera-motions.js"
|
|
38
38
|
import { joinHintFragments } from "./hint-join.js"
|
|
39
|
-
import { composeTransitionHintFromConnections, type TransitionDuration, type TransitionIntensity, type TransitionPosition, type TransitionTiming } from "./transitions.js"
|
|
39
|
+
import { composeTransitionHintFromConnections, readTransitionOptionValues, type TransitionDuration, type TransitionIntensity, type TransitionPosition, type TransitionTiming } from "./transitions.js"
|
|
40
40
|
import { composeCharacterFxHintFromConnections, type CharacterFxDuration, type CharacterFxIntensity, type CharacterFxPosition, type CharacterFxTiming } from "./character-fx.js"
|
|
41
41
|
import { composeCharacterMotionHintFromConnections, type CharacterMotionPace, type CharacterMotionPosition, type CharacterMotionTiming } from "./character-motion.js"
|
|
42
42
|
import { containsMinorAgeHint, isMinorAge } from "./age-floor.js"
|
|
@@ -158,6 +158,14 @@ function byMode<T>(mode: PickerHintMode, full: T, compact: T): T {
|
|
|
158
158
|
return mode === "compact" ? compact : full
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/** How much a shot moves (the Motion node's three steps), as the clause it
|
|
162
|
+
* adds to a video prompt; the compact form is the bare term. */
|
|
163
|
+
const MOTION_INTENSITY_HINTS: Readonly<Record<string, { readonly full: string; readonly compact: string }>> = {
|
|
164
|
+
subtle: { full: "subtle, gentle motion with slow, minimal movement", compact: "subtle motion" },
|
|
165
|
+
moderate: { full: "moderate, natural motion at an even pace", compact: "moderate motion" },
|
|
166
|
+
dynamic: { full: "dynamic, energetic motion with fast, pronounced movement", compact: "dynamic motion" },
|
|
167
|
+
}
|
|
168
|
+
|
|
161
169
|
/**
|
|
162
170
|
* Dispatch by parameter-node type to its prompt-hint string. For camera-motion,
|
|
163
171
|
* pass `ctx` to include the composed start/end clauses; otherwise only the
|
|
@@ -227,8 +235,11 @@ function resolveParameterHint(
|
|
|
227
235
|
duration: asStr(data.duration) as TransitionDuration | undefined,
|
|
228
236
|
intensity: asStr(data.intensity) as TransitionIntensity | undefined,
|
|
229
237
|
}
|
|
238
|
+
// A picked row's own options (a wipe's `wipeDirection`), off the same node data.
|
|
239
|
+
const optionValues = readTransitionOptionValues(data)
|
|
240
|
+
const hintOptions = optionValues ? { optionValues } : undefined
|
|
230
241
|
if (!ctx) {
|
|
231
|
-
return withCustomText(data, composeTransitionHintFromConnections(transitionId, [], [], timing, mode))
|
|
242
|
+
return withCustomText(data, composeTransitionHintFromConnections(transitionId, [], [], timing, mode, hintOptions))
|
|
232
243
|
}
|
|
233
244
|
const startHints: string[] = []
|
|
234
245
|
const endHints: string[] = []
|
|
@@ -241,7 +252,7 @@ function resolveParameterHint(
|
|
|
241
252
|
if (edge.targetHandle === "startState") startHints.push(hint)
|
|
242
253
|
else if (edge.targetHandle === "endState") endHints.push(hint)
|
|
243
254
|
}
|
|
244
|
-
return withCustomText(data, composeTransitionHintFromConnections(transitionId, startHints, endHints, timing, mode))
|
|
255
|
+
return withCustomText(data, composeTransitionHintFromConnections(transitionId, startHints, endHints, timing, mode, hintOptions))
|
|
245
256
|
}
|
|
246
257
|
|
|
247
258
|
if (node.type === "character-fx") {
|
|
@@ -409,6 +420,12 @@ function resolveBaseHint(
|
|
|
409
420
|
return withCustomText(data, byMode(mode, getCompositionEffectPromptHint, getCompositionEffectTerm)(asStr(data.compositionEffect)))
|
|
410
421
|
case "post-process-effects":
|
|
411
422
|
return withCustomText(data, buildPostProcessHints(data.postProcess, mode).join(", "))
|
|
423
|
+
// Motion intensity: a Generation Settings node that a video node's
|
|
424
|
+
// Settings input takes as a prompt fragment (video only).
|
|
425
|
+
case "motion": {
|
|
426
|
+
const hint = MOTION_INTENSITY_HINTS[asStr(data.motion)]
|
|
427
|
+
return hint ? byMode(mode, hint.full, hint.compact) : ""
|
|
428
|
+
}
|
|
412
429
|
|
|
413
430
|
// Free text authored by the user, not catalog copy — there is no shorter
|
|
414
431
|
// professional form to swap in, so these are identical in both modes.
|
package/src/person.ts
CHANGED
|
@@ -1060,7 +1060,8 @@ export interface PersonValue {
|
|
|
1060
1060
|
/** Specific age in years. Only consulted when `age === "age-custom"`. */
|
|
1061
1061
|
customAge?: number
|
|
1062
1062
|
/** Single id, or an array of up to 2 ids for mixed heritage (e.g.
|
|
1063
|
-
* ["slavic","mediterranean"] → "of mixed Slavic
|
|
1063
|
+
* ["slavic","mediterranean"] → "of mixed Slavic Eastern European and
|
|
1064
|
+
* Mediterranean heritage"). */
|
|
1064
1065
|
ethnicity?: string | ReadonlyArray<string>
|
|
1065
1066
|
/** Regional / cultural aesthetic vibe (e.g. `"cali-beach"`, `"parisienne"`,
|
|
1066
1067
|
* `"kinshasa-sape"`). Composes with ethnicity, skin tone, hair, and
|
|
@@ -1238,16 +1239,31 @@ function normalizePickIds(value: unknown): string[] {
|
|
|
1238
1239
|
*/
|
|
1239
1240
|
type PersonFragmentFor = (id: string | undefined | null) => string
|
|
1240
1241
|
|
|
1242
|
+
/**
|
|
1243
|
+
* The heritage an ethnicity entry names, as a prompt-safe noun: its promptHint
|
|
1244
|
+
* without the "of … descent" / "… heritage" wrapper ("of Mediterranean descent"
|
|
1245
|
+
* → "Mediterranean"). NOT the short label — that is display copy for the
|
|
1246
|
+
* picker grid ("Mediter.", "Pacific Isl.", "any", a bare "East"), which is what
|
|
1247
|
+
* two picks used to put in the prompt ("of mixed Slavic and Mediter. heritage").
|
|
1248
|
+
*/
|
|
1249
|
+
function heritageNoun(id: string): string | undefined {
|
|
1250
|
+
const noun = (getPerson(id)?.promptHint ?? "")
|
|
1251
|
+
.trim()
|
|
1252
|
+
.replace(/^of\s+/i, "")
|
|
1253
|
+
.replace(/\s+(descent|heritage)$/i, "")
|
|
1254
|
+
.trim()
|
|
1255
|
+
return noun.length > 0 ? noun : undefined
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1241
1258
|
function buildEthnicityFragment(value: unknown, fragmentFor: PersonFragmentFor): string {
|
|
1242
|
-
|
|
1259
|
+
// "mixed" is itself the mixed-heritage pick; paired with a specific one it
|
|
1260
|
+
// adds nothing, so the pair reads as that one.
|
|
1261
|
+
const ids = normalizePickIds(value).filter((id, _, all) => all.length < 2 || id !== "mixed")
|
|
1243
1262
|
if (ids.length === 0) return ""
|
|
1244
1263
|
if (ids.length === 1) return fragmentFor(ids[0])
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
.filter((s): s is string => Boolean(s))
|
|
1249
|
-
if (labels.length < 2) return fragmentFor(ids[0])
|
|
1250
|
-
return `of mixed ${labels[0]} and ${labels[1]} heritage`
|
|
1264
|
+
const nouns = ids.slice(0, 2).map(heritageNoun).filter((s): s is string => Boolean(s))
|
|
1265
|
+
if (nouns.length < 2) return fragmentFor(ids[0])
|
|
1266
|
+
return `of mixed ${nouns[0]} and ${nouns[1]} heritage`
|
|
1251
1267
|
}
|
|
1252
1268
|
|
|
1253
1269
|
/**
|