@slatesvideo/shared 0.5.5 → 0.5.6

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.
@@ -11,14 +11,45 @@
11
11
  // 2. One authoritative face avoids averaging competing renderings.
12
12
  // 3. One generation means one asset to inspect and bind.
13
13
  //
14
- // KNOWN COST, accepted for v1: a neutral chest-up portrait carries no dental
15
- // information, so a character who smiles in a shot gets invented teeth. The
16
- // 2026-06-26 doctrine already holds that the user's prompt owns expression.
17
- // Revisit only if a receipt shows invented teeth.
14
+ // EXPRESSION IS A SLIGHT SMILE WITH THE TEETH JUST VISIBLE (Eric, 2026-07-30,
15
+ // replacing the v1 neutral default). The v1 note called neutral's missing
16
+ // dental information a "known cost, revisit only if a receipt shows invented
17
+ // teeth" — this is that revisit, and it arrived from the other direction:
18
+ // Eric hand-edited a sheet to smiling and the result "worked really well".
19
+ //
20
+ // WHY: a closed-mouth portrait carries ZERO dental information, so every
21
+ // downstream shot where the character smiles has to invent teeth, and teeth are
22
+ // person-specific and stable — inventing them is a highly visible identity
23
+ // break. The smile also records the nasolabial fold, the eye crinkle and where
24
+ // the cheeks sit raised, none of which a neutral mouth shows.
25
+ //
26
+ // THE COST, NAMED: `references-read-literally.md` says a baked-in property is
27
+ // read as a property of the SUBJECT, so a smile risks a character who smiles
28
+ // through a beat that asked for grief. Survivable because the 2026-06-26
29
+ // doctrine still holds — the user's prompt owns expression, and downstream
30
+ // prompts name an emotional register on every delivered line.
31
+ //
32
+ // SLIGHT, never a grin: a broad smile deforms eyes, cheeks and mouth enough
33
+ // that the model has to un-deform it for any neutral shot.
34
+ //
35
+ // NON-HUMANS ARE CARVED OUT (Eric, 2026-07-30). A smile clause on a horse, a
36
+ // dragon or a robot produces bared teeth. Non-human characters get a natural
37
+ // neutral expression instead. NOTE THE SCOPE DIFFERS from the A-pose carve-out
38
+ // beside it: that one is anatomical (quadruped / non-bipedal), this one is
39
+ // about having a human mouth — so a BIPEDAL robot or humanoid alien is covered
40
+ // by this carve-out and not by that one. Two carve-outs, deliberately, because
41
+ // one predicate does not fit both.
42
+ //
43
+ // Receipt strength: N=1, and it is a "looked right" judgement, not a scored
44
+ // identity-hold comparison against the neutral sheets. HOW YOU'D KNOW THIS IS
45
+ // BEATEN: a character smiling through a beat prompted angry or grieving, or
46
+ // identity holding measurably worse than neutral did. Reverting is one line.
18
47
  //
19
48
  // THE FRONT PANEL IS HEADLESS (shipped 2026-07-22, receipt-gated). The face is
20
- // cropped off the front body panel — the "ghost mannequin" treatment — taking
21
- // competing face renderings 2 → 1. The BACK panel keeps its head: it has no
49
+ // cropped off the front body panel, taking competing face renderings 2 → 1.
50
+ // ONLY the face — the body, neck, arms and hands render normally; see the
51
+ // 2026-07-30 (b) note below for what happens when that isn't said. The BACK
52
+ // panel keeps its head: it has no
22
53
  // face to compete with the portrait, and it is the only panel where hair fall
23
54
  // reads. The rule is "kill every competing rendering of the FACE", not "kill
24
55
  // every head".
@@ -27,9 +58,45 @@
27
58
  // real generations (research/model-prompting-research.md, "Head-crop receipt"):
28
59
  // 1. It is prompt-reachable. NB2 renders a clean invisible-mannequin panel
29
60
  // with no refusal. THIS DEPENDS ON THE PHRASING: it is framed as FRAMING
