@nodaro/prompts 1.28.0 → 1.30.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 CHANGED
@@ -5159,7 +5159,8 @@ var TRANSITIONS = [
5159
5159
  description: "Hidden cut disguised by matched motion and color",
5160
5160
  promptHint: "hidden seamless transition: the camera motion, color palette, and on-screen motion at the end of the first shot continue exactly across the cut into the second shot, so the boundary is invisible and the two shots feel like one unbroken take",
5161
5161
  term: "invisible cut",
5162
- instant: true
5162
+ instant: true,
5163
+ blendable: true
5163
5164
  },
5164
5165
  {
5165
5166
  id: "whip-pan",
@@ -5572,7 +5573,8 @@ var TRANSITIONS = [
5572
5573
  description: "Subject jumps, landing matches into new scene",
5573
5574
  promptHint: "the subject launches into a jump, and at the height of the leap the picture cuts to a new place. The camera follows the arc of the jump at the same speed on both sides of the cut, and the subject stays at the same place in the frame. In the second shot the same jump carries on without a break, and the subject comes down and lands in the new place. The shot ends on the subject landed in the second shot, fully resolved",
5574
5575
  term: "match cut on a jump",
5575
- instant: true
5576
+ instant: true,
5577
+ blendable: true
5576
5578
  },
5577
5579
  {
5578
5580
  id: "hand-swipe",
@@ -5769,6 +5771,14 @@ function isInstantTransition(id) {
5769
5771
  if (ids.length === 0) return false;
5770
5772
  return ids.every((one) => getTransition(one)?.instant === true);
5771
5773
  }
5774
+ function isBlendableTransition(id) {
5775
+ const ids = typeof id === "string" ? [id] : id ? [...id] : [];
5776
+ if (ids.length === 0) return false;
5777
+ return ids.every((one) => {
5778
+ const t = getTransition(one);
5779
+ return t?.instant === true && t.blendable === true;
5780
+ });
5781
+ }
5772
5782
  var INSTANT_CUT_CLAUSE = "an abrupt single-frame hard cut, no dissolve, crossfade or superimposition; the two images never blend";
5773
5783
  var LEADING_LABEL = /^\s*((?:[^\s:,;.()]+\s+){0,4}[^\s:,;.()]+)\s*:\s+/;
5774
5784
  var normalizePhrase = (s) => s.trim().toLowerCase().replace(/\s+/g, " ");
@@ -5777,16 +5787,23 @@ function transitionHintBody(id, term, values2) {
5777
5787
  const t = normalizePhrase(term);
5778
5788
  return hint.split(/,\s*/).filter((item) => normalizePhrase(item) !== t).join(", ").trim();
5779
5789
  }
5780
- function transitionFragment(id, withCutClause, values2) {
5790
+ function transitionFragment(id, cutClause, values2) {
5781
5791
  const term = getTransitionTerm(id);
5782
5792
  if (!term) return "";
5783
- const inner = [transitionHintBody(id, term, values2), withCutClause ? INSTANT_CUT_CLAUSE : ""].filter((part) => part.length > 0).join("; ");
5793
+ const inner = [transitionHintBody(id, term, values2), cutClause].filter((part) => part.length > 0).join("; ");
5784
5794
  return inner.length > 0 && normalizePhrase(inner) !== normalizePhrase(term) ? `${term} (${inner})` : term;
5785
5795
  }
5786
5796
  function renderTransitionBases(ids, _mode = "full", values2) {
5797
+ return renderBasesWith(ids, INSTANT_CUT_CLAUSE, values2);
5798
+ }
5799
+ function renderBasesWith(ids, cutClause, values2) {
5787
5800
  const picked = ids.filter((id) => getTransitionTerm(id).length > 0);
5788
5801
  const instant = isInstantTransition(picked);
5789
- return picked.map((id, i) => transitionFragment(id, instant && i === 0, values2));
5802
+ return picked.map((id, i) => transitionFragment(id, instant && i === 0 ? cutClause : "", values2));
5803
+ }
5804
+ function blendedCutClause(duration3) {
5805
+ const row = TRANSITION_DURATIONS.find((d) => d.id === duration3);
5806
+ return row?.blendsCut ? `instead of a hard cut, the two shots blend into each other over ${row.term}` : "";
5790
5807
  }
5791
5808
  var TRANSITION_POSITIONS = [
5792
5809
  { id: "auto", label: "Auto", description: "Let the model place it", promptHint: "", term: "" },
@@ -5798,7 +5815,7 @@ var TRANSITION_POSITIONS = [
5798
5815
  var TRANSITION_DURATIONS = [
5799
5816
  { id: "auto", label: "Auto", description: "Let the model time it", promptHint: "", term: "" },
5800
5817
  { id: "instant", label: "Instant", description: "No perceptible duration", promptHint: "occurring instantaneously", term: "instantaneous" },
5801
- { id: "short", label: "Short (~1s)", description: "Approximately 1 second", promptHint: "lasting approximately 1 second", term: "about 1 second" },
5818
+ { id: "short", label: "Short (~1s)", description: "Approximately 1 second", promptHint: "lasting approximately 1 second", term: "about 1 second", blendsCut: true },
5802
5819
  { id: "medium", label: "Medium (~2s)", description: "Approximately 2 seconds", promptHint: "lasting approximately 2 seconds", term: "about 2 seconds" },
5803
5820
  { id: "long", label: "Long (~3s)", description: "Approximately 3 seconds", promptHint: "lasting approximately 3 seconds", term: "about 3 seconds" }
5804
5821
  ];
@@ -5823,11 +5840,13 @@ var SHOT_POSITION_CLAUSES = Object.fromEntries(
5823
5840
  );
5824
5841
  var DURATION_CLAUSES = clausesOf(TRANSITION_DURATIONS);
5825
5842
  var INTENSITY_CLAUSES = clausesOf(TRANSITION_INTENSITIES);
5826
- function composeTransitionHintFromConnections(transitionId, startHints, endHints, timing, mode = "full", options) {
5843
+ function composeTransitionHintFromConnections(transitionId, startHints, endHints, timing, _mode = "full", options) {
5827
5844
  const ids = Array.isArray(transitionId) ? Array.from(new Set(transitionId)).slice(0, 2) : transitionId ? [transitionId] : [];
5828
- const baseHints = renderTransitionBases(ids, mode, options?.optionValues);
5845
+ const contributing = ids.filter((id) => getTransitionTerm(id).length > 0);
5846
+ const instant = isInstantTransition(contributing);
5847
+ const blend = instant && isBlendableTransition(contributing) ? blendedCutClause(timing?.duration) : "";
5848
+ const baseHints = renderBasesWith(ids, blend || INSTANT_CUT_CLAUSE, options?.optionValues);
5829
5849
  if (baseHints.length === 0) return "";
5830
- const instant = isInstantTransition(ids.filter((id) => getTransitionTerm(id).length > 0));
5831
5850
  const combinedBase = baseHints.join(", and ");
5832
5851
  const parts = [combinedBase];
5833
5852
  if (timing?.position && timing.position !== "auto" && !(instant && timing.position === "full")) {
@@ -21588,6 +21607,8 @@ function toOptions(arr, categoryField) {
21588
21607
  if (categoryField) opt.category = e[categoryField];
21589
21608
  if (e.adultOnly) opt.adultOnly = true;
21590
21609
  if (e.instant) opt.instant = true;
21610
+ if (e.blendable) opt.blendable = true;
21611
+ if (e.blendsCut) opt.blendsCut = true;
21591
21612
  if (e.options?.length) {
21592
21613
  opt.params = e.options.map((o) => ({
21593
21614
  field: o.field,
@@ -45622,6 +45643,20 @@ var VOICE_DESIGN_PRESETS = [
45622
45643
  group: "Narration & Character",
45623
45644
  data: { voiceDescription: "A calm, soft-spoken meditation guide with a slow, breathy, reassuring delivery." }
45624
45645
  },
45646
+ {
45647
+ id: "voice-design/casual-creator-female",
45648
+ name: "Casual Creator (female)",
45649
+ description: "Relaxed, warm, everyday.",
45650
+ group: "Narration & Character",
45651
+ data: { voiceDescription: "An adult woman in her late twenties talking casually to the viewer as if telling a friend about something she tried: relaxed, warm and upbeat, a natural conversational pace with small pauses, a light neutral American accent, recorded close and dry in a quiet room, never polished or announcer-like." }
45652
+ },
45653
+ {
45654
+ id: "voice-design/casual-creator-male",
45655
+ name: "Casual Creator (male)",
45656
+ description: "Relaxed, warm, everyday.",
45657
+ group: "Narration & Character",
45658
+ data: { voiceDescription: "An adult man in his late twenties talking casually to the viewer as if telling a friend about something he tried: relaxed, warm and upbeat, a natural conversational pace with small pauses, a light neutral American accent, recorded close and dry in a quiet room, never polished or announcer-like." }
45659
+ },
45625
45660
  // ── Professional & Assistant ─────────────────────────────────────────────
45626
45661
  {
45627
45662
  id: "voice-design/hype-announcer",
@@ -45729,6 +45764,27 @@ var TEXT_TO_AUDIO_PRESETS = [
45729
45764
  group: "Ambiences (loopable)",
45730
45765
  data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.5, prompt: "low sci-fi ambient drone, ominous spaceship hum" }
45731
45766
  },
45767
+ {
45768
+ id: "text-to-audio/living-room-tone",
45769
+ name: "Living Room Tone",
45770
+ description: "Quiet, lived-in room.",
45771
+ group: "Ambiences (loopable)",
45772
+ data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.4, prompt: "quiet living room room tone, faint air and a soft distant household hum, steady and even, an empty room at rest" }
45773
+ },
45774
+ {
45775
+ id: "text-to-audio/kitchen-hum",
45776
+ name: "Kitchen Hum",
45777
+ description: "Fridge hum in a still kitchen.",
45778
+ group: "Ambiences (loopable)",
45779
+ data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.4, prompt: "home kitchen room tone, a low steady refrigerator hum with faint air, calm and continuous, an empty kitchen at rest" }
45780
+ },
45781
+ {
45782
+ id: "text-to-audio/cafe-murmur",
45783
+ name: "Caf\xE9 Murmur",
45784
+ description: "Soft, indistinct caf\xE9 chatter.",
45785
+ group: "Ambiences (loopable)",
45786
+ data: { provider: "elevenlabs-sfx", duration: 22, loop: true, promptInfluence: 0.4, prompt: "caf\xE9 background ambience, a soft indistinct murmur of distant conversation and the occasional faint cup clink, steady and even, no clear words" }
45787
+ },
45732
45788
  // ── UI & Stingers ────────────────────────────────────────────────────────
45733
45789
  {
45734
45790
  id: "text-to-audio/ui-click",
@@ -45884,6 +45940,16 @@ var LLM_CHAT_PRESETS = [
45884
45940
  temperature: 0.7
45885
45941
  }
45886
45942
  },
45943
+ {
45944
+ id: "llm-chat/hook-generator",
45945
+ name: "Hook Generator",
45946
+ description: "10 opening lines for a talking-to-camera video.",
45947
+ group: "Writing & Marketing",
45948
+ data: {
45949
+ systemPrompt: "You write opening lines for short vertical videos in which one person talks straight to the camera about a product, a site or an idea. Given the user's topic, write 10 different opening lines.\n\nEvery line:\n- is one short spoken clause of at most 8 words;\n- leads with the payload the viewer can check: a number, a price, a time saved, a before and after, a contrast with the usual way, or a named result;\n- opens on the claim itself \u2014 no greeting, no filler word, no introduction of the product before the claim;\n- sounds said, not written: plain words, contractions welcome, no hashtags, no emoji, no stage directions;\n- spells the product or brand exactly as the user gives it and writes numbers as digits.\n\nVary the angle across the 10: a surprising number, a price, time saved, a mistake to avoid, a before and after, a contrast, a named result, a direct question. Write in the language the user writes in. Return only the numbered list, one line each.",
45950
+ temperature: 0.9
45951
+ }
45952
+ },
45887
45953
  // ── Utility ──────────────────────────────────────────────────────────────
45888
45954
  {
45889
45955
  id: "llm-chat/prompt-enhancer",
@@ -46065,6 +46131,16 @@ var IMAGE_TO_TEXT_PRESETS = [
46065
46131
  group: "Extraction",
46066
46132
  data: { detailLevel: "detailed", customPrompt: "Write a compelling e-commerce product description based on this product image." }
46067
46133
  },
46134
+ {
46135
+ id: "image-to-text/site-screenshot-summary",
46136
+ name: "Site Screenshot Summary",
46137
+ description: "Section, headings and figures from a web page.",
46138
+ group: "Extraction",
46139
+ data: {
46140
+ detailLevel: "structured",
46141
+ customPrompt: 'Describe this website screenshot as five labelled lines. Section: which part of the site it is (for example the top of the home page, a feature, how it works, reviews, pricing, sign-up, navigation or footer). Headings: every heading exactly as written. Figures: every number, price, rating and date exactly as written, each with what it refers to. Brand: the product or company name exactly as spelled. First look: what a viewer notices first, in one sentence. Write "none" on a line with nothing to report.'
46142
+ }
46143
+ },
46068
46144
  // ── Creative ─────────────────────────────────────────────────────────────
46069
46145
  {
46070
46146
  id: "image-to-text/scene-description",
@@ -46176,6 +46252,20 @@ var ADD_CAPTIONS_PRESETS = [
46176
46252
  description: "Subtitles along the top.",
46177
46253
  group: "Caption Styles",
46178
46254
  data: { style: "subtitle", position: "top", fontSize: 36, color: "#FFFFFF", autoTranscribe: true }
46255
+ },
46256
+ {
46257
+ id: "add-captions/hook-plate",
46258
+ name: "Hook Plate",
46259
+ description: "Your opening line as a bold plate near the top.",
46260
+ group: "Caption Styles",
46261
+ data: { style: "subtitle", look: "clean", position: "top", positionY: 20, fontSize: 32, fontWeight: 800, maxWordsPerLine: 4, color: "#111111", backgroundColor: "#FFFFFF", autoTranscribe: false }
46262
+ },
46263
+ {
46264
+ id: "add-captions/body-captions",
46265
+ name: "Body Captions",
46266
+ description: "Word-by-word captions under the face.",
46267
+ group: "Caption Styles",
46268
+ data: { style: "word-highlight", look: "outline", position: "bottom", positionY: 72, fontSize: 32, maxWordsPerLine: 3, color: "#FFFFFF", autoTranscribe: true }
46179
46269
  }
46180
46270
  ];
46181
46271
  var COMBINE_VIDEOS_PRESETS = [
@@ -46737,6 +46827,80 @@ function getFactoryPresets(nodeType) {
46737
46827
  return FACTORY_PRESETS[nodeType] ?? [];
46738
46828
  }
46739
46829
 
46830
+ // src/caption-treatments.ts
46831
+ var HOOK_PLATE_PRESET_ID = "add-captions/hook-plate";
46832
+ var BODY_CAPTIONS_PRESET_ID = "add-captions/body-captions";
46833
+ var CAPTION_LEVERS_BY_LANGUAGE = {
46834
+ he: { fontFamily: "Rubik", uppercase: false }
46835
+ };
46836
+ var CAPTION_SEGMENT_LEVER_KEYS = [
46837
+ "style",
46838
+ "position",
46839
+ "fontSize",
46840
+ "color",
46841
+ "backgroundColor",
46842
+ "look",
46843
+ "fontFamily",
46844
+ "fontWeight",
46845
+ "strokeColor",
46846
+ "strokeWidth",
46847
+ "highlightColor",
46848
+ "uppercase",
46849
+ "positionY",
46850
+ "animate",
46851
+ "maxWordsPerLine"
46852
+ ];
46853
+ var LEVER_KEYS = new Set(CAPTION_SEGMENT_LEVER_KEYS);
46854
+ function pickLevers(source) {
46855
+ const out = {};
46856
+ if (!source) return out;
46857
+ for (const [k, v] of Object.entries(source)) if (LEVER_KEYS.has(k) && v !== void 0 && v !== null) out[k] = v;
46858
+ return out;
46859
+ }
46860
+ function presetLevers(id) {
46861
+ const preset = ADD_CAPTIONS_PRESETS.find((p) => p.id === id);
46862
+ if (!preset) throw new Error(`The ${id} caption preset is missing from the catalog.`);
46863
+ return pickLevers(preset.data);
46864
+ }
46865
+ var PLATE_LEVERS = presetLevers(HOOK_PLATE_PRESET_ID);
46866
+ var BODY_LEVERS = presetLevers(BODY_CAPTIONS_PRESET_ID);
46867
+ function languageLevers(tag) {
46868
+ if (typeof tag !== "string") return {};
46869
+ const primary = tag.trim().split(/[-_]/)[0].toLowerCase();
46870
+ return Object.hasOwn(CAPTION_LEVERS_BY_LANGUAGE, primary) ? CAPTION_LEVERS_BY_LANGUAGE[primary] : {};
46871
+ }
46872
+ var isTime = (n) => typeof n === "number" && Number.isFinite(n);
46873
+ function hookPlateCaptionSegments(plan, opts = {}) {
46874
+ const hookEnd = plan.hookEndMs;
46875
+ if (!isTime(hookEnd) || hookEnd < 0) return { segments: [], dropped: ["hook", "body"] };
46876
+ const language = languageLevers(plan.language);
46877
+ const limit = typeof plan.videoDurationMs === "number" ? plan.videoDurationMs : Number.POSITIVE_INFINITY;
46878
+ const segments = [];
46879
+ const dropped = [];
46880
+ const plateEnd = Math.min(hookEnd, limit);
46881
+ if (plateEnd > 0 && typeof plan.hookText === "string" && /\S/.test(plan.hookText)) {
46882
+ segments.push({ startMs: 0, endMs: plateEnd, text: plan.hookText, ...PLATE_LEVERS, ...language });
46883
+ } else {
46884
+ dropped.push("hook");
46885
+ }
46886
+ const bodyEnd = Math.min(plan.bodyEndMs, limit);
46887
+ const words = Array.isArray(plan.captions) ? plan.captions : [];
46888
+ const chosen = pickLevers(opts.bodyLevers);
46889
+ const bodyLevers = Object.keys(chosen).length > 0 ? chosen : BODY_LEVERS;
46890
+ if (bodyEnd > hookEnd && words.length > 0) {
46891
+ segments.push({
46892
+ startMs: hookEnd,
46893
+ endMs: bodyEnd,
46894
+ captions: words.map((w) => ({ text: w.text, startMs: w.startMs, endMs: w.endMs })),
46895
+ ...bodyLevers,
46896
+ ...language
46897
+ });
46898
+ } else {
46899
+ dropped.push("body");
46900
+ }
46901
+ return { segments, dropped };
46902
+ }
46903
+
46740
46904
  // src/style-presets.ts
46741
46905
  var STYLE_PRESETS = [
46742
46906
  {
@@ -47425,6 +47589,7 @@ exports.BACKDROPS = BACKDROPS;
47425
47589
  exports.BACKDROP_CATEGORY_LABELS = BACKDROP_CATEGORY_LABELS;
47426
47590
  exports.BACKDROP_CATEGORY_ORDER = BACKDROP_CATEGORY_ORDER;
47427
47591
  exports.BACKDROP_IDS = BACKDROP_IDS;
47592
+ exports.BODY_CAPTIONS_PRESET_ID = BODY_CAPTIONS_PRESET_ID;
47428
47593
  exports.BRAND_PRESETS = BRAND_PRESETS;
47429
47594
  exports.BRAND_PRESET_IDS = BRAND_PRESET_IDS;
47430
47595
  exports.BRAND_PRESET_META = BRAND_PRESET_META;
@@ -47434,6 +47599,8 @@ exports.CAMERA_MOTIONS = CAMERA_MOTIONS;
47434
47599
  exports.CAMERA_MOTION_CATEGORY_LABELS = CAMERA_MOTION_CATEGORY_LABELS;
47435
47600
  exports.CAMERA_MOTION_CATEGORY_ORDER = CAMERA_MOTION_CATEGORY_ORDER;
47436
47601
  exports.CAMERA_MOTION_IDS = CAMERA_MOTION_IDS;
47602
+ exports.CAPTION_LEVERS_BY_LANGUAGE = CAPTION_LEVERS_BY_LANGUAGE;
47603
+ exports.CAPTION_SEGMENT_LEVER_KEYS = CAPTION_SEGMENT_LEVER_KEYS;
47437
47604
  exports.CHARACTER_ART_FAMILIES = CHARACTER_ART_FAMILIES;
47438
47605
  exports.CHARACTER_ART_FILES = CHARACTER_ART_FILES;
47439
47606
  exports.CHARACTER_ART_PATH = CHARACTER_ART_PATH;
@@ -47491,6 +47658,7 @@ exports.HELD_PROPS = HELD_PROPS;
47491
47658
  exports.HELD_PROP_CATEGORY_LABELS = HELD_PROP_CATEGORY_LABELS;
47492
47659
  exports.HELD_PROP_CATEGORY_ORDER = HELD_PROP_CATEGORY_ORDER;
47493
47660
  exports.HELD_PROP_IDS = HELD_PROP_IDS;
47661
+ exports.HOOK_PLATE_PRESET_ID = HOOK_PLATE_PRESET_ID;
47494
47662
  exports.IMAGE_HINT_MODE_DEFAULT = IMAGE_HINT_MODE_DEFAULT;
47495
47663
  exports.IMAGE_REFERENCE_PROMPT_DOCTRINE = IMAGE_REFERENCE_PROMPT_DOCTRINE;
47496
47664
  exports.IMAGE_WIZARD_CATEGORIES = IMAGE_WIZARD_CATEGORIES;
@@ -47680,6 +47848,7 @@ exports.applyTemplate = applyTemplate;
47680
47848
  exports.asBodyClauses = asBodyClauses;
47681
47849
  exports.assembleImageInput = assembleImageInput;
47682
47850
  exports.assembleSunoInput = assembleSunoInput;
47851
+ exports.blendedCutClause = blendedCutClause;
47683
47852
  exports.buildActionFxHints = buildActionFxHints;
47684
47853
  exports.buildActionFxTerms = buildActionFxTerms;
47685
47854
  exports.buildAdCreativeAnalysisUserText = buildAdCreativeAnalysisUserText;
@@ -47983,9 +48152,11 @@ exports.groupFactoryPresets = groupFactoryPresets;
47983
48152
  exports.hasCatalogPacksFor = hasCatalogPacksFor;
47984
48153
  exports.hasScene3DLayoutScopingLine = hasScene3DLayoutScopingLine;
47985
48154
  exports.hasUpstreamCharacter = hasUpstreamCharacter;
48155
+ exports.hookPlateCaptionSegments = hookPlateCaptionSegments;
47986
48156
  exports.identityRefsSentence = identityRefsSentence;
47987
48157
  exports.insertBeforeStyleSection = insertBeforeStyleSection;
47988
48158
  exports.isAnalyzablePicker = isAnalyzablePicker;
48159
+ exports.isBlendableTransition = isBlendableTransition;
47989
48160
  exports.isDeniedStyleId = isDeniedStyleId;
47990
48161
  exports.isInstantTransition = isInstantTransition;
47991
48162
  exports.isInstrumentalVocal = isInstrumentalVocal;