@slatesvideo/shared 0.5.3 → 0.5.4
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/README.md +18 -0
- package/dist/operations/index.d.ts +1 -0
- package/dist/operations/index.js +7 -2
- package/dist/prompts/character-sheet.d.ts +24 -12
- package/dist/prompts/character-sheet.js +78 -28
- package/dist/prompts/environment-sheet.d.ts +9 -1
- package/dist/prompts/environment-sheet.js +17 -3
- package/dist/prompts/model-facts.js +4 -1
- package/dist/prompts/partials.generated.d.ts +2 -0
- package/dist/prompts/partials.generated.js +14 -0
- package/dist/prompts/prompting-tips.js +49 -18
- package/dist/prompts/reference-rules.d.ts +43 -14
- package/dist/prompts/reference-rules.js +50 -26
- package/dist/skills/content.js +12 -12
- package/package.json +4 -3
- package/skills/_partials/decision-log.md +12 -0
- package/skills/_partials/reference-rules-core.md +12 -0
- package/skills/_partials/reference-tips-short.md +2 -0
- package/skills/_partials/references-read-literally.md +11 -0
- package/skills/_partials/still-gate.md +3 -0
- package/skills/slates-character-turnaround.md +64 -29
- package/skills/slates-cost-discipline.md +10 -0
- package/skills/slates-edit-and-iterate.md +16 -1
- package/skills/slates-model-selection.md +24 -1
- package/skills/slates-one-prompt-film.md +19 -0
- package/skills/slates-prompting-flux-2-max.md +36 -5
- package/skills/slates-prompting-kling-v3.md +33 -4
- package/skills/slates-prompting-nano-banana-2.md +40 -10
- package/skills/slates-prompting-seedance.md +284 -85
- package/skills/slates-prompting-veo-3.md +33 -4
- package/skills/slates-storyboard-from-script.md +19 -0
- package/skills/slates-vision-feedback-loop.md +49 -2
package/README.md
CHANGED
|
@@ -3,3 +3,21 @@
|
|
|
3
3
|
Internal shared layer for the [Slates](https://slates.video) MCP server and CLI: the auth/connection-file reader, the cloud and desktop HTTP clients, and the single operations array both surfaces register. You almost certainly want [@slatesvideo/mcp-server](https://www.npmjs.com/package/@slatesvideo/mcp-server) (MCP clients like Claude Desktop and Cursor) or [@slatesvideo/cli](https://www.npmjs.com/package/@slatesvideo/cli) (the `slates` command) instead — this package is published only as their dependency.
|
|
4
4
|
|
|
5
5
|
Source: [github.com/EricDisero/slates-mcp](https://github.com/EricDisero/slates-mcp)
|
|
6
|
+
|
|
7
|
+
## Prompt partials — how shared prompting prose stays de-forked
|
|
8
|
+
|
|
9
|
+
Prompting doctrine that applies to more than one model lives **once**, in `skills/_partials/<name>.md`. Every surface that needs it derives:
|
|
10
|
+
|
|
11
|
+
- **Skill markdown** (`skills/*.md`) carries a marker pair; `scripts/sync-partials.mjs` writes the resolved text between them and commits it, so each de-fork is visible in review and `install-skills` can keep writing the files verbatim to disk.
|
|
12
|
+
|
|
13
|
+
```markdown
|
|
14
|
+
<!-- @inject:reference-rules-core -->
|
|
15
|
+
…generated; do not edit between the markers…
|
|
16
|
+
<!-- @end:reference-rules-core -->
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **TypeScript** reads the same text from the generated `PARTIALS` record (`src/prompts/partials.generated.ts`) — that is how `REFERENCE_RULES_TEXT` and the doctrine-bearing cards in `prompting-tips.ts` are built.
|
|
20
|
+
|
|
21
|
+
`npm run build` runs `sync-partials.mjs --check`, which fails with a diff if anything inside a marker block was hand-edited, if a skill references a partial that doesn't exist, if markers are unbalanced, or if a partial is referenced by nothing. Run `npm run sync-partials` to apply changes after editing a partial.
|
|
22
|
+
|
|
23
|
+
**To change shared prompting doctrine: edit the partial.** Never edit between markers, never edit `partials.generated.ts`, and never hand-copy a shared rule into a new skill — a copy is a fork with a delay fuse. Per-model levers (a vendor's own official consistency mechanism, its caps and transport quirks) stay hand-authored, below the injected block, under a `### For <model> specifically` heading.
|
|
@@ -112,6 +112,7 @@ export declare const generateCharacterSheets: Operation<{
|
|
|
112
112
|
baseAssetId: string;
|
|
113
113
|
sheetTypes?: ('turnaround' | 'expression')[];
|
|
114
114
|
userNotes?: string;
|
|
115
|
+
model?: 'nano-banana-2' | 'nano-banana-2-lite' | 'nano-banana-pro' | 'gpt-image-2';
|
|
115
116
|
}>;
|
|
116
117
|
export declare const generateEnvironmentPlate: Operation<{
|
|
117
118
|
environmentId: string;
|
package/dist/operations/index.js
CHANGED
|
@@ -580,7 +580,7 @@ export const createEnvironment = {
|
|
|
580
580
|
};
|
|
581
581
|
export const generateCharacterSheets = {
|
|
582
582
|
id: 'slates_generate_character_sheets',
|
|
583
|
-
description: "Generate a character's
|
|
583
|
+
description: "Generate a character's identity reference sheet from a base portrait asset and bind it to the character. THE real character-building workflow — call right after slates_create_character. ONE sheet per character by default: a dominant three-quarter chest-up portrait (the sole face authority) plus full-body front and back panels on a deep neutral-grey plate — it binds to the turnaround slot and the expression slot stays null. That is deliberate: every bound sheet costs a reference slot on EVERY downstream shot, so one sheet per character doubles the cast you can stage against real caps (Kling 3.0 takes 4 ingredients, NB2 4 character slots, Seedance 9). Pass sheetTypes:['expression'] only when a character genuinely needs a separate expression range. baseAssetId is the source portrait (a project asset). Afterward, carry the character into a scene by passing its bound sheet asset id as characterAssetIds to slates_generate_video (or referenceAssetIds to slates_generate_image). Read slates-character-turnaround before calling. Default nano-banana-2 @ 2K — quote the price from slates_estimate_generation_cost, never from memory.",
|
|
584
584
|
input: z.object({
|
|
585
585
|
characterId: z.string().uuid(),
|
|
586
586
|
projectId: z.string().uuid(),
|
|
@@ -588,8 +588,12 @@ export const generateCharacterSheets = {
|
|
|
588
588
|
sheetTypes: z
|
|
589
589
|
.array(z.enum(['turnaround', 'expression']))
|
|
590
590
|
.optional()
|
|
591
|
-
.describe(
|
|
591
|
+
.describe("Which sheets to generate. Default ['turnaround'] — the single identity sheet. Add 'expression' only when the character needs a dedicated expression range; it costs a second reference slot on every downstream generation."),
|
|
592
592
|
userNotes: z.string().optional().describe('Extra instruction, e.g. "use the woman on the left".'),
|
|
593
|
+
model: z
|
|
594
|
+
.enum(['nano-banana-2', 'nano-banana-2-lite', 'nano-banana-pro', 'gpt-image-2'])
|
|
595
|
+
.optional()
|
|
596
|
+
.describe('Image model for the sheet. Omit for the default (nano-banana-2). Exists so the layout-vs-face tradeoff can be tested with comparison gens — do not switch without a receipt.'),
|
|
593
597
|
}),
|
|
594
598
|
async run(input, ctx) {
|
|
595
599
|
return ok(await ctx.desktop().post('/agent/characters/generate-sheets', {
|
|
@@ -598,6 +602,7 @@ export const generateCharacterSheets = {
|
|
|
598
602
|
baseAssetIds: [input.baseAssetId],
|
|
599
603
|
sheetTypes: input.sheetTypes,
|
|
600
604
|
userNotes: input.userNotes,
|
|
605
|
+
model: input.model,
|
|
601
606
|
}));
|
|
602
607
|
},
|
|
603
608
|
};
|
|
@@ -1,24 +1,36 @@
|
|
|
1
|
-
/** Turnaround = the IDENTITY reference. 4 neutral full-body angles. */
|
|
2
|
-
export declare const CHARACTER_ANGLES_DESC = "front view, back view, left side profile, right side profile";
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The identity sheet's panels. The portrait is FIRST and DOMINANT — every
|
|
3
|
+
* detail the downstream model will ever know about the face comes from those
|
|
4
|
+
* pixels, so it gets the resolution. The body panels exist for build,
|
|
5
|
+
* proportion, wardrobe and hair, not for the face.
|
|
6
|
+
*
|
|
7
|
+
* The back panel KEEPS its head on purpose: it has no face to compete with the
|
|
8
|
+
* portrait, and it is the only panel where hair fall reads.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CHARACTER_SHEET_PANELS_DESC: string;
|
|
11
|
+
/** Panel identifiers, in sheet order. */
|
|
12
|
+
export declare const BODY_POSE_LABELS: readonly ["portrait", "front", "back"];
|
|
13
|
+
/**
|
|
14
|
+
* Expression-sheet close-ups — LEGACY. Kept so characters built before the
|
|
15
|
+
* 2026-07-21 single-sheet architecture keep regenerating correctly, and so the
|
|
16
|
+
* expression slot remains usable for a character that genuinely needs a
|
|
17
|
+
* dedicated expression range.
|
|
7
18
|
*/
|
|
8
19
|
export declare const CHARACTER_EXPRESSIONS_DESC = "neutral expression on left, genuine smile showing teeth in center, serious frown on right";
|
|
9
|
-
export declare const BODY_POSE_LABELS: readonly ["front", "back", "profile-left", "profile-right"];
|
|
10
20
|
export declare const EXPRESSION_LABELS: readonly ["neutral", "smile", "serious"];
|
|
11
21
|
/**
|
|
12
|
-
*
|
|
22
|
+
* The character identity sheet — one asset, three panels, bound to the
|
|
23
|
+
* turnaround slot. Named `buildCharacterTurnaroundPrompt` for continuity with
|
|
24
|
+
* every existing caller and with the slot it binds to.
|
|
25
|
+
*
|
|
13
26
|
* @param userStyle optional natural-language style transform (e.g. "make her a real person")
|
|
14
27
|
*/
|
|
15
28
|
export declare function buildCharacterTurnaroundPrompt(userStyle?: string | null): string;
|
|
16
29
|
/**
|
|
17
|
-
* Expression sheet —
|
|
18
|
-
*
|
|
19
|
-
* character
|
|
20
|
-
*
|
|
21
|
-
* expressions don't average the generated face.
|
|
30
|
+
* Expression sheet — LEGACY close-up face reference. Since 2026-07-21 the
|
|
31
|
+
* identity sheet above is the default and this slot is normally left null.
|
|
32
|
+
* Generate one only when a character needs an explicit expression range;
|
|
33
|
+
* attaching it costs a second reference slot on every generation.
|
|
22
34
|
*/
|
|
23
35
|
export declare function buildExpressionSheetPrompt(userStyle?: string | null): string;
|
|
24
36
|
//# sourceMappingURL=character-sheet.d.ts.map
|
|
@@ -1,24 +1,70 @@
|
|
|
1
|
-
// Canonical character-sheet prompt content.
|
|
2
|
-
// medium, render on a flat-lit plain background, optional natural-language
|
|
3
|
-
// style transform. (Replaces the old photorealistic / match-reference fork
|
|
4
|
-
// that baked studio lighting or a scene's lighting into the identity ref.)
|
|
1
|
+
// Canonical character-sheet prompt content.
|
|
5
2
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
3
|
+
// ARCHITECTURE (locked by Eric 2026-07-21): ONE identity sheet per character.
|
|
4
|
+
// A dominant off-frontal chest-up portrait carries the face; two full-body
|
|
5
|
+
// panels (front + back) carry build, wardrobe and hair. The sheet binds to the
|
|
6
|
+
// character's TURNAROUND slot and the expression slot is left null.
|
|
7
|
+
//
|
|
8
|
+
// Why one sheet and not two — three arguments, none of which depend on a
|
|
9
|
+
// comparison generation:
|
|
10
|
+
// 1. Reference-cap economics. Every `@character` mention pushes BOTH bound
|
|
11
|
+
// sheets into one reference group, so a two-sheet character costs TWO
|
|
12
|
+
// reference slots on every generation. Against real caps that is brutal:
|
|
13
|
+
// Kling 3.0 takes 4 ingredients (2 characters, zero room for an
|
|
14
|
+
// environment), NB2 has 4 character slots, Seedance 9. One sheet each
|
|
15
|
+
// DOUBLES the cast you can stage on every model we route to.
|
|
16
|
+
// 2. Competing face renderings drop 6 → 2. The old pair sent three large
|
|
17
|
+
// portraits plus three postage-stamp faces (turnaround front + both
|
|
18
|
+
// profiles). The model cannot tell which rendering is authoritative and
|
|
19
|
+
// averages them; ByteDance documents the same root cause for its
|
|
20
|
+
// duplicate-character failure (ModelArk :1959) and prescribes fewer
|
|
21
|
+
// competing views. Both profile panels disappear with the shape change.
|
|
22
|
+
// 3. One generation instead of two per character — half the sheet spend, one
|
|
23
|
+
// asset to inspect and bind.
|
|
24
|
+
//
|
|
25
|
+
// KNOWN COST, accepted for v1: a neutral chest-up portrait carries no dental
|
|
26
|
+
// information, so a character who smiles in a shot gets invented teeth. The
|
|
27
|
+
// 2026-06-26 doctrine already holds that the user's prompt owns expression.
|
|
28
|
+
// Revisit only if a receipt shows invented teeth.
|
|
29
|
+
//
|
|
30
|
+
// DEFERRED, not rejected: cropping the face off the front body panel (the
|
|
31
|
+
// "ghost mannequin" treatment). Adopting this layout takes competing faces
|
|
32
|
+
// 6 → 2 for free; the crop buys only 2 → 1, and it is contradicted by the only
|
|
33
|
+
// visible output in the source corpus. Flipping it later is a one-line change
|
|
34
|
+
// here — no migration, no data touched.
|
|
35
|
+
//
|
|
36
|
+
// BACK-COMPAT IS MANDATORY: ~20 live users have characters bound to BOTH
|
|
37
|
+
// slots. `buildExpressionSheetPrompt` stays exported and the expression slot
|
|
38
|
+
// stays readable; `mentions.ts` only pushes non-null paths, so old two-sheet
|
|
39
|
+
// characters and new one-sheet characters both work unchanged.
|
|
40
|
+
//
|
|
41
|
+
// SOURCE OF TRUTH. The desktop imports these builders through
|
|
42
|
+
// `slate/src/shared/prompts/character-sheet.ts` (a thin re-export since 1.2.1)
|
|
43
|
+
// — there is no desktop prompt mirror to update. Map:
|
|
44
|
+
// second-brain business/projects/slates/product/prompting-ssot.md
|
|
45
|
+
import { IDENTITY_CRAFT_CLAUSE, IDENTITY_LIGHTING_CLAUSE, INHERIT_SOURCE_STYLE, } from './reference-rules.js';
|
|
12
46
|
import { renderStyleInstruction } from './style-library.js';
|
|
13
|
-
/** Turnaround = the IDENTITY reference. 4 neutral full-body angles. */
|
|
14
|
-
export const CHARACTER_ANGLES_DESC = 'front view, back view, left side profile, right side profile';
|
|
15
47
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
48
|
+
* The identity sheet's panels. The portrait is FIRST and DOMINANT — every
|
|
49
|
+
* detail the downstream model will ever know about the face comes from those
|
|
50
|
+
* pixels, so it gets the resolution. The body panels exist for build,
|
|
51
|
+
* proportion, wardrobe and hair, not for the face.
|
|
52
|
+
*
|
|
53
|
+
* The back panel KEEPS its head on purpose: it has no face to compete with the
|
|
54
|
+
* portrait, and it is the only panel where hair fall reads.
|
|
55
|
+
*/
|
|
56
|
+
export const CHARACTER_SHEET_PANELS_DESC = 'a large chest-up portrait on the left at a three-quarter angle (never dead-on), ' +
|
|
57
|
+
'a full-body front view in a relaxed A-pose in the centre, ' +
|
|
58
|
+
'and a full-body back view on the right';
|
|
59
|
+
/** Panel identifiers, in sheet order. */
|
|
60
|
+
export const BODY_POSE_LABELS = ['portrait', 'front', 'back'];
|
|
61
|
+
/**
|
|
62
|
+
* Expression-sheet close-ups — LEGACY. Kept so characters built before the
|
|
63
|
+
* 2026-07-21 single-sheet architecture keep regenerating correctly, and so the
|
|
64
|
+
* expression slot remains usable for a character that genuinely needs a
|
|
65
|
+
* dedicated expression range.
|
|
19
66
|
*/
|
|
20
67
|
export const CHARACTER_EXPRESSIONS_DESC = 'neutral expression on left, genuine smile showing teeth in center, serious frown on right';
|
|
21
|
-
export const BODY_POSE_LABELS = ['front', 'back', 'profile-left', 'profile-right'];
|
|
22
68
|
export const EXPRESSION_LABELS = ['neutral', 'smile', 'serious'];
|
|
23
69
|
// The sheet's style directive: a user transform REPLACES the inherit-source
|
|
24
70
|
// instruction (so the model isn't told to both preserve the medium AND change
|
|
@@ -27,27 +73,31 @@ function styleDirective(userStyle) {
|
|
|
27
73
|
return renderStyleInstruction(userStyle).trim() || INHERIT_SOURCE_STYLE;
|
|
28
74
|
}
|
|
29
75
|
/**
|
|
30
|
-
*
|
|
76
|
+
* The character identity sheet — one asset, three panels, bound to the
|
|
77
|
+
* turnaround slot. Named `buildCharacterTurnaroundPrompt` for continuity with
|
|
78
|
+
* every existing caller and with the slot it binds to.
|
|
79
|
+
*
|
|
31
80
|
* @param userStyle optional natural-language style transform (e.g. "make her a real person")
|
|
32
81
|
*/
|
|
33
82
|
export function buildCharacterTurnaroundPrompt(userStyle) {
|
|
34
|
-
return (`
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`
|
|
38
|
-
|
|
83
|
+
return (`A single character identity reference sheet of one character, three panels side by side on one plate: ` +
|
|
84
|
+
`${CHARACTER_SHEET_PANELS_DESC}. ` +
|
|
85
|
+
`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. ` +
|
|
86
|
+
`Neutral expression and identical appearance, wardrobe and hair across all three panels. ` +
|
|
87
|
+
`${styleDirective(userStyle)} ${IDENTITY_LIGHTING_CLAUSE} ${IDENTITY_CRAFT_CLAUSE} ` +
|
|
88
|
+
`For quadruped or non-bipedal characters, replace the A-pose with a natural standing stance and keep the same three-panel layout. ` +
|
|
89
|
+
`No text, no labels, no captions, no panel borders.`);
|
|
39
90
|
}
|
|
40
91
|
/**
|
|
41
|
-
* Expression sheet —
|
|
42
|
-
*
|
|
43
|
-
* character
|
|
44
|
-
*
|
|
45
|
-
* expressions don't average the generated face.
|
|
92
|
+
* Expression sheet — LEGACY close-up face reference. Since 2026-07-21 the
|
|
93
|
+
* identity sheet above is the default and this slot is normally left null.
|
|
94
|
+
* Generate one only when a character needs an explicit expression range;
|
|
95
|
+
* attaching it costs a second reference slot on every generation.
|
|
46
96
|
*/
|
|
47
97
|
export function buildExpressionSheetPrompt(userStyle) {
|
|
48
98
|
return (`Character expression reference sheet with 3 head and shoulder portraits arranged side by side horizontally: ${CHARACTER_EXPRESSIONS_DESC}. ` +
|
|
49
99
|
`Consistent character appearance across all three. ` +
|
|
50
|
-
`${styleDirective(userStyle)} ${IDENTITY_LIGHTING_CLAUSE} Same framing for each. ` +
|
|
100
|
+
`${styleDirective(userStyle)} ${IDENTITY_LIGHTING_CLAUSE} ${IDENTITY_CRAFT_CLAUSE} Same framing for each. ` +
|
|
51
101
|
`No text, no labels, no captions.`);
|
|
52
102
|
}
|
|
53
103
|
//# sourceMappingURL=character-sheet.js.map
|
|
@@ -5,5 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function buildEnvironmentEstablishingPrompt(userStyle?: string | null): string;
|
|
7
7
|
/** Guidance shown to users/agents: prefer describing the environment in text. */
|
|
8
|
-
export declare const ENVIRONMENT_DESCRIBE_FIRST
|
|
8
|
+
export declare const ENVIRONMENT_DESCRIBE_FIRST: string;
|
|
9
|
+
/**
|
|
10
|
+
* The continuity test for a location that must hold across shots: generate a
|
|
11
|
+
* SECOND frame from the reverse angle and approve the plate only if the anchor
|
|
12
|
+
* object, the openings, the light side, the materials and the palette all still
|
|
13
|
+
* match. A plate that fails this will drift the moment the camera turns — and a
|
|
14
|
+
* broken plate gives every later character failure a second plausible cause.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ENVIRONMENT_REVERSE_ANGLE_TEST = "Reverse-angle check: generate one frame from the opposite side of the space and confirm the anchor object, openings, light direction, materials and colour palette all still match. If they do not, the plate is not locked \u2014 fix the plate before staging anything in it.";
|
|
9
17
|
//# sourceMappingURL=environment-sheet.d.ts.map
|
|
@@ -16,12 +16,26 @@ function styleDirective(userStyle) {
|
|
|
16
16
|
* @param userStyle optional natural-language style transform
|
|
17
17
|
*/
|
|
18
18
|
export function buildEnvironmentEstablishingPrompt(userStyle) {
|
|
19
|
-
return (`A single clean establishing shot of this empty location
|
|
19
|
+
return (`A single clean establishing shot of this empty location, framed at a three-quarter angle — never a dead-on frontal view — so two walls or planes are visible and the floor reads as usable staging space. ` +
|
|
20
|
+
`Capture the space: its architecture or geography, materials, and depth. ` +
|
|
21
|
+
`Include a clearly readable ANCHOR OBJECT with a definite position in the room — a sofa, a doorway, a counter, a signpost — so later shots can be blocked against it. ` +
|
|
20
22
|
`The location is empty and unpopulated, ready for scene staging. ` +
|
|
21
|
-
`${styleDirective(userStyle)} Use ${ENVIRONMENT_NATURAL_LIGHT}. ` +
|
|
23
|
+
`${styleDirective(userStyle)} Use ${ENVIRONMENT_NATURAL_LIGHT}, motivated by ONE dominant source with shadows falling consistently away from it — soft and diffused for interiors, no hard visible light rays. ` +
|
|
24
|
+
`Add gentle atmospheric haze with distance so near and far planes separate in depth rather than reading at the same sharpness. ` +
|
|
22
25
|
`If the reference image contains people or characters, generate the location as an empty space — ignore the figures. ` +
|
|
23
26
|
`No text, no labels, no captions.`);
|
|
24
27
|
}
|
|
25
28
|
/** Guidance shown to users/agents: prefer describing the environment in text. */
|
|
26
|
-
export const ENVIRONMENT_DESCRIBE_FIRST = 'Default to describing the environment in words and let the model build it to fit the shot. Generate an establishing plate only when a location must be locked exactly across shots.'
|
|
29
|
+
export const ENVIRONMENT_DESCRIBE_FIRST = 'Default to describing the environment in words and let the model build it to fit the shot. Generate an establishing plate only when a location must be locked exactly across shots. ' +
|
|
30
|
+
'When you do: frame it three-quarter, never dead-on (a frontal facade turns the location into a backdrop characters stand in FRONT of; a three-quarter exposes side geometry and usable floor), ' +
|
|
31
|
+
'name an anchor object so blocking can be stated as "between the sofa\'s hall-side arm and the window" instead of "on the left" — one is testable after a camera turn, the other drifts, ' +
|
|
32
|
+
'give it one motivated light source with shadows falling away from it, and ask for atmospheric haze so depth separates.';
|
|
33
|
+
/**
|
|
34
|
+
* The continuity test for a location that must hold across shots: generate a
|
|
35
|
+
* SECOND frame from the reverse angle and approve the plate only if the anchor
|
|
36
|
+
* object, the openings, the light side, the materials and the palette all still
|
|
37
|
+
* match. A plate that fails this will drift the moment the camera turns — and a
|
|
38
|
+
* broken plate gives every later character failure a second plausible cause.
|
|
39
|
+
*/
|
|
40
|
+
export const ENVIRONMENT_REVERSE_ANGLE_TEST = 'Reverse-angle check: generate one frame from the opposite side of the space and confirm the anchor object, openings, light direction, materials and colour palette all still match. If they do not, the plate is not locked — fix the plate before staging anything in it.';
|
|
27
41
|
//# sourceMappingURL=environment-sheet.js.map
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
export const MODEL_FACTS = [
|
|
7
7
|
{
|
|
8
8
|
id: 'nano-banana-2',
|
|
9
|
-
|
|
9
|
+
// Gemini 3.1 FLASH Image — verified against the runtime slug map in
|
|
10
|
+
// slate/src/main/api/google.ts. Nano Banana PRO is a different model
|
|
11
|
+
// (gemini-3-pro-image-preview); do not conflate them.
|
|
12
|
+
label: 'Nano Banana 2 (Gemini 3.1 Flash Image)',
|
|
10
13
|
kind: 'image',
|
|
11
14
|
maxRefImages: 14, // 10 object-fidelity + 4 character-consistency; categories don't trade.
|
|
12
15
|
maxIngredients: null,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// GENERATED — do not edit. Source: packages/shared/skills/_partials/*.md
|
|
2
|
+
// Regenerated by scripts/sync-partials.mjs on every build.
|
|
3
|
+
//
|
|
4
|
+
// This is the SAME text injected between the @inject markers in the
|
|
5
|
+
// per-model skills, so the TS consumers and the markdown consumers can
|
|
6
|
+
// no longer disagree. Edit the partial, not this file, not the skills.
|
|
7
|
+
export const PARTIALS = {
|
|
8
|
+
"decision-log": "When you surface the plan, include a short **decision log** — one line per decision *you* made that the user did not specify:\n\n```\nsource phrase or declared default → what you wrote → what it resolves\n\"in a diner\" → chrome-and-vinyl booth, 3/4 on the counter → fixes the anchor so blocking is repeatable\n(no time of day) → late afternoon, low warm key → default; say the word and it changes\n(no camera) → slow push-in, single move → one move per shot; stacking increases instability\n```\n\n**Hard rule: never silently add weather, props, style, or camera movement.** If it wasn't in the brief and you added it, it goes in the log. This is the \"why did you add that?\" affordance — for an agent that writes prompts on the user's behalf and spends their credits, it is what keeps the model in assembly and the user in the director's chair.\n\n> ❌ **Do NOT turn this into a question gate.** Clarifying questions before optimizing directly fight the locked fast-path rule: *if intent is clear, generate immediately with sane defaults, don't ask questions; only ask for production intent, and batch every question into one message.* Log the decisions, then go. The log is an **output**, not an interrogation — surfaced alongside the plan, never as a separate ceremony, and never as a reason to wait.",
|
|
9
|
+
"reference-rules-core": "Identity = a few flat-lit neutral angles; one reference per role, named inline; 2-4 refs not 12; describe environments instead of feeding a grid.\n\n1. **2-4 strong references beat both extremes.** Not 1 (warps toward itself), not 12 (averages worse). Start with 2-3 focused refs — each one adds context AND another variable to balance.\n2. **One reference per ROLE, named in the prompt** — identity / style-grade / environment. The model does **not** infer a reference's role from its position in the list; the inline name carries it. Same-role competitors drift (two \"identity\" refs of different people blend into a third face). Slates composes the naming for you from your `@mentions` / `#tags` — you never hand-write role labels.\n3. **One identity sheet per character — and whatever you do attach for a subject, NAME it as one entity.** A character's identity sheet is a single asset (dominant portrait + body panels), so attach that one asset rather than a pile of views: **fewer competing renderings of a face is always better, because the model cannot tell which one is authoritative and averages them.** Where a character carries a second bound sheet — an explicit expression range, or a legacy turnaround+expression pair — cite BOTH under the SAME name, `Marcus (images 1 and 2)`. That shared name, not a role essay, is what tells the model they are ONE person and stops the varied expressions from averaging the face. **Do NOT hand-write a \"Reference Image Instructions\" block or role essays** (\"use for identity, ignore the outfit, render a neutral expression\") — that drags the sheet's studio lighting and wardrobe into a scene that asked for neither. The prompt leads; the user's words own wardrobe, expression, lighting, and action.\n4. **Flat-light identity refs.** Prep identity references with flat, even, shadowless lighting on a plain neutral background. A studio-lit or scene-lit character sheet bleeds its lighting into every generation — the failure looks like the subject was green-screen-pasted in front of the location. Reference prep beats prompting here.\n5. **Environment: describe it, don't feed a grid.** Default to describing the location in words and let the model build a space that fits the shot. Reserve an environment reference for a mandatory exact-match, and then use ONE clean establishing image with natural ambient light that reads as the location's real light — never a multi-panel grid fed whole.\n6. **Grids: explore, don't input.** Use grids to explore compositions cheaply, then pick a cell. Never feed a grid back in as a reference — the cells share a split detail budget and were generated jointly, so their flaws propagate.\n7. **Reuse the same refs across every shot** in a sequence. Lock a set and keep it; swapping references mid-sequence causes drift, because the model adapts each reference to the current prompt rather than copying it.\n8. **Legible in-shot text → bake it into a still start frame, never trust text-to-video.** Have an image model render the text, then animate from that locked frame. Video models smear type.\n9. **Working from existing media — describe ONLY what changes.** The source already carries its composition, motion, timing, and performance; re-describing them fights the model. Narrate the delta. (Video lane: restyle your own clip while keeping the performance; delayed-VFX on \"video one\"; marker-object insertion; video-as-reference for a series.)\n10. **Style transforms happen in natural language.** By default the source's artistic medium and visual style are inherited. To change it, add a plain-text instruction (\"anime → real person\"). There are no preset pickers, and there is no style slider.",
|
|
10
|
+
"reference-tips-short": "Name each reference inline; never write role essays. Slates does this for you: `@mention` a subject or environment and it composes `Marcus (images 1 and 2) in the cafe (image 3)`, citing them in the exact order it sends them. Citing both of a character's sheets under the SAME name is what tells the model they are one person — a \"Reference Image Instructions\" block does the opposite and drags the sheet's studio lighting into your scene. Start with 2-3 focused refs.",
|
|
11
|
+
"references-read-literally": "> **The general law: the model reads a reference literally.**\n> A reference image is not a suggestion. Whatever is baked into it — lighting, medium, texture, symmetry, competing identities — is read as a **property of the subject** and reproduced downstream. A baked rim light tints every shot made from that sheet. A sheet that looks like a 3D game render gets animated like game footage. Two competing renderings of one face get averaged into a third face.\n\nEvery reference rule below is a corollary of that one sentence, which is why \"prep the reference\" beats \"prompt around the reference\" every time:\n\n- **Flat, plain identity refs** — because scene lighting in the sheet becomes scene lighting in the output (Slates' own receipt: a studio-lit sheet produced a subject that looked green-screen-pasted in front of mountains).\n- **One authoritative rendering per subject** — because the model cannot tell which panel is the real one. ByteDance documents this failure directly: multi-view character assets \"confuse the model's character recognition, causing it to generate duplicate characters of the same appearance.\"\n- **No 3D-game-render look in a reference** — the model recognizes the render mood and inherits its motion character, so the *animation* comes out looking like game footage. This is not a taste rule; it is the same literal-reading mechanism applied to the temporal layer.\n- **Break perfect symmetry** — mirrored faces and dead-square framing read as synthetic, and the model preserves that reading rather than correcting it.\n\n**What this means in practice:** when output is wrong in a way that tracks the *subject* rather than the *scene* — the lighting is wrong the same way in every shot, the face drifts, the material looks synthetic everywhere — fix the reference, not the prompt. Prompting around a baked-in property is the expensive way to lose.",
|
|
12
|
+
"still-gate": "**A visible defect in the still is already a STOP.** Do not animate it. Fix the frame first, then move to motion — and go to motion only when the crop passes the still scan and you genuinely need movement to confirm an uncertain edge, reflection, or object.\n\nThis is a **cost** rule as much as a craft rule: a 1080p/10s premium video generation costs many multiples of an image re-roll, and video is where a defect stops being fixable. Anything wrong in the still gets worse in motion — soft geometry mushes, broken-but-plausible objects fall apart, oily textures start crawling. **Animating a known-bad frame is the single most expensive mistake in the pipeline.** Re-rolling the image is the cheap move; re-rolling the video is not.",
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=partials.generated.js.map
|
|
@@ -9,11 +9,19 @@
|
|
|
9
9
|
// matching entry here in the same pass. Keys are model FAMILIES — the
|
|
10
10
|
// desktop maps concrete model ids to a family key with its MODEL_REGISTRY
|
|
11
11
|
// helpers (the runtime truth for ids lives in slate/src/shared/pricing.ts).
|
|
12
|
+
//
|
|
13
|
+
// Cards whose content is ALSO doctrine (not just model trivia) compose their
|
|
14
|
+
// copy from skills/_partials/*.md via PARTIALS rather than restating it. The
|
|
15
|
+
// NANO_BANANA reference card is why: it shipped "label every role" — retired
|
|
16
|
+
// doctrine — for thirteen months after the reversal, in the same package as
|
|
17
|
+
// the rule forbidding it. Hand-sync didn't merely drift, it survived a
|
|
18
|
+
// reversal. Add a short partial; don't hand-copy a rule into a card.
|
|
19
|
+
import { PARTIALS } from './partials.generated.js';
|
|
12
20
|
const SEEDANCE = {
|
|
13
21
|
label: 'Seedance 2.0',
|
|
14
22
|
intro: [
|
|
15
|
-
'Seedance 2.0
|
|
16
|
-
"
|
|
23
|
+
'Seedance 2.0 is a multimodal director: it reads your text, images, video and audio at once and splits them into a "spatial layer" (what is in frame) and a "temporal layer" (how it changes). So a good prompt is an engineering-style instruction, not a piece of copywriting. Audio is always generated alongside video at no extra cost.',
|
|
24
|
+
"ByteDance's official advanced formula has 8 slots: precise subject + action details + scene/environment + lighting & color tone + camera movement + visual style + image quality + constraints. Sweet spot 60-150 words for a single shot, longer for multi-shot.",
|
|
17
25
|
],
|
|
18
26
|
columns: [
|
|
19
27
|
[
|
|
@@ -23,37 +31,55 @@ const SEEDANCE = {
|
|
|
23
31
|
note: "The first 20-30 words are the identity anchor. If the subject isn't locked in immediately, Seedance will hallucinate new subjects mid-generation.",
|
|
24
32
|
},
|
|
25
33
|
{
|
|
26
|
-
heading: '
|
|
27
|
-
example: '
|
|
28
|
-
note: '
|
|
34
|
+
heading: 'Shot 1 / Shot 2 / Shot 3 — never time stamps',
|
|
35
|
+
example: 'Shot 1: Side shot of the alley; the man slowly starts running.\nShot 2: He knocks over a fruit stand; the camera shakes and cuts to his face.\nShot 3: He climbs a low wall; the camera pulls back onto the empty street.',
|
|
36
|
+
note: 'ByteDance: write a "Shot 1 / Shot 2 / Shot 3" storyboard in the order events occur, then merge it into one prompt. Do NOT write "At 4 seconds" or "0:00–0:03" and do not set per-shot durations — official docs say precise timing is unstable and forcing it "may lead to abnormal generation results." Let the plot set the pacing.',
|
|
37
|
+
critical: true,
|
|
29
38
|
},
|
|
30
39
|
{
|
|
31
|
-
heading: '
|
|
40
|
+
heading: 'Order inside each shot',
|
|
41
|
+
example: 'camera move → action + expression → position change → audio',
|
|
42
|
+
note: "ByteDance's recommended per-shot order. Lead with the camera (\"slowly push in from a wide shot\", \"fixed camera position\", \"cut to...\"), then what the subject does, then where they end up, then the sound.",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
heading: 'Lighting is a top quality lever',
|
|
32
46
|
example: 'A cool-white diagonal beam from upper left, dust particles drifting through...',
|
|
33
|
-
note: '
|
|
47
|
+
note: 'Lighting & color tone has its own slot in the official formula. Describe it before or alongside the subject.',
|
|
34
48
|
},
|
|
35
49
|
],
|
|
36
50
|
[
|
|
37
51
|
{
|
|
38
|
-
heading: '
|
|
39
|
-
example: '
|
|
40
|
-
note: '
|
|
52
|
+
heading: 'Standard camera terms — including shot size',
|
|
53
|
+
example: 'medium shot · close-up · wide shot · slow push-in · smooth lateral tracking · fixed shot',
|
|
54
|
+
note: 'ByteDance: the model has a strong understanding of camera terminology, so use it directly — this is an open vocabulary, not a fixed list, and shot size counts as camera direction. Only ONE camera movement per shot: asking for push, pull, pan and move at once increases image instability.',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
heading: 'Slow, gentle, continuous movement',
|
|
58
|
+
example: 'slowly raise a hand · quickly turn the head · walk slowly · sit down naturally with the motion',
|
|
59
|
+
note: 'Official rule: name the body part and quantify range, speed and force — and prefer small continuous movement over sprints, big jumps and violent rolls. Slow-motion is supported in natural language; "fast" is a known quality-degrading word.',
|
|
41
60
|
},
|
|
42
61
|
{
|
|
43
|
-
heading: '
|
|
44
|
-
example: '
|
|
45
|
-
note: '
|
|
62
|
+
heading: 'Externalize emotion',
|
|
63
|
+
example: '❌ she looks very sad\n✅ head lowering, shoulders trembling slightly, eyes reddening, fingers clutching the corner of her clothing',
|
|
64
|
+
note: 'Replace abstract emotion words with the physical detail that shows them. This is the single highest-leverage habit in ByteDance\'s guide — the model renders bodies, not adjectives.',
|
|
46
65
|
},
|
|
47
66
|
{
|
|
48
67
|
heading: 'Separate camera from subject motion',
|
|
49
68
|
example: 'The earbud rises smoothly. The camera tracks upward.',
|
|
50
|
-
note: 'Two different sentences. Mixing them ("the camera speed ramps as the earbud rises") is
|
|
69
|
+
note: 'Two different sentences. Mixing them ("the camera speed ramps as the earbud rises") is a common cause of shaky, glitchy output.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
heading: 'Multi-character shots — forbid twins',
|
|
73
|
+
example: 'Throughout the video, characters with completely identical appearance, clothing, and accessories are prohibited. Do not generate duplicate avatars or a twin effect.',
|
|
74
|
+
note: 'With several characters in frame, Seedance can render the same person twice. ByteDance\'s fix: bind each character to its image ("Marcus (image 1)"), append that constraint verbatim at the end, and prefer single-person reference photos. Past 4 reference people, stability drops — compose a group still first.',
|
|
51
75
|
},
|
|
52
76
|
],
|
|
53
77
|
],
|
|
54
78
|
footer: [
|
|
79
|
+
'Quality and constraint slots have their own official vocabulary: ask for "HD, rich details, cinematic texture, natural colors, soft lighting" — not "8K / masterpiece / trending on artstation." Seedance has no negative-prompt field, so constraints go inline: "keep it subtitle-free", "do not generate a logo", "do not generate a watermark".',
|
|
55
80
|
'Style block at the end: one primary anchor plus 2-3 supporting details. End with "Single continuous take" if you want one shot with no cuts. Never write "no cut" or "seamless transition" — those aren\'t in the training vocabulary.',
|
|
56
|
-
'Multi-modal: up to 9 images
|
|
81
|
+
'Multi-modal: up to 9 images, 3 videos and 3 audio references. Cite them by type and index — "Zhang San@Image 1", or the "Marcus (image 1)" form Slates composes from your @mentions. Never cite an asset ID instead of the image number; the model can\'t associate the two. Max length: 4,000 characters.',
|
|
82
|
+
'Don\'t cross-pollinate image-model syntax: named lenses, apertures and film stocks ("85mm f/1.4", "Kodak Portra 400") are a Nano Banana lever and a Seedance anti-pattern. Translate them into shot size, depth of field and colour tone instead.',
|
|
57
83
|
],
|
|
58
84
|
};
|
|
59
85
|
const KLING = {
|
|
@@ -269,6 +295,11 @@ const NANO_BANANA = {
|
|
|
269
295
|
example: 'Kodak Portra 400 · Fuji Velvia 50 · Ilford HP5 Plus · CineStill 800T',
|
|
270
296
|
note: 'Portra = natural skin warmth. Velvia = saturated landscape. HP5 = gritty B&W grain. CineStill 800T = tungsten night with halation. Never mix stocks.',
|
|
271
297
|
},
|
|
298
|
+
{
|
|
299
|
+
heading: "Don't carry lens + stock into a video prompt",
|
|
300
|
+
example: '85mm f/1.4, Portra 400\n→ close-up, shallow depth of field, warm natural colors, cinematic texture',
|
|
301
|
+
note: 'Lenses, apertures, film stocks and camera bodies are an image-model lever and a video-model anti-pattern — ByteDance\'s Seedance guide never mentions f-stops, lens millimetres, fps or shutter angle. When you animate a frame you made here, translate the look into shot size, depth of field and colour tone instead of pasting the gear list across.',
|
|
302
|
+
},
|
|
272
303
|
{
|
|
273
304
|
heading: 'Physics-based lighting',
|
|
274
305
|
example: 'Single key light at 45 degrees from upper left. Color temperature 4500K. Crisp catchlights in the eyes.',
|
|
@@ -293,9 +324,9 @@ const NANO_BANANA = {
|
|
|
293
324
|
note: 'Reframe positively first. Use inline "without" / "free of" only when positive framing can\'t suppress the unwanted element.',
|
|
294
325
|
},
|
|
295
326
|
{
|
|
296
|
-
heading: 'Reference images — label
|
|
297
|
-
example: '
|
|
298
|
-
note:
|
|
327
|
+
heading: 'Reference images — name them, never label roles',
|
|
328
|
+
example: 'Marcus (images 1 and 2) sits across from the woman (image 3) in the cafe (image 4).',
|
|
329
|
+
note: `Up to 14 refs (10 object + 4 character — caps don't trade). ${PARTIALS['reference-tips-short']}`,
|
|
299
330
|
},
|
|
300
331
|
{
|
|
301
332
|
heading: 'Common fixes',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { PARTIALS } from './partials.generated.js';
|
|
1
2
|
export type SourceGrade = 'Eric-test' | 'community' | 'code-verified' | 'creator-demo';
|
|
2
3
|
export interface ReferenceRule {
|
|
3
4
|
id: string;
|
|
@@ -11,24 +12,52 @@ export interface ReferenceRule {
|
|
|
11
12
|
* are the reusable text the templates compose.
|
|
12
13
|
*/
|
|
13
14
|
export declare const REFERENCE_RULES: ReferenceRule[];
|
|
14
|
-
/** Flat, even, shadowless identity lighting on a
|
|
15
|
+
/** Flat, even, shadowless identity lighting on a deep neutral-grey plate. */
|
|
15
16
|
export declare const IDENTITY_LIGHTING = "flat, even, shadowless lighting";
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* The plate value is deliberate, not decorative: **white bleeds into the
|
|
19
|
+
* generated video and washes out the location; black eats edge detail and
|
|
20
|
+
* crushes hair and wardrobe silhouettes.** A deep neutral grey holds both.
|
|
21
|
+
*/
|
|
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.";
|
|
25
|
+
/**
|
|
26
|
+
* Craft clauses every identity reference wants — the eye and skin detail that
|
|
27
|
+
* survives downstream, plus the two "reads literally" guards. Crushed-black
|
|
28
|
+
* irises carry no light information, so eye tone drifts between generations;
|
|
29
|
+
* no catchlight reads as dead eyes; perfect mirroring reads as synthetic and
|
|
30
|
+
* the model PRESERVES that reading; a game-render look gets ANIMATED like game
|
|
31
|
+
* footage. See skills/_partials/references-read-literally.md.
|
|
32
|
+
*/
|
|
33
|
+
export declare const IDENTITY_CRAFT_CLAUSE: string;
|
|
18
34
|
/** Inherit the source's artistic medium unless told otherwise. */
|
|
19
35
|
export declare const INHERIT_SOURCE_STYLE = "Preserve the artistic medium and visual style of the reference image (photograph, anime, illustration, 3D render, painterly, etc.).";
|
|
20
36
|
/** Environment plate guidance: one clean, naturally-lit establishing image. */
|
|
21
|
-
export declare const ENVIRONMENT_NATURAL_LIGHT = "natural
|
|
22
|
-
/**
|
|
23
|
-
|
|
37
|
+
export declare const ENVIRONMENT_NATURAL_LIGHT = "natural ambient lighting that reads as the location's real light, not a studio setup";
|
|
38
|
+
/**
|
|
39
|
+
* One-line summary used as a header in skills + the lead magnet. DERIVED from
|
|
40
|
+
* the partial's opening line — a hand-authored copy here would be a ninth
|
|
41
|
+
* wording of the same rule, which is the thing this whole mechanism exists to
|
|
42
|
+
* stop. Edit `skills/_partials/reference-rules-core.md`.
|
|
43
|
+
*/
|
|
44
|
+
export declare const REFERENCE_RULES_HEADLINE: string;
|
|
24
45
|
/**
|
|
25
|
-
* Canonical markdown block — the SOURCE OF TRUTH
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
46
|
+
* Canonical markdown block — the SOURCE OF TRUTH for reference-image doctrine
|
|
47
|
+
* across every Slates surface.
|
|
48
|
+
*
|
|
49
|
+
* ✅ WIRED 2026-07-21. This is no longer hand-reconciled prose. The text lives
|
|
50
|
+
* in `skills/_partials/reference-rules-core.md`; `scripts/sync-partials.mjs`
|
|
51
|
+
* injects it between the `@inject:reference-rules-core` markers in the
|
|
52
|
+
* per-model skills AND emits it here via `partials.generated.ts`. One edit to
|
|
53
|
+
* the partial now moves the markdown skills, this export, the MCP
|
|
54
|
+
* prompting-guide op, the CLI-installed skills, and the Studio Agent together.
|
|
55
|
+
*
|
|
56
|
+
* The build runs `sync-partials.mjs --check`, so a hand-edit inside a marker
|
|
57
|
+
* block fails the build with a diff instead of silently forking.
|
|
58
|
+
*
|
|
59
|
+
* To change reference doctrine: edit `skills/_partials/reference-rules-core.md`.
|
|
60
|
+
* Do NOT edit this file, and do NOT edit between markers in a skill.
|
|
32
61
|
*/
|
|
33
|
-
export declare const REFERENCE_RULES_TEXT
|
|
62
|
+
export declare const REFERENCE_RULES_TEXT: string;
|
|
34
63
|
//# sourceMappingURL=reference-rules.d.ts.map
|