30
- // ("cropped at the collarbone, head not shown, invisible-mannequin
31
- // presentation"), a standard e-commerce genre with deep training data.
32
- // Never phrase it as removal or decapitation.
61
+ // ("cropped at the collarbone, invisible-mannequin presentation"), a
62
+ // standard e-commerce genre with deep training data. Never phrase it as
63
+ // removal or decapitation.
64
+ //
65
+ // 2026-07-30 (a) — THE PHRASING NARROWED, and the receipt above was
66
+ // MODEL-SCOPED. "framed from the collarbone down with the head not shown"
67
+ // passed NB2 and is a HARD 422 on gpt-image-2: fal returns
68
+ // content_policy_violation with loc ["body","prompt"], so the text is
69
+ // rejected before any image is read. Diagnosis: "the head not shown"
70
+ // states an anatomical ABSENCE — a headless human body — which reads as
71
+ // gore to OpenAI's classifier. "cropped at the collarbone" states a
72
+ // CAMERA fact and carries the same instruction.
73
+ // RULE: describe an exclusion as a framing choice, never as a missing
74
+ // body part. The "invisible-mannequin" genre anchor is KEPT because the
75
+ // NB2 receipt says it is what makes the panel reachable there.
76
+ // HOW YOU'D KNOW THIS IS BEATEN: a front panel that comes back with a
77
+ // head on it, meaning "cropped at the collarbone" alone is too weak
78
+ // without the absence clause. If that happens, the fix is a
79
+ // model-conditional phrasing, not restoring the 422.
80
+ //
81
+ // 2026-07-30 (b) — THE GENRE ANCHOR HAS TO BE SCOPED TO THE FACE, and
82
+ // this one cost real generations. "an invisible-mannequin presentation
83
+ // WHERE THE CLOTHING HOLDS ITS OWN SHAPE" is the e-commerce genre stated
84
+ // in full — and the full genre means NO BODY AT ALL, an empty outfit
85
+ // photographed on nothing. Eric's sheets came back with the skin removed:
86
+ // no neck, no hands, no forearms, a floating garment. The genre anchor
87
+ // was doing exactly what it says.
88
+ // Eric's replacement, verbatim, and the default since: "an invisible-
89
+ // mannequin presentation with just the face cropped out." Same anchor
90
+ // (still what makes the panel reachable on NB2), bounded so the only
91
+ // thing missing is the face.
92
+ // RULE: a genre anchor imports the WHOLE genre unless you bound it —
93
+ // name what STAYS, not just what goes. This is the same failure shape as
94
+ // (a) from the opposite side: (a) was an exclusion phrased too
95
+ // anatomically, (b) was an exclusion scoped too widely.
96
+ // HOW YOU'D KNOW THIS IS BEATEN: front panels returning with a head on
97
+ // them — "just the face cropped out" would then be reading as a face
98
+ // edit rather than a crop, leaving the collarbone clause to carry it
99
+ // alone.
33
100
  // 2. The literal-reading law does NOT fire on it. This was the real risk and
34
101
  // it is ours, not the source corpus's: `references-read-literally.md` says
35
102
  // a baked-in property is read as a property of the SUBJECT, and this panel
@@ -65,11 +132,12 @@ import { renderStyleInstruction } from './style-library.js';
65
132
  * that cannot match the portrait's, so the sheet would carry two competing
66
133
  * identities and the model averages them. A back view has no face to compete
67
134
  * with, and it is the only panel where hair fall reads. See the header comment
68
- * for the receipt and for why the phrasing must stay framing, not removal.
135
+ * for the receipt, and for why the phrasing must stay FRAMING rather than
136
+ * removal and must be SCOPED TO THE FACE rather than to the whole body.
69
137
  */
70
138
  export const CHARACTER_SHEET_PANELS_DESC = 'a large chest-up portrait on the left at a three-quarter angle (never dead-on), ' +
71
- 'a full-body front view in a relaxed A-pose in the centre, framed from the collarbone down with the head not shown — ' +
72
- 'an invisible-mannequin presentation where the clothing holds its own shape, ' +
139
+ 'a full-body front view in a relaxed A-pose in the centre, cropped at the collarbone — ' +
140
+ 'an invisible-mannequin presentation with just the face cropped out, ' +
73
141
  'and a full-body back view on the right with the head and hair fully visible';
74
142
  /** Panel identifiers, in sheet order. */
75
143
  export const BODY_POSE_LABELS = ['portrait', 'front', 'back'];
@@ -89,8 +157,9 @@ export function buildCharacterIdentityPrompt(userStyle) {
89
157
  `${CHARACTER_SHEET_PANELS_DESC}. ` +
90
158
  `The portrait is the largest panel and occupies roughly a quarter to a third of the sheet — it is the sole authority for the face, so render it at maximum facial detail. ` +
91
159
  `No second rendering of the face anywhere on the sheet. ` +
92
- `Neutral expression and identical appearance, wardrobe and hair across all three panels. ` +
160
+ `A slight natural smile with the teeth just visible, and identical appearance, wardrobe and hair across all three panels. ` +
93
161
  `${styleDirective(userStyle)} ${IDENTITY_LIGHTING_CLAUSE} ${IDENTITY_CRAFT_CLAUSE} ` +
162
+ `For non-human characters, use a natural neutral expression instead of a smile. ` +
94
163
  `For quadruped or non-bipedal characters, replace the A-pose with a natural standing stance, show the whole animal including the head on both body panels, and keep the same three-panel layout. ` +
95
164
  `No text, no labels, no captions, no panel borders.`);
96
165
  }
@@ -1,7 +1,7 @@
1
1
  export interface ModelFact {
2
2
  id: string;
3
3
  label: string;
4
- kind: 'image' | 'video';
4
+ kind: 'image' | 'video' | 'audio';
5
5
  /** Max reference images (image models) — null if not applicable. */
6
6
  maxRefImages: number | null;
7
7
  /** Max ingredient images (video models) — null if not applicable. */
@@ -103,6 +103,38 @@ export const MODEL_FACTS = [
103
103
  maxIngredients: 0, // prompt + source clip ONLY — no element/style refs on this endpoint
104
104
  notes: 'VIDEO-TO-VIDEO EDIT, prompt-only — THE EDIT-FIDELITY WINNER (7/09 head-to-head vs Kling edit on real talking footage: lips held perfectly, audio near-identical, both action beats landed). Takes an EXISTING 3-10s clip and changes what the prompt names, footage-synced (prop/effect/environment/lighting swaps). Fidelity is EARNED by prompt discipline: ONE short instruction + "Keep everything else the same." — long descriptive prompts DESTROY it (Google-documented + 7/09 receipt). Never name objects as metaphors ("candle-like" → literal candle). Quirk: occasional tail jitter/doubled last speech beat — trim the tail. NO reference images (identity swaps needing refs → Kling edit); bit-exact audio needs → Kling keep_audio or segment-splice. 720p output, cheapest edit seat (~2/3 of Kling edit Std).',
105
105
  },
106
+ {
107
+ id: 'seed-audio',
108
+ label: 'Seed Audio 1.0',
109
+ kind: 'audio',
110
+ maxRefImages: 1, // ONE image XOR up to 3 audio clips — the two inputs are mutually exclusive.
111
+ maxIngredients: null,
112
+ notes: 'DEFAULT audio model — the one-pass SCENE workhorse: dialogue, SFX, and ambience together from ONE plain sentence. Route here for continuity beds, room tone, crowd/nature soundscapes, and quick scratch VO. AUDIO-ONLY: cannot generate images or video. 🚨 THERE IS NO DURATION PARAMETER — length comes from the words, so you MUST NAME THE LENGTH IN THE PROMPT TEXT ("... 15 seconds"). Slates appends the requested length automatically and BILLS the requested seconds, so a prompt that fights the number wastes credits. Prompts are ONE plain sentence, no production jargon and no SFX:/Ambient: prefixes (those are Kling syntax and hurt here). Say the crowd size out loud — "applause" returns a full room when the joke was three people. 1-120s. Inputs: ONE image (describe-what-you-see scoring) XOR up to 3 audio clips referenced in the prompt as @Audio1-@Audio3, never both. 20 preset voices, or leave voice unset and let the scene cast itself.',
113
+ },
114
+ {
115
+ id: 'eleven-v3',
116
+ label: 'ElevenLabs Eleven v3 (TTS)',
117
+ kind: 'audio',
118
+ maxRefImages: null,
119
+ maxIngredients: null,
120
+ notes: 'CONTROLLED, REPEATABLE named-voice VOICEOVER — route here whenever the exact words matter and must be re-renderable in the same voice (ad reads, narration, character lines to lip-sync against). AUDIO-ONLY. The text field IS the script: it is spoken verbatim, so never put stage directions in it. 1-5000 characters, billed per 100-character bucket, so trimming a sentence genuinely saves credits. 20 preset voices (Rachel default) — pick one and keep it for the whole piece. stability 0-1 trades consistency against expressiveness (low = more emotive and more variable). No voice cloning on this route. Word-level timestamps come back free and are what a future caption pass consumes. For scene ambience or SFX rather than speech, use seed-audio / eleven-sfx.',
121
+ },
122
+ {
123
+ id: 'eleven-sfx',
124
+ label: 'ElevenLabs Sound Effects v2',
125
+ kind: 'audio',
126
+ maxRefImages: null,
127
+ maxIngredients: null,
128
+ notes: 'ONE-SHOT SOUND EFFECT with an EXACT duration — route here for a single hit that must land on a frame (door slam, whoosh, impact, UI blip) or for a seamless loop. AUDIO-ONLY. 0.5-22s, and Slates always sends the duration explicitly (a null duration means a non-deterministic charge, so it is never left to the model). Describe the physical CAUSE, not the label: "heavy oak door slams shut in a stone hallway" beats "door sound". Text caps at 450 characters. loop=true produces a seamless bed. prompt_influence 0-1: higher hugs the prompt with less variation, lower explores. For layered scenes with dialogue or room tone, seed-audio does it in one pass instead.',
129
+ },
130
+ {
131
+ id: 'suno',
132
+ label: 'Suno',
133
+ kind: 'audio',
134
+ maxRefImages: null,
135
+ maxIngredients: null,
136
+ notes: 'FULL MUSIC TRACKS with structure — route here for anything a listener would call a song or a score. AUDIO-ONLY. EVERY call returns TWO variations for one flat price, and duration is FREE up to 360s (measured 2026-07-31: a 240s track costs the same as a default one), which makes it the cheapest way to get a bed that outlasts a cut. Two modes: DESCRIPTION mode (customMode=false, prompt is a <=500-char description and the lyrics get written for you) and CUSTOM mode (customMode=true, needs style + title; prompt then holds the EXACT LYRICS, sung as written — never put a description there). instrumental=true scores a scene with no vocals. Steer with style/negativeTags rather than piling adjectives into the prompt. duration 10-360s is V5_5 + custom mode only. Unofficial API wrapper, so treat availability as best-effort.',
137
+ },
106
138
  ];
107
139
  const FACT_BY_ID = new Map(MODEL_FACTS.map((m) => [m.id, m]));
108
140
  export function getModelFact(id) {
@@ -16,7 +16,7 @@ export interface PromptingTipsEntry {
16
16
  /** Footer callout paragraphs. */
17
17
  footer?: string[];
18
18
  }
19
- export type PromptingTipsKey = 'seedance' | 'kling' | 'kling-edit' | 'veo' | 'omni-flash' | 'omni-flash-edit' | 'nano-banana' | 'nano-banana-lite';
19
+ export type PromptingTipsKey = 'seedance' | 'kling' | 'kling-edit' | 'veo' | 'omni-flash' | 'omni-flash-edit' | 'nano-banana' | 'nano-banana-lite' | 'seed-audio' | 'eleven-v3' | 'eleven-sfx' | 'suno';
20
20
  export declare const PROMPTING_TIPS: Record<PromptingTipsKey, PromptingTipsEntry>;
21
21
  /** Null when no tips exist for the key — callers render an honest fallback. */
22
22
  export declare function getPromptingTips(key: string): PromptingTipsEntry | null;
@@ -359,6 +359,196 @@ const NANO_BANANA_LITE = {
359
359
  : card),
360
360
  ],
361
361
  };
362
+ // ── Audio lane ──────────────────────────────────────────────────
363
+ // The four audio surfaces prompt NOTHING like the video models. The single
364
+ // most expensive mistake is bringing Kling's "SFX:" / "Ambient noise:" syntax
365
+ // to Seed Audio, which reads it as literal text. Every entry below leads with
366
+ // what the surface actually wants.
367
+ const SEED_AUDIO = {
368
+ label: 'Seed Audio 1.0',
369
+ intro: [
370
+ 'Seed Audio builds a whole audio scene — dialogue, effects and ambience together — from one plain sentence. Write it the way you would describe the moment to a person standing next to you, not the way you would write a video prompt.',
371
+ 'It has no duration setting. Length comes from the words, so Slates appends your chosen duration to the prompt ("… 15 seconds") and bills exactly that. Set the duration control to what you actually want and let the sentence stay clean.',
372
+ ],
373
+ columns: [
374
+ [
375
+ {
376
+ heading: 'One plain sentence',
377
+ example: 'nature soundscape, wide open field cicadas and birds and a loon.',
378
+ note: 'No shot language, no production jargon, no formatting. Plain description outperforms anything that reads like a spec sheet.',
379
+ },
380
+ {
381
+ heading: 'Duration lives in the prompt',
382
+ example: 'tiny applause of 2 or 3 people at an open mic. 15 seconds',
383
+ note: 'The duration control writes this for you. Do not also type a different length into your sentence — the two will fight and you pay for the one you selected.',
384
+ critical: true,
385
+ },
386
+ {
387
+ heading: 'Say the crowd size',
388
+ example: 'tiny applause of 2 or 3 people · a packed arena roaring',
389
+ note: '"Applause" alone returns a full room. Scale words are the single highest-leverage edit on any crowd, traffic or nature bed.',
390
+ },
391
+ {
392
+ heading: 'Cut beds longer than the shot',
393
+ note: 'Ask for a few seconds more than the clip needs so the edit has handles to fade in and out of. Beds that end exactly on the cut always sound clipped.',
394
+ },
395
+ ],
396
+ [
397
+ {
398
+ heading: 'No Kling syntax here',
399
+ example: '✗ SFX: heavy boots\n✓ heavy boots on wet pavement, a siren far off',
400
+ note: 'The "SFX:" and "Ambient noise:" prefixes belong to Kling video prompts. Seed Audio treats them as words in the scene and the result gets worse.',
401
+ critical: true,
402
+ },
403
+ {
404
+ heading: 'Dialogue in quotes',
405
+ example: 'a tired bartender says, "we closed twenty minutes ago", glasses clinking behind him',
406
+ note: 'Speech goes in quotes inside the same sentence as the room. Pick a preset voice for a specific speaker, or leave it unset and let the scene cast itself.',
407
+ },
408
+ {
409
+ heading: 'References',
410
+ example: 'match the room tone of @Audio1',
411
+ note: 'Up to 3 audio clips (max 30s each), referenced as @Audio1–@Audio3 — OR one image to score what is in frame. Never both in the same generation.',
412
+ },
413
+ {
414
+ heading: 'Know its seat',
415
+ note: 'Scenes, beds and room tone in one pass. For an exact script in a repeatable voice use Eleven v3; for a single effect on a specific frame use Sound Effects; for a song use Suno.',
416
+ },
417
+ ],
418
+ ],
419
+ };
420
+ const ELEVEN_V3 = {
421
+ label: 'ElevenLabs Eleven v3',
422
+ intro: [
423
+ 'Eleven v3 speaks your text verbatim in a named voice. It is the controlled, repeatable lane: the same text and the same voice give you a read you can regenerate after a script tweak without the performance drifting.',
424
+ 'Billing is per 100 characters of text, rounded up — so tightening a sentence genuinely costs less, and a stray pasted paragraph genuinely costs more.',
425
+ ],
426
+ columns: [
427
+ [
428
+ {
429
+ heading: 'The text field is the script',
430
+ example: '✗ (excited) Say this fast: Grab yours today!\n✓ Grab yours today!',
431
+ note: 'Everything you type gets spoken. Stage directions, character names and bracketed notes will be read out loud.',
432
+ critical: true,
433
+ },
434
+ {
435
+ heading: 'Punctuate for pace',
436
+ example: 'It works. Every time. · It works — every time…',
437
+ note: 'Full stops, commas, dashes and ellipses are your only timing controls. Rewrite the punctuation before you touch the settings.',
438
+ },
439
+ {
440
+ heading: 'Pick a voice and stay',
441
+ note: '20 preset voices. Choosing one per character or per piece is what makes a series sound deliberate; swapping voices mid-piece reads as a mistake.',
442
+ },
443
+ ],
444
+ [
445
+ {
446
+ heading: 'Stability',
447
+ example: '0.3 emotive · 0.5 default · 0.8 steady',
448
+ note: 'Lower is more expressive and more variable take-to-take. Higher is flatter and more repeatable. Raise it for long narration, lower it for a single dramatic line.',
449
+ },
450
+ {
451
+ heading: 'Spell out the tricky bits',
452
+ example: 'SKU → "ess kay you" · 2026 → "twenty twenty six"',
453
+ note: 'Acronyms, product names, prices and years are where TTS embarrasses itself. Write the pronunciation you want.',
454
+ },
455
+ {
456
+ heading: 'Know its seat',
457
+ note: 'Exact words, repeatable voice, lines you will lip-sync against. Ambience, crowds and rooms belong to Seed Audio; one-shot effects to Sound Effects.',
458
+ },
459
+ ],
460
+ ],
461
+ footer: [
462
+ 'Word-level timestamps come back with every generation at no extra cost — that is what a future caption pass will read, so there is no reason to turn them off.',
463
+ ],
464
+ };
465
+ const ELEVEN_SFX = {
466
+ label: 'ElevenLabs Sound Effects',
467
+ intro: [
468
+ 'Sound Effects makes one short sound with an exact length — the lane for a hit that has to land on a specific frame, or a seamless loop you can lay under a whole scene.',
469
+ 'Duration is always sent explicitly (0.5–22s). Billing is per second, so the length you pick is the price you pay.',
470
+ ],
471
+ columns: [
472
+ [
473
+ {
474
+ heading: 'Describe the cause, not the label',
475
+ example: '✗ door sound\n✓ heavy oak door slams shut in a stone hallway',
476
+ note: 'Material, weight and room are what separate a usable effect from a stock-library shrug. Name all three.',
477
+ critical: true,
478
+ },
479
+ {
480
+ heading: 'One sound per generation',
481
+ note: 'This surface makes a single event. A door, then footsteps, then a siren is three generations layered on the timeline — or one Seed Audio scene.',
482
+ },
483
+ {
484
+ heading: 'Duration is the edit',
485
+ example: '0.8s for an impact · 4s for a whoosh · 22s for a bed',
486
+ note: 'Ask for roughly the length you need. A 4-second request for a door slam pads the tail with room tone you then have to trim.',
487
+ },
488
+ ],
489
+ [
490
+ {
491
+ heading: 'Loops',
492
+ example: 'steady rain on a canvas tent (loop on, 12s)',
493
+ note: 'Turn loop on for anything continuous — rain, engine hum, crowd murmur — and it will tile without a seam.',
494
+ },
495
+ {
496
+ heading: 'Prompt influence',
497
+ example: '0.3 default · 0.7 literal',
498
+ note: 'Higher hugs your wording with less variation between takes; lower explores. Raise it when a re-roll keeps wandering off the brief.',
499
+ },
500
+ {
501
+ heading: 'Know its seat',
502
+ note: 'One precise effect on a known frame. Full rooms and layered scenes are cheaper and better in one Seed Audio pass.',
503
+ },
504
+ ],
505
+ ],
506
+ };
507
+ const SUNO = {
508
+ label: 'Suno',
509
+ intro: [
510
+ 'Suno writes full music. Every generation returns TWO variations for one flat price, and length is free up to six minutes — so there is never a reason to generate a bed that is shorter than your edit.',
511
+ 'The one thing to get right is which mode you are in, because it changes what the prompt field means.',
512
+ ],
513
+ columns: [
514
+ [
515
+ {
516
+ heading: 'Description mode',
517
+ example: 'brooding synthwave for a night drive, analog bass, no vocals',
518
+ note: 'The prompt is a description (max 500 characters) and the lyrics get written for you. This is the fast path when you just need a mood.',
519
+ },
520
+ {
521
+ heading: 'Custom mode — the prompt IS the lyrics',
522
+ example: 'Style: dream pop, hazy\nTitle: Blue Hour\nPrompt: [Verse 1] The lights come on…',
523
+ note: 'In custom mode the prompt is sung exactly as written. Putting a description there gets your description sung back at you.',
524
+ critical: true,
525
+ },
526
+ {
527
+ heading: 'Instrumental',
528
+ note: 'Turn instrumental on to score a scene with no vocals — style and title still steer it, and the prompt field is ignored.',
529
+ },
530
+ ],
531
+ [
532
+ {
533
+ heading: 'Steer with style, not adjectives',
534
+ example: 'Style: 90s trip-hop, dusty breakbeat, Rhodes\nAvoid: brass, EDM drops',
535
+ note: 'Genre, era, instrumentation and tempo belong in the style field. Negative tags remove what keeps creeping in.',
536
+ },
537
+ {
538
+ heading: 'Length is free',
539
+ example: '10–360 seconds',
540
+ note: 'A six-minute track costs exactly what a default one does. Ask for longer than the cut needs and trim on the timeline.',
541
+ },
542
+ {
543
+ heading: 'Two songs, both yours',
544
+ note: 'Both variations land in the gallery. They are genuinely different takes on the same brief — audition both before re-rolling.',
545
+ },
546
+ ],
547
+ ],
548
+ footer: [
549
+ 'Files are hosted by the provider for a limited window — Slates downloads and stores them on your machine as soon as the track finishes, so nothing expires out from under a project.',
550
+ ],
551
+ };
362
552
  export const PROMPTING_TIPS = {
363
553
  seedance: SEEDANCE,
364
554
  kling: KLING,
@@ -368,6 +558,10 @@ export const PROMPTING_TIPS = {
368
558
  'omni-flash-edit': OMNI_FLASH_EDIT,
369
559
  'nano-banana': NANO_BANANA,
370
560
  'nano-banana-lite': NANO_BANANA_LITE,
561
+ 'seed-audio': SEED_AUDIO,
562
+ 'eleven-v3': ELEVEN_V3,
563
+ 'eleven-sfx': ELEVEN_SFX,
564
+ suno: SUNO,
371
565
  };
372
566
  /** Null when no tips exist for the key — callers render an honest fallback. */
373
567
  export function getPromptingTips(key) {
@@ -20,8 +20,25 @@ export declare const IDENTITY_LIGHTING = "flat, even, shadowless lighting";
20
20
  * crushes hair and wardrobe silhouettes.** A deep neutral grey holds both.
21
21
  */
22
22
  export declare const IDENTITY_PLATE_HEX = "#3a3a3c";
23
- export declare const IDENTITY_BACKGROUND = "a plain, deep neutral-grey background (#3a3a3c)";
24
- export declare const IDENTITY_LIGHTING_CLAUSE = "Render on a plain, deep neutral-grey background (#3a3a3c) with flat, even, shadowless lighting so the sheet captures the character's identity, not scene lighting.";
23
+ /**
24
+ * 🚨 THE HEX IS EMITTED WITHOUT ITS `#` AND THAT IS LOAD-BEARING (2026-07-30).
25
+ *
26
+ * `#` is a REFERENCE-TOKEN SIGIL in the desktop's prompt composer
27
+ * (`slate/src/shared/promptComposition.ts` — `/([@#])([\w-]+)/g`), and an
28
+ * unresolved `#token` is SILENTLY DELETED. So `#3a3a3c` never survived to any
29
+ * model: fal echoed back `deep neutral-grey background ()` on a real 2026-07-30
30
+ * request. The plate value has been doing nothing since the composer shipped.
31
+ *
32
+ * Writing it bare keeps the value in the prompt. `IDENTITY_PLATE_HEX` keeps its
33
+ * `#` because it is a colour constant and may have non-prompt consumers.
34
+ *
35
+ * GENERAL RULE FOR THIS FILE: never emit `#` or `@` into prompt text. Both are
36
+ * sigils downstream and both mangle silently — no error, no log, just missing
37
+ * words. HOW YOU'D KNOW THIS IS BEATEN: the composer stops treating bare
38
+ * `#hex` as a token, or starts leaving unresolved tokens intact.
39
+ */
40
+ export declare const IDENTITY_BACKGROUND: string;
41
+ export declare const IDENTITY_LIGHTING_CLAUSE: string;
25
42
  /**
26
43
  * Craft clauses every identity reference wants — the eye and skin detail that
27
44
  * survives downstream, plus the two "reads literally" guards. Crushed-black
@@ -96,7 +96,24 @@ export const IDENTITY_LIGHTING = 'flat, even, shadowless lighting';
96
96
  * crushes hair and wardrobe silhouettes.** A deep neutral grey holds both.
97
97
  */
98
98
  export const IDENTITY_PLATE_HEX = '#3a3a3c';
99
- export const IDENTITY_BACKGROUND = `a plain, deep neutral-grey background (${IDENTITY_PLATE_HEX})`;
99
+ /**
100
+ * 🚨 THE HEX IS EMITTED WITHOUT ITS `#` AND THAT IS LOAD-BEARING (2026-07-30).
101
+ *
102
+ * `#` is a REFERENCE-TOKEN SIGIL in the desktop's prompt composer
103
+ * (`slate/src/shared/promptComposition.ts` — `/([@#])([\w-]+)/g`), and an
104
+ * unresolved `#token` is SILENTLY DELETED. So `#3a3a3c` never survived to any
105
+ * model: fal echoed back `deep neutral-grey background ()` on a real 2026-07-30
106
+ * request. The plate value has been doing nothing since the composer shipped.
107
+ *
108
+ * Writing it bare keeps the value in the prompt. `IDENTITY_PLATE_HEX` keeps its
109
+ * `#` because it is a colour constant and may have non-prompt consumers.
110
+ *
111
+ * GENERAL RULE FOR THIS FILE: never emit `#` or `@` into prompt text. Both are
112
+ * sigils downstream and both mangle silently — no error, no log, just missing
113
+ * words. HOW YOU'D KNOW THIS IS BEATEN: the composer stops treating bare
114
+ * `#hex` as a token, or starts leaving unresolved tokens intact.
115
+ */
116
+ export const IDENTITY_BACKGROUND = `a plain, deep neutral-grey background (hex ${IDENTITY_PLATE_HEX.replace('#', '')})`;
100
117
  export const IDENTITY_LIGHTING_CLAUSE = `Render on ${IDENTITY_BACKGROUND} with ${IDENTITY_LIGHTING} so the sheet captures the character's identity, not scene lighting.`;
101
118
  /**
102
119
  * Craft clauses every identity reference wants — the eye and skin detail that