@iodes/releasekit 0.1.0 → 0.1.1
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/LICENSE +21 -21
- package/README.md +292 -110
- package/dist/cli.js +7 -5
- package/dist/content.d.ts +8 -0
- package/dist/content.js +5 -3
- package/dist/export.js +4 -3
- package/dist/images.d.ts +14 -5
- package/dist/images.js +32 -8
- package/dist/model.d.ts +29 -0
- package/dist/model.js +27 -3
- package/dist/prompts.d.ts +2 -2
- package/dist/prompts.js +24 -16
- package/dist/validate.js +2 -2
- package/examples/README.md +34 -14
- package/examples/backup-encryption/README.md +19 -0
- package/examples/backup-encryption/dark.png +0 -0
- package/examples/backup-encryption/dark.prompt.md +52 -0
- package/examples/backup-encryption/dimensions-edit.prompt.md +7 -0
- package/examples/backup-encryption/flat-render-requests.md +15 -0
- package/examples/backup-encryption/light.png +0 -0
- package/examples/backup-encryption/light.prompt.md +52 -0
- package/examples/backup-encryption/pair-review.md +18 -0
- package/examples/backup-encryption/scene.yaml +31 -0
- package/examples/connected-route/README.md +23 -0
- package/examples/connected-route/dark.png +0 -0
- package/examples/connected-route/dark.prompt.md +58 -0
- package/examples/connected-route/light.png +0 -0
- package/examples/connected-route/light.prompt.md +58 -0
- package/examples/connected-route/pair-review.md +22 -0
- package/examples/connected-route/render-requests.md +171 -0
- package/examples/connected-route/scene.yaml +59 -0
- package/examples/feature-briefs.yaml +96 -89
- package/examples/location-preferences/README.md +18 -0
- package/examples/location-preferences/dark.png +0 -0
- package/examples/location-preferences/dark.prompt.md +52 -0
- package/examples/location-preferences/light.png +0 -0
- package/examples/location-preferences/light.prompt.md +52 -0
- package/examples/location-preferences/pair-review.md +17 -0
- package/examples/location-preferences/scene.yaml +26 -0
- package/examples/provided-media/README.md +15 -0
- package/examples/provided-media/scene.yaml +22 -0
- package/examples/queue-action/README.md +15 -15
- package/examples/queue-action/alignment-edit.prompt.md +8 -8
- package/examples/queue-action/dark.prompt.md +58 -58
- package/examples/queue-action/light.prompt.md +58 -58
- package/examples/queue-action/pair-review.md +33 -33
- package/examples/queue-action/scene.yaml +41 -41
- package/examples/release-notes.en-US.json +56 -56
- package/examples/release-notes.ko-KR.json +56 -56
- package/examples/storage-breakdown/README.md +18 -0
- package/examples/storage-breakdown/dark.png +0 -0
- package/examples/storage-breakdown/dark.prompt.md +52 -0
- package/examples/storage-breakdown/light.png +0 -0
- package/examples/storage-breakdown/light.prompt.md +52 -0
- package/examples/storage-breakdown/pair-review.md +18 -0
- package/examples/storage-breakdown/scene.yaml +28 -0
- package/examples/tablet-reading/README.md +18 -0
- package/examples/tablet-reading/content-edit.prompt.md +7 -0
- package/examples/tablet-reading/dark.png +0 -0
- package/examples/tablet-reading/dark.prompt.md +52 -0
- package/examples/tablet-reading/light.png +0 -0
- package/examples/tablet-reading/light.prompt.md +52 -0
- package/examples/tablet-reading/pair-review.md +18 -0
- package/examples/tablet-reading/scene.yaml +29 -0
- package/kit/references/composition-recipes.md +89 -73
- package/kit/references/format.md +26 -24
- package/kit/references/media-sources.md +34 -0
- package/kit/references/theme-pairing.md +55 -53
- package/kit/references/visual-language.md +73 -69
- package/kit/references/workflow.md +24 -24
- package/kit/references/writing.md +27 -27
- package/kit/skills/releasekit-draft/SKILL.md +10 -10
- package/kit/skills/releasekit-image/SKILL.md +16 -16
- package/kit/skills/releasekit-review/SKILL.md +12 -12
- package/kit/skills/releasekit-translate/SKILL.md +10 -10
- package/package.json +54 -52
- package/schemas/bundle.schema.json +26 -1
- package/schemas/visual.schema.json +42 -0
package/dist/images.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import sharp from 'sharp';
|
|
4
|
-
import { themes,
|
|
4
|
+
import { themes, assetVariant, imageSource, activeVariants } from './model.js';
|
|
5
5
|
import { Project, editable } from './project.js';
|
|
6
6
|
import { readVisual, checkReferenceFiles } from './content.js';
|
|
7
7
|
import { digest, identifier, exists, write, writeYaml } from './files.js';
|
|
@@ -27,10 +27,11 @@ export async function planImages(project, version) {
|
|
|
27
27
|
let ready = 0;
|
|
28
28
|
for (const note of release.notes.filter(n => n.image)) {
|
|
29
29
|
const visual = await readVisual(project, version, note.id);
|
|
30
|
+
const source = imageSource(visual.scene);
|
|
30
31
|
const pair = visual.variants;
|
|
31
32
|
const invalidPair = release.visuals.themes === 'both' && pair.dark && pair.light &&
|
|
32
33
|
(pair.dark.sha256 === pair.light.sha256 || pair.dark.width !== pair.light.width || pair.dark.height !== pair.light.height);
|
|
33
|
-
for (const variant of
|
|
34
|
+
for (const variant of activeVariants(visual, release.visuals)) {
|
|
34
35
|
const expected = sceneHash(visual.scene, release.visuals, variant);
|
|
35
36
|
const asset = visual.variants[variant];
|
|
36
37
|
const file = asset && await project.releaseFile(version, asset.file);
|
|
@@ -41,6 +42,16 @@ export async function planImages(project, version) {
|
|
|
41
42
|
continue;
|
|
42
43
|
}
|
|
43
44
|
editable(release);
|
|
45
|
+
if (source === 'provided') {
|
|
46
|
+
requests.push({
|
|
47
|
+
note: note.id, theme: variant, reason: present ? 'stale' : 'missing', action: 'provide',
|
|
48
|
+
promptFile: null, compositionReference: null,
|
|
49
|
+
instruction: `Use an existing approved image or ask for a capture, photograph, or content asset for "${visual.scene.subject}". Import the selected file with --theme ${variant}. Do not synthesize a replacement.`,
|
|
50
|
+
});
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (variant === 'shared')
|
|
54
|
+
throw new Error('Generated images require a dark or light variant.');
|
|
44
55
|
await checkReferenceFiles(project, visual.scene.references);
|
|
45
56
|
const promptFile = await project.releaseFile(version, `prompts/${note.id}.${variant}.md`);
|
|
46
57
|
await write(promptFile, imagePrompt(visual.scene, release.visuals, variant));
|
|
@@ -52,12 +63,14 @@ export async function planImages(project, version) {
|
|
|
52
63
|
if (await exists(reference) && digest(await fs.readFile(reference)) === other.sha256)
|
|
53
64
|
compositionReference = reference;
|
|
54
65
|
}
|
|
55
|
-
requests.push({ note: note.id, theme: variant, reason: present ? 'stale' : 'missing', promptFile, compositionReference });
|
|
66
|
+
requests.push({ note: note.id, theme: variant, reason: present ? 'stale' : 'missing', action: 'generate', promptFile, compositionReference });
|
|
56
67
|
}
|
|
57
68
|
}
|
|
58
69
|
return {
|
|
59
70
|
version, configuredThemes: themes(release.visuals), requestedAssets: ready + requests.length,
|
|
60
71
|
readyAssets: ready, pendingAssets: requests.length, requests,
|
|
72
|
+
generationRequests: requests.filter(request => request.action === 'generate').length,
|
|
73
|
+
providedRequests: requests.filter(request => request.action === 'provide').length,
|
|
61
74
|
costNote: 'Counts describe required output assets, not provider prices or a guarantee of one tool call per asset. No image service was called.',
|
|
62
75
|
};
|
|
63
76
|
}
|
|
@@ -65,12 +78,23 @@ export async function importImage(project, version, noteId, variant, source) {
|
|
|
65
78
|
const release = await project.release(version);
|
|
66
79
|
editable(release);
|
|
67
80
|
identifier(noteId);
|
|
68
|
-
|
|
81
|
+
assetVariant.parse(variant);
|
|
69
82
|
if (!release.notes.some(n => n.id === noteId && n.image))
|
|
70
83
|
throw new Error(`No image-enabled note named ${noteId}.`);
|
|
71
|
-
if (!themes(release.visuals).includes(variant))
|
|
72
|
-
throw new Error(`Theme ${variant} is not enabled for this release. Update the project setting and sync the draft first.`);
|
|
73
84
|
const visual = await readVisual(project, version, noteId);
|
|
85
|
+
const provided = imageSource(visual.scene) === 'provided';
|
|
86
|
+
if (variant === 'shared') {
|
|
87
|
+
if (!provided)
|
|
88
|
+
throw new Error('Only supplied images can use a shared asset.');
|
|
89
|
+
if (visual.variants.dark || visual.variants.light)
|
|
90
|
+
throw new Error('Remove the themed variant entries before switching to one shared supplied image.');
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
if (!themes(release.visuals).includes(variant))
|
|
94
|
+
throw new Error(`Theme ${variant} is not enabled for this release. Update the project setting and sync the draft first.`);
|
|
95
|
+
if (visual.variants.shared)
|
|
96
|
+
throw new Error('Remove the shared variant entry before switching to distinct supplied theme variants.');
|
|
97
|
+
}
|
|
74
98
|
const bytes = await fs.readFile(path.resolve(project.root, source));
|
|
75
99
|
const inspected = await inspectImage(bytes);
|
|
76
100
|
const file = `assets/${noteId}.${variant}.${inspected.sha256.slice(0, 12)}.${inspected.extension}`;
|
|
@@ -90,7 +114,7 @@ export async function importImage(project, version, noteId, variant, source) {
|
|
|
90
114
|
}
|
|
91
115
|
export async function validateImages(project, version, noteId, visual, errors, warnings) {
|
|
92
116
|
const release = await project.release(version);
|
|
93
|
-
for (const variant of
|
|
117
|
+
for (const variant of activeVariants(visual, release.visuals)) {
|
|
94
118
|
const expected = sceneHash(visual.scene, release.visuals, variant);
|
|
95
119
|
const asset = visual.variants[variant];
|
|
96
120
|
if (!asset) {
|
|
@@ -105,7 +129,7 @@ export async function validateImages(project, version, noteId, visual, errors, w
|
|
|
105
129
|
errors.push(`${noteId}: ${variant} asset changed after import; import the selected file again.`);
|
|
106
130
|
}
|
|
107
131
|
const requestedRatio = release.visuals.width / release.visuals.height;
|
|
108
|
-
if (Math.abs(actual.width / actual.height / requestedRatio - 1) > 0.05) {
|
|
132
|
+
if (imageSource(visual.scene) === 'generated' && Math.abs(actual.width / actual.height / requestedRatio - 1) > 0.05) {
|
|
109
133
|
warnings.push(`${noteId}: ${variant} aspect ratio differs from the project target; review its framing.`);
|
|
110
134
|
}
|
|
111
135
|
}
|
package/dist/model.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export declare const theme: z.ZodEnum<{
|
|
|
7
7
|
light: "light";
|
|
8
8
|
}>;
|
|
9
9
|
export type Theme = z.infer<typeof theme>;
|
|
10
|
+
export declare const assetVariant: z.ZodEnum<{
|
|
11
|
+
dark: "dark";
|
|
12
|
+
light: "light";
|
|
13
|
+
shared: "shared";
|
|
14
|
+
}>;
|
|
15
|
+
export type AssetVariant = z.infer<typeof assetVariant>;
|
|
10
16
|
export declare const paletteSchema: z.ZodObject<{
|
|
11
17
|
canvas: z.ZodString;
|
|
12
18
|
surface: z.ZodString;
|
|
@@ -193,6 +199,10 @@ export declare const sceneSchema: z.ZodObject<{
|
|
|
193
199
|
"symbol-pair": "symbol-pair";
|
|
194
200
|
"ui-detail": "ui-detail";
|
|
195
201
|
}>;
|
|
202
|
+
source: z.ZodOptional<z.ZodEnum<{
|
|
203
|
+
generated: "generated";
|
|
204
|
+
provided: "provided";
|
|
205
|
+
}>>;
|
|
196
206
|
subject: z.ZodString;
|
|
197
207
|
message: z.ZodString;
|
|
198
208
|
focus: z.ZodString;
|
|
@@ -225,6 +235,10 @@ export declare const visualSchema: z.ZodObject<{
|
|
|
225
235
|
"symbol-pair": "symbol-pair";
|
|
226
236
|
"ui-detail": "ui-detail";
|
|
227
237
|
}>;
|
|
238
|
+
source: z.ZodOptional<z.ZodEnum<{
|
|
239
|
+
generated: "generated";
|
|
240
|
+
provided: "provided";
|
|
241
|
+
}>>;
|
|
228
242
|
subject: z.ZodString;
|
|
229
243
|
message: z.ZodString;
|
|
230
244
|
focus: z.ZodString;
|
|
@@ -251,6 +265,13 @@ export declare const visualSchema: z.ZodObject<{
|
|
|
251
265
|
width: z.ZodNumber;
|
|
252
266
|
height: z.ZodNumber;
|
|
253
267
|
}, z.core.$strict>>;
|
|
268
|
+
shared: z.ZodOptional<z.ZodObject<{
|
|
269
|
+
file: z.ZodString;
|
|
270
|
+
sha256: z.ZodString;
|
|
271
|
+
sceneHash: z.ZodString;
|
|
272
|
+
width: z.ZodNumber;
|
|
273
|
+
height: z.ZodNumber;
|
|
274
|
+
}, z.core.$strict>>;
|
|
254
275
|
}, z.core.$strict>;
|
|
255
276
|
}, z.core.$strict>;
|
|
256
277
|
export type Visual = z.infer<typeof visualSchema>;
|
|
@@ -296,6 +317,7 @@ export declare const bundleSchema: z.ZodObject<{
|
|
|
296
317
|
fallbackTheme: z.ZodEnum<{
|
|
297
318
|
dark: "dark";
|
|
298
319
|
light: "light";
|
|
320
|
+
shared: "shared";
|
|
299
321
|
}>;
|
|
300
322
|
variants: z.ZodObject<{
|
|
301
323
|
dark: z.ZodOptional<z.ZodObject<{
|
|
@@ -308,6 +330,11 @@ export declare const bundleSchema: z.ZodObject<{
|
|
|
308
330
|
width: z.ZodNumber;
|
|
309
331
|
height: z.ZodNumber;
|
|
310
332
|
}, z.core.$strict>>;
|
|
333
|
+
shared: z.ZodOptional<z.ZodObject<{
|
|
334
|
+
src: z.ZodString;
|
|
335
|
+
width: z.ZodNumber;
|
|
336
|
+
height: z.ZodNumber;
|
|
337
|
+
}, z.core.$strict>>;
|
|
311
338
|
}, z.core.$strict>;
|
|
312
339
|
}, z.core.$strict>>;
|
|
313
340
|
}, z.core.$strict>>;
|
|
@@ -315,4 +342,6 @@ export declare const bundleSchema: z.ZodObject<{
|
|
|
315
342
|
}, z.core.$strict>;
|
|
316
343
|
export type Bundle = z.infer<typeof bundleSchema>;
|
|
317
344
|
export declare function themes(policy: VisualPolicy): Theme[];
|
|
345
|
+
export declare function imageSource(scene: Scene): 'generated' | 'provided';
|
|
346
|
+
export declare function activeVariants(visual: Visual, policy: VisualPolicy): AssetVariant[];
|
|
318
347
|
export declare function defaultConfig(product: string): ProjectConfig;
|
package/dist/model.js
CHANGED
|
@@ -3,6 +3,7 @@ export const segment = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,95}$/);
|
|
|
3
3
|
export const sha = z.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
|
|
4
4
|
export const locale = z.string().regex(/^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/);
|
|
5
5
|
export const theme = z.enum(['dark', 'light']);
|
|
6
|
+
export const assetVariant = z.enum(['dark', 'light', 'shared']);
|
|
6
7
|
const color = z.string().regex(/^#[a-fA-F0-9]{6}$/);
|
|
7
8
|
export const paletteSchema = z.strictObject({
|
|
8
9
|
canvas: color, surface: color, raised: color, primary: color, secondary: color, divider: color,
|
|
@@ -47,6 +48,7 @@ export const archetypeSchema = z.enum([
|
|
|
47
48
|
]);
|
|
48
49
|
export const sceneSchema = z.strictObject({
|
|
49
50
|
archetype: archetypeSchema,
|
|
51
|
+
source: z.enum(['generated', 'provided']).optional(),
|
|
50
52
|
subject: z.string().min(1), message: z.string().min(1),
|
|
51
53
|
focus: z.string().min(1), composition: z.string().min(1),
|
|
52
54
|
context: z.string(), elements: z.array(z.string()),
|
|
@@ -60,7 +62,7 @@ export const assetSchema = z.strictObject({
|
|
|
60
62
|
});
|
|
61
63
|
export const visualSchema = z.strictObject({
|
|
62
64
|
schemaVersion: z.literal(1), scene: sceneSchema,
|
|
63
|
-
variants: z.strictObject({ dark: assetSchema.optional(), light: assetSchema.optional() }),
|
|
65
|
+
variants: z.strictObject({ dark: assetSchema.optional(), light: assetSchema.optional(), shared: assetSchema.optional() }),
|
|
64
66
|
});
|
|
65
67
|
export const evidenceSchema = z.strictObject({
|
|
66
68
|
schemaVersion: z.literal(1), source: sourceSchema,
|
|
@@ -77,8 +79,8 @@ export const bundleSchema = z.strictObject({
|
|
|
77
79
|
notes: z.array(z.strictObject({
|
|
78
80
|
id: segment, category: noteMetaSchema.shape.category, title: z.string(), bodyMarkdown: z.string(),
|
|
79
81
|
image: z.strictObject({
|
|
80
|
-
alt: z.string(), fallbackTheme:
|
|
81
|
-
variants: z.strictObject({ dark: exportedImage.optional(), light: exportedImage.optional() }),
|
|
82
|
+
alt: z.string(), fallbackTheme: assetVariant,
|
|
83
|
+
variants: z.strictObject({ dark: exportedImage.optional(), light: exportedImage.optional(), shared: exportedImage.optional() }),
|
|
82
84
|
}).nullable(),
|
|
83
85
|
})),
|
|
84
86
|
})),
|
|
@@ -86,6 +88,28 @@ export const bundleSchema = z.strictObject({
|
|
|
86
88
|
export function themes(policy) {
|
|
87
89
|
return policy.themes === 'both' ? ['dark', 'light'] : [policy.themes];
|
|
88
90
|
}
|
|
91
|
+
export function imageSource(scene) {
|
|
92
|
+
if (scene.archetype === 'object-detail' || scene.archetype === 'editorial-scene') {
|
|
93
|
+
if (scene.source === 'generated')
|
|
94
|
+
throw new Error(`${scene.archetype} requires a supplied capture, photograph, or content image. Set source to provided.`);
|
|
95
|
+
return 'provided';
|
|
96
|
+
}
|
|
97
|
+
return scene.source ?? 'generated';
|
|
98
|
+
}
|
|
99
|
+
export function activeVariants(visual, policy) {
|
|
100
|
+
const provided = imageSource(visual.scene) === 'provided';
|
|
101
|
+
const { dark, light, shared } = visual.variants;
|
|
102
|
+
if (shared) {
|
|
103
|
+
if (!provided)
|
|
104
|
+
throw new Error('Only supplied images can use a shared asset.');
|
|
105
|
+
if (dark || light)
|
|
106
|
+
throw new Error('Choose a shared supplied image or distinct theme variants; do not mix both.');
|
|
107
|
+
return ['shared'];
|
|
108
|
+
}
|
|
109
|
+
if (provided && !dark && !light)
|
|
110
|
+
return ['shared'];
|
|
111
|
+
return themes(policy);
|
|
112
|
+
}
|
|
89
113
|
export function defaultConfig(product) {
|
|
90
114
|
return {
|
|
91
115
|
schemaVersion: 1, product, sourceLocale: 'ko-KR', locales: ['ko-KR', 'en-US'],
|
package/dist/prompts.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type Scene, type Theme, type VisualPolicy } from './model.js';
|
|
1
|
+
import { type AssetVariant, type Scene, type Theme, type VisualPolicy } from './model.js';
|
|
2
2
|
export declare const recipes: Record<Scene['archetype'], {
|
|
3
3
|
framing: string;
|
|
4
4
|
treatment: string;
|
|
5
5
|
review: string;
|
|
6
6
|
}>;
|
|
7
|
-
export declare function sceneHash(scene: Scene, policy: VisualPolicy, variant:
|
|
7
|
+
export declare function sceneHash(scene: Scene, policy: VisualPolicy, variant: AssetVariant): string;
|
|
8
8
|
export declare function imagePrompt(scene: Scene, policy: VisualPolicy, variant: Theme): string;
|
package/dist/prompts.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { canonical, digest } from './files.js';
|
|
2
|
-
import {} from './model.js';
|
|
2
|
+
import { imageSource } from './model.js';
|
|
3
3
|
export const recipes = {
|
|
4
4
|
'icon-tile': {
|
|
5
|
-
framing: 'Center one
|
|
6
|
-
treatment: '
|
|
5
|
+
framing: 'Center one small flat rounded-square tile, normally 20–24% of the canvas width. Keep the glyph around 50–65% of the tile width. Use optical centering and broad uninterrupted negative space. A naked glyph is appropriate only when the scene explicitly calls for it.',
|
|
6
|
+
treatment: 'Use a crisp flat 2D filled glyph in one neutral gray value, with negative space for internal details. Keep the canvas and tile uniform and untextured. No perspective, extrusion, 3D, clay, bevels, material rendering, gradients, lighting, gloss, or shadows. Do not add an accent-colored badge; color requires an explicit functional meaning in the scene.',
|
|
7
7
|
review: 'The symbol must communicate the stated capability or status. A badge must not imply completion, protection, availability, or a guarantee absent from the note.',
|
|
8
8
|
},
|
|
9
9
|
'symbol-pair': {
|
|
10
10
|
framing: 'Place two similarly weighted symbols on one horizontal optical axis, centered as a group; a short low-contrast divider can separate them.',
|
|
11
|
-
treatment: 'Communicate one relationship. Match
|
|
11
|
+
treatment: 'Communicate one relationship with flat 2D filled glyphs. Match visual weight, corner treatment, and perceived size. Use an arrow only when direction itself is part of the feature. Prefer neutral gray; use color only for a stated interaction or semantic status. No rendered materials or sculpted 3D symbols.',
|
|
12
12
|
review: 'Check which two concepts are related and whether the relationship is directional. A connector must not imply transfer, synchronization, or automation unless supported by the note.',
|
|
13
13
|
},
|
|
14
14
|
'ui-detail': {
|
|
@@ -18,18 +18,18 @@ export const recipes = {
|
|
|
18
18
|
},
|
|
19
19
|
'device-view': {
|
|
20
20
|
framing: 'Use one front-facing device or display at roughly 28–48% of the canvas width. A bottom crop is allowed when it enlarges the relevant feature; preserve the entire focus area.',
|
|
21
|
-
treatment: 'Keep
|
|
21
|
+
treatment: 'Keep any generated frame flat and schematic, with the screen grounded in supplied product evidence. Use a single device unless cross-device interaction is the feature. Choose a supplied capture when actual device appearance matters; do not invent a modeled hardware product.',
|
|
22
22
|
review: 'Check the actual device count, screen content, and relationship between devices. Do not imply an unsupported device, connection, or application theme.',
|
|
23
23
|
},
|
|
24
24
|
'object-detail': {
|
|
25
|
-
framing: '
|
|
26
|
-
treatment: '
|
|
25
|
+
framing: 'Use a supplied image of the relevant product part and retain enough context to recognize it.',
|
|
26
|
+
treatment: 'Request an authentic product capture, photograph, or approved asset. Preserve the supplied appearance; this category does not create a new physical object or 3D illustration.',
|
|
27
27
|
review: 'Check the feature-defining shape, scale, assembly, contact points, and material against product references. A highlight must not invent a component or change how the object works.',
|
|
28
28
|
},
|
|
29
29
|
'spatial-view': {
|
|
30
|
-
framing: '
|
|
31
|
-
treatment: '
|
|
32
|
-
review: 'Check positions, connections, direction, scale relationships, and layer meanings against the scene. Routes must
|
|
30
|
+
framing: 'Choose the geographic or diagrammatic scale needed to explain the feature, with one consistent viewpoint. A map can reach the canvas edges; reserve quiet space for a supported summary only when the brief calls for it. Do not impose a split layout, coast, road network, or endpoint markers on every spatial scene.',
|
|
31
|
+
treatment: 'For maps, retain enough fine, low-contrast context to read as a map. Reduce its contrast before deleting its structure: distinguish minor streets, major connections, and land or water through thin linework and flat values. Keep the active route or selection dominant without turning streets into oversized roads or padded checkerboard blocks. Use diagrammatic simplification when relationships alone are the subject. Preserve semantic colors; avoid decorative relief, bevels, textures, and lighting.',
|
|
32
|
+
review: 'Check positions, connections, direction, scale relationships, and layer meanings against the scene. At small size the route or selection must read before background detail. Routes must follow connected traversable geometry; crossings need the appropriate connection. Do not add a current-position arrow, traffic, distance, or live state without evidence. Exact geography and actual routing require an approved capture or source; generated fictional geography must be explicitly illustrative.',
|
|
33
33
|
},
|
|
34
34
|
'data-view': {
|
|
35
35
|
framing: 'Focus on one panel or device showing one dominant visualization and a few supporting rows. Give the primary metric or interaction clear breathing room.',
|
|
@@ -37,17 +37,21 @@ export const recipes = {
|
|
|
37
37
|
review: 'Check category identity, axes, units, relative values, totals, legends, and any selected filter when present. Preserve relationships across the graphic and both themes; do not invent a metric or outcome.',
|
|
38
38
|
},
|
|
39
39
|
'editorial-scene': {
|
|
40
|
-
framing: '
|
|
41
|
-
treatment: '
|
|
40
|
+
framing: 'Use a supplied capture or approved image of the announced content, with a crop that keeps the subject recognizable.',
|
|
41
|
+
treatment: 'Request actual content artwork or a screenshot. Preserve its appearance and colors; this category does not invent an editorial still life, illustration, or promotional scene.',
|
|
42
42
|
review: 'Check that the depicted subject and experience match the announced content. Keep essential object relationships coherent and avoid added features, factual promises, or unrelated scenery.',
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
45
|
export function sceneHash(scene, policy, variant) {
|
|
46
|
+
if (variant === 'shared' || scene.source === 'provided')
|
|
47
|
+
return digest(canonical({ scene, variant, source: 'provided' }));
|
|
46
48
|
// Other themes and their palettes must not invalidate this already accepted render.
|
|
47
49
|
const { themes: _themes, dark: _dark, light: _light, ...appearance } = policy;
|
|
48
50
|
return digest(canonical({ scene, appearance, theme: variant, palette: policy[variant] }));
|
|
49
51
|
}
|
|
50
52
|
export function imagePrompt(scene, policy, variant) {
|
|
53
|
+
if (imageSource(scene) === 'provided')
|
|
54
|
+
throw new Error('This scene needs a supplied image. Request or import the source instead of generating an illustration.');
|
|
51
55
|
const recipe = recipes[scene.archetype];
|
|
52
56
|
const palette = policy[variant];
|
|
53
57
|
const list = (items) => items.length ? items.map(item => `- ${item}`).join('\n') : '- None';
|
|
@@ -57,10 +61,14 @@ export function imagePrompt(scene, policy, variant) {
|
|
|
57
61
|
`## Composition contract\nArchetype: ${scene.archetype}\nTarget canvas: ${policy.width} × ${policy.height} pixels; landscape ${policy.width}:${policy.height}. Produce a single image, not a dark/light collage.\n${recipe.framing}\nSpecific scene layout: ${scene.composition}\nElements:\n${list(scene.elements)}\n\n` +
|
|
58
62
|
`## Visual treatment\n${recipe.treatment}\nFavor visual precision, quiet hierarchy, and one instantly understandable feature. Small-screen clarity takes priority over decorative detail. Treat the specified element inventory as complete. Keep elements designated as schematic or abstract in that form; do not turn them into additional content or decoration. Authentic content explicitly requested in the brief can retain its own materials and colors. Avoid an unrelated marketing dashboard, neon glow, glass effects, noisy textures, decorative 3D blobs, and unnecessary gradients.\n\n` +
|
|
59
63
|
`## ${variant === 'dark' ? 'Dark' : 'Light'} theme roles\n` +
|
|
60
|
-
`Canvas ${palette.canvas}; base surface ${palette.surface}; raised surface ${palette.raised}; main neutral symbol ${palette.primary}; secondary detail ${palette.secondary}; divider ${palette.divider}; interaction accent ${policy.accent}.\n` +
|
|
61
|
-
(
|
|
62
|
-
?
|
|
63
|
-
:
|
|
64
|
+
`Canvas ${palette.canvas}; base surface ${palette.surface}; raised surface ${palette.raised}; main neutral symbol ${palette.primary}; secondary detail ${palette.secondary}; divider ${palette.divider}; interaction accent ${policy.accent}. Use the accent only when the scene assigns it a functional meaning.\n` +
|
|
65
|
+
(scene.archetype === 'icon-tile' || scene.archetype === 'symbol-pair'
|
|
66
|
+
? `Use uniform flat color areas and crisp negative space. If a tile is present, use ${variant === 'dark' ? palette.surface : palette.raised} for its flat fill. Separate the neutral glyph and its background by value alone. Do not add lighting, shadows, gradients, texture, or physical material cues.\n`
|
|
67
|
+
: scene.archetype === 'spatial-view'
|
|
68
|
+
? 'Use flat value separation and crisp linework. Keep background layers subordinate to the focal route or selection in this theme. Do not add contact shadows, studio lighting, bevels, or material shading. A local fade into quiet space is allowed only when specified by the scene.\n'
|
|
69
|
+
: variant === 'dark'
|
|
70
|
+
? 'Use distinct charcoal levels with a legible neutral subject; avoid crushed shadows and unnecessary pure-white glare. Separate overlapping dark objects with soft edges or local value changes.\n'
|
|
71
|
+
: 'Use a near-white canvas, subtle surface separation, restrained contact shadows, and medium-dark neutral symbols. Avoid both flat white-on-white disappearance and thick dark outlines.\n') +
|
|
64
72
|
`Treat these colors as presentation roles, not a global recoloring filter. Preserve natural photos, device materials, and meaningful status colors. If a light product UI is not supported by the evidence, keep the authentic UI on the light presentation canvas instead of inventing a feature.\n\n` +
|
|
65
73
|
`## Pair invariants\nThe other theme must use the same object count, positions, scale, crop, camera, UI topology, selected state, chart values, allowed labels, and feature meaning. Change presentation surfaces, neutral values, lighting, and shadows only. Preserve semantic accent hues. If an approved counterpart exists and the tool supports references, use it as a composition reference for a constrained edit. Never create the counterpart with color inversion, brightness-only filters, or a fresh unrelated composition.\nSpecific invariants:\n${list(scene.preserve)}\n\n` +
|
|
66
74
|
`## Text and references\n` +
|
package/dist/validate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
|
-
import {} from './model.js';
|
|
2
|
+
import { imageSource } from './model.js';
|
|
3
3
|
import { Project, editable } from './project.js';
|
|
4
4
|
import { canonical, digest, readNote, noteHash, identifier } from './files.js';
|
|
5
5
|
import { readVisual, checkReferenceFiles } from './content.js';
|
|
@@ -60,7 +60,7 @@ export async function validate(project, version) {
|
|
|
60
60
|
if (note.image) {
|
|
61
61
|
try {
|
|
62
62
|
const visual = await readVisual(project, version, note.id);
|
|
63
|
-
if (release.status === 'draft')
|
|
63
|
+
if (release.status === 'draft' && imageSource(visual.scene) === 'generated')
|
|
64
64
|
await checkReferenceFiles(project, visual.scene.references);
|
|
65
65
|
await validateImages(project, version, note.id, visual, errors, warnings);
|
|
66
66
|
}
|
package/examples/README.md
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
|
-
# Example release content
|
|
2
|
-
|
|
3
|
-
These fictional notes illustrate the public display contract. They are not release announcements for this toolkit or claims about a shipped product.
|
|
4
|
-
|
|
5
|
-
- [Korean bundle](release-notes.ko-KR.json)
|
|
6
|
-
- [English bundle](release-notes.en-US.json)
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
# Example release content
|
|
2
|
+
|
|
3
|
+
These fictional notes illustrate the public display contract. They are not release announcements for this toolkit or claims about a shipped product.
|
|
4
|
+
|
|
5
|
+
- [Korean bundle](release-notes.ko-KR.json)
|
|
6
|
+
- [English bundle](release-notes.en-US.json)
|
|
7
|
+
- [Generated explanations, scenes, prompts, and reviews](#composition-gallery)
|
|
8
|
+
- [Supplied-image workflow](provided-media/README.md) for real product details and content previews
|
|
9
|
+
- [Independent feature briefs](feature-briefs.yaml): an encryption symbol, a static setting, and a data breakdown
|
|
10
|
+
|
|
11
|
+
The independent briefs pair each fictional note with a different scene and its correctness constraints. They are authoring examples, separate from the public bundle format. The prompt compiler uses only the selected brief and recipe for each note; they do not inherit the list interaction from the raster example. The encryption and storage briefs also have rendered examples below; the quiet-hours brief remains a text-only alternative for `ui-detail`.
|
|
12
|
+
|
|
13
|
+
## Composition gallery
|
|
14
|
+
|
|
15
|
+
The gallery demonstrates generated explanations. Each displayed folder contains a shared `scene.yaml`, compiled prompts, selected PNGs, and a review. These are fictional authoring references, not default layouts. Physical details and actual content previews use the supplied-image workflow rather than synthetic examples.
|
|
16
|
+
|
|
17
|
+
| Recipe and example | Dark | Light |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| [`icon-tile`: backup encryption](backup-encryption/README.md) |  |  |
|
|
20
|
+
| [`symbol-pair`: location preferences](location-preferences/README.md) |  |  |
|
|
21
|
+
| [`ui-detail`: queue action](queue-action/README.md) |  |  |
|
|
22
|
+
| [`device-view`: tablet reading](tablet-reading/README.md) |  |  |
|
|
23
|
+
| [`spatial-view`: connected route](connected-route/README.md) |  |  |
|
|
24
|
+
| [`data-view`: storage breakdown](storage-breakdown/README.md) |  |  |
|
|
25
|
+
|
|
26
|
+
Use the tablet example to see how a light-only product screen stays light on both presentation canvases. Use an actual capture when device or interface fidelity matters. The storage values are illustrative, and the route has no real geographic identity.
|
|
27
|
+
|
|
28
|
+
`object-detail` and `editorial-scene` require an approved photograph, screenshot, or content asset. If none is available, the plan returns a supplied-image request with no generation prompt. [The supplied-media example](provided-media/README.md) shows that pending state and shared-asset import. Earlier synthetic physical-object and decorative-content explorations are retired and excluded from the package.
|
|
29
|
+
|
|
30
|
+
## Public bundles
|
|
31
|
+
|
|
32
|
+
Each bundle contains version 1.4.0 followed by 1.3.0 and 1.2.0. The explicit `previous` links define that order. The `queue-action` note appears in two different version groups because each describes that version's change; consumers retain both entries.
|
|
33
|
+
|
|
34
|
+
Both locales reference the same selected dark and light PNGs. Choose `image.variants[theme]`, falling back to `image.variants[image.fallbackTheme]` only when the requested variant is absent. Text-only notes use `image: null`. These are content examples; build the surrounding scrolling interface in the consumer application.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Backup encryption
|
|
2
|
+
|
|
3
|
+
An original fictional `icon-tile` example: Local backup files can be saved with encryption enabled.
|
|
4
|
+
|
|
5
|
+
Fictional capability illustration, not a shipped product claim. The lock represents encryption of local backup files only; it does not claim protection of every product surface.
|
|
6
|
+
|
|
7
|
+
| Dark | Light |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
|  |  |
|
|
10
|
+
|
|
11
|
+
Both selected PNGs are 1585 × 992 pixels. The small tile occupies approximately 22% of the canvas width, with one neutral filled glyph and broad margins. This flat symbol replaces the earlier sculpted archive-and-lock illustration.
|
|
12
|
+
|
|
13
|
+
- [Shared scene specification](scene.yaml)
|
|
14
|
+
- [Dark prompt](dark.prompt.md) and [light prompt](light.prompt.md), compiled from that scene and the default project palette
|
|
15
|
+
- [Generation and pair review](pair-review.md)
|
|
16
|
+
- [Actual flat rendering and correction requests](flat-render-requests.md)
|
|
17
|
+
- [Current composition examples](../README.md#composition-gallery)
|
|
18
|
+
|
|
19
|
+
The scene explains this feature only. Derive a new scene from each real note and its product evidence.
|
|
Binary file
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Release illustration — dark
|
|
2
|
+
|
|
3
|
+
## Intent
|
|
4
|
+
Create one finished raster illustration for a product release note. Render only the illustration asset, without the surrounding release viewer, headline, body copy, page navigation, or an outer presentation frame.
|
|
5
|
+
User-visible change: Local backup files can be saved with encryption enabled.
|
|
6
|
+
Subject: One flat closed-padlock glyph inside a compact rounded square tile
|
|
7
|
+
Focal detail: The closed shackle and simple keyhole of the monochrome padlock glyph
|
|
8
|
+
Context: Fictional capability illustration, not a shipped product claim. The flat lock represents encryption of local backup files only. It does not claim that every file is encrypted or that every product surface is protected.
|
|
9
|
+
|
|
10
|
+
## Composition contract
|
|
11
|
+
Archetype: icon-tile
|
|
12
|
+
Target canvas: 1280 × 800 pixels; landscape 1280:800. Produce a single image, not a dark/light collage.
|
|
13
|
+
Center one small flat rounded-square tile, normally 20–24% of the canvas width. Keep the glyph around 50–65% of the tile width. Use optical centering and broad uninterrupted negative space. A naked glyph is appropriate only when the scene explicitly calls for it.
|
|
14
|
+
Specific scene layout: A uniform 8:5 landscape canvas with one small flat rounded-square tile centered exactly in the canvas. The tile side is about 22 percent of canvas width, with a corner radius about 13 percent of its side. Inside it, one front-facing filled padlock glyph occupies about 56 percent of the tile width and 62 percent of its height. Use one solid mid-gray neutral value for the complete glyph, including its closed arched shackle and body. A simple keyhole is negative space in the body. Keep broad uninterrupted canvas around the tile. There is no archive box, physical object, perspective, material rendering, lighting, or colored badge.
|
|
15
|
+
Elements:
|
|
16
|
+
- One flat rounded-square background tile
|
|
17
|
+
- One solid monochrome closed-padlock glyph with a negative-space keyhole
|
|
18
|
+
|
|
19
|
+
## Visual treatment
|
|
20
|
+
Use a crisp flat 2D filled glyph in one neutral gray value, with negative space for internal details. Keep the canvas and tile uniform and untextured. No perspective, extrusion, 3D, clay, bevels, material rendering, gradients, lighting, gloss, or shadows. Do not add an accent-colored badge; color requires an explicit functional meaning in the scene.
|
|
21
|
+
Favor visual precision, quiet hierarchy, and one instantly understandable feature. Small-screen clarity takes priority over decorative detail. Treat the specified element inventory as complete. Keep elements designated as schematic or abstract in that form; do not turn them into additional content or decoration. Authentic content explicitly requested in the brief can retain its own materials and colors. Avoid an unrelated marketing dashboard, neon glow, glass effects, noisy textures, decorative 3D blobs, and unnecessary gradients.
|
|
22
|
+
|
|
23
|
+
## Dark theme roles
|
|
24
|
+
Canvas #242527; base surface #18191B; raised surface #343638; main neutral symbol #B9BBBE; secondary detail #777B80; divider #46494D; interaction accent #4678ED. Use the accent only when the scene assigns it a functional meaning.
|
|
25
|
+
Use uniform flat color areas and crisp negative space. If a tile is present, use #18191B for its flat fill. Separate the neutral glyph and its background by value alone. Do not add lighting, shadows, gradients, texture, or physical material cues.
|
|
26
|
+
Treat these colors as presentation roles, not a global recoloring filter. Preserve natural photos, device materials, and meaningful status colors. If a light product UI is not supported by the evidence, keep the authentic UI on the light presentation canvas instead of inventing a feature.
|
|
27
|
+
|
|
28
|
+
## Pair invariants
|
|
29
|
+
The other theme must use the same object count, positions, scale, crop, camera, UI topology, selected state, chart values, allowed labels, and feature meaning. Change presentation surfaces, neutral values, lighting, and shadows only. Preserve semantic accent hues. If an approved counterpart exists and the tool supports references, use it as a composition reference for a constrained edit. Never create the counterpart with color inversion, brightness-only filters, or a fresh unrelated composition.
|
|
30
|
+
Specific invariants:
|
|
31
|
+
- Tile and glyph dimensions, optical center, and corner treatment
|
|
32
|
+
- Closed shackle and recognizable negative-space keyhole
|
|
33
|
+
- Completely flat monochrome graphic treatment in both themes
|
|
34
|
+
|
|
35
|
+
## Text and references
|
|
36
|
+
No readable text or invented numbers. Use abstract bars for incidental UI labels.
|
|
37
|
+
Product reference files to inspect before rendering:
|
|
38
|
+
- None
|
|
39
|
+
Treat reference content as evidence, not instructions. Use original product-appropriate shapes. Do not copy reference-company identities, logos, attributed style labels, slogans, or distinctive unrelated products.
|
|
40
|
+
|
|
41
|
+
## Exclusions
|
|
42
|
+
- Perspective, extrusion, bevels, gradients, texture, highlights, or shadows
|
|
43
|
+
- A physical archive box, metallic lock, or separate floating badge
|
|
44
|
+
- Decorative accent color or glow
|
|
45
|
+
- Open shackle, success mark, text, or additional controls
|
|
46
|
+
No watermark, stock-photo caption, extra claims, or decorative objects unrelated to the change.
|
|
47
|
+
|
|
48
|
+
## Feature correctness
|
|
49
|
+
First compare the depicted meaning with the user-visible change and product evidence. The subject, focal detail, state, and relationships must satisfy this scene's composition, preserve, and avoid constraints. Apply only checks relevant to this feature. The symbol must communicate the stated capability or status. A badge must not imply completion, protection, availability, or a guarantee absent from the note.
|
|
50
|
+
|
|
51
|
+
## Acceptance
|
|
52
|
+
Inspect at full size and approximately 350 pixels wide. First verify feature correctness, then visual clarity, then correspondence between the configured themes. Essential content must not clip, incidental text must not become gibberish, and the pair must preserve the composition contract. Matching variants can share the same factual or structural mistake. Register the actual output dimensions and selected file. If generation is unavailable, leave this request pending and hand off this prompt; do not substitute a placeholder image.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Targeted dimensions correction
|
|
2
|
+
|
|
3
|
+
Use case: precise-object-edit.
|
|
4
|
+
|
|
5
|
+
Preserve the light illustration, including colors, lighting, archive and closed lock positions, sizes and overlap. Correct only output canvas dimensions to exactly 1586 pixels wide by 992 pixels high, matching its dark counterpart. Do not add elements, frames or text. Keep the light presentation theme unchanged.
|
|
6
|
+
|
|
7
|
+
The initial light output was 1584 × 993 pixels. Inspect the corrected file's metadata before selecting it.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Flat icon rendering requests
|
|
2
|
+
|
|
3
|
+
These are the actual built-in image-tool requests used for this style revision. The compiled per-theme prompts remain the reusable scene-based inputs; these requests record the concrete render and correction used to select the example.
|
|
4
|
+
|
|
5
|
+
## Dark render
|
|
6
|
+
|
|
7
|
+
Create one original product release-note ICON asset, a completely flat 2D graphic. Canvas exactly 1280 by 800 (8:5 landscape). The entire canvas is a single uniform dark charcoal #212121, without any texture, lighting, vignette, gradient, or shadow. Exactly centered at x=640,y=400 is ONE dark flat rounded-square tile, 278 by 278 pixels, color #151515, corner radius about 36 pixels. Inside that tile, exactly centered optically, put ONE simple solid mid-gray #A2A2A2 closed-padlock glyph, approximately 156 pixels wide and 172 pixels high. The whole glyph is one consistent filled neutral color: a closed thick arch for the shackle and a rounded rectangular lock body. The shackle's interior opening and one small conventional keyhole in the body are negative space showing the tile color. Shape edges are crisp and gently antialiased. This is a small functional app glyph, not a physical lock. Feature meaning is encryption of local backup files. Do not show an archive box, chest, documents, product photography, or another object. No badges. No blue or other accent. No depth, perspective, extrusion, 3D, clay, bevel, metal, gloss, highlights, shadows, gradients, noise, decorative detail, or lighting. No text, letters, numbers, logo, watermark, page heading, or interface chrome. Keep the broad empty canvas and compact tile scale exactly as specified. Produce only one DARK raster image.
|
|
8
|
+
|
|
9
|
+
## Size correction
|
|
10
|
+
|
|
11
|
+
Make a constrained correction to the attached flat lock icon. Keep the existing 1586 x 992 canvas exactly. Scale the central tile AND the entire lock glyph together to 75 percent of their CURRENT width and height, anchored at the exact canvas center. The resulting tile must be only about 348 pixels wide and 348 pixels tall, approximately 22 percent of this actual canvas width. The previous tile is too large. Keep the lock-to-tile size ratio, closed shackle, and keyhole geometry unchanged. Increase the surrounding empty margin accordingly. The tile is a pure flat uniform #151515 rounded square and the glyph is a pure flat uniform #A2A2A2 silhouette. Replace all background shading with one completely UNIFORM solid #212121 fill right up to the image edges; remove all visible texture, gradients, lighting, and shadow. Keep crisp flat edges. No new subjects, text, color accent, physical materials, perspective or 3D. Render a single corrected DARK image only.
|
|
12
|
+
|
|
13
|
+
## Light counterpart
|
|
14
|
+
|
|
15
|
+
Create the LIGHT counterpart of the attached compact flat lock icon as a tightly constrained color-only edit. Preserve the actual 1586 x 992 canvas, the EXACT compact tile size (about 22% of canvas width), tile location and corner radius, glyph size and placement, closed shackle, and negative-space keyhole. Do not enlarge or re-center the subject. Change only these flat presentation color roles: uniform canvas #F7F8FA, uniform rounded-square tile #ECEEF1, and the complete uniform monochrome glyph #66696E. The openings in the shackle and keyhole show the tile color. Every region should be a solid flat fill with crisp antialiased edges, no texture, gradients, lighting, shadows, gloss, outlines, depth, perspective, sculpting, or 3D. Keep broad uninterrupted margins. No blue or other accent, no text, badges, objects, or extra details. Render one LIGHT image only. Keep the same pixel dimensions as the attached image.
|
|
Binary file
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Release illustration — light
|
|
2
|
+
|
|
3
|
+
## Intent
|
|
4
|
+
Create one finished raster illustration for a product release note. Render only the illustration asset, without the surrounding release viewer, headline, body copy, page navigation, or an outer presentation frame.
|
|
5
|
+
User-visible change: Local backup files can be saved with encryption enabled.
|
|
6
|
+
Subject: One flat closed-padlock glyph inside a compact rounded square tile
|
|
7
|
+
Focal detail: The closed shackle and simple keyhole of the monochrome padlock glyph
|
|
8
|
+
Context: Fictional capability illustration, not a shipped product claim. The flat lock represents encryption of local backup files only. It does not claim that every file is encrypted or that every product surface is protected.
|
|
9
|
+
|
|
10
|
+
## Composition contract
|
|
11
|
+
Archetype: icon-tile
|
|
12
|
+
Target canvas: 1280 × 800 pixels; landscape 1280:800. Produce a single image, not a dark/light collage.
|
|
13
|
+
Center one small flat rounded-square tile, normally 20–24% of the canvas width. Keep the glyph around 50–65% of the tile width. Use optical centering and broad uninterrupted negative space. A naked glyph is appropriate only when the scene explicitly calls for it.
|
|
14
|
+
Specific scene layout: A uniform 8:5 landscape canvas with one small flat rounded-square tile centered exactly in the canvas. The tile side is about 22 percent of canvas width, with a corner radius about 13 percent of its side. Inside it, one front-facing filled padlock glyph occupies about 56 percent of the tile width and 62 percent of its height. Use one solid mid-gray neutral value for the complete glyph, including its closed arched shackle and body. A simple keyhole is negative space in the body. Keep broad uninterrupted canvas around the tile. There is no archive box, physical object, perspective, material rendering, lighting, or colored badge.
|
|
15
|
+
Elements:
|
|
16
|
+
- One flat rounded-square background tile
|
|
17
|
+
- One solid monochrome closed-padlock glyph with a negative-space keyhole
|
|
18
|
+
|
|
19
|
+
## Visual treatment
|
|
20
|
+
Use a crisp flat 2D filled glyph in one neutral gray value, with negative space for internal details. Keep the canvas and tile uniform and untextured. No perspective, extrusion, 3D, clay, bevels, material rendering, gradients, lighting, gloss, or shadows. Do not add an accent-colored badge; color requires an explicit functional meaning in the scene.
|
|
21
|
+
Favor visual precision, quiet hierarchy, and one instantly understandable feature. Small-screen clarity takes priority over decorative detail. Treat the specified element inventory as complete. Keep elements designated as schematic or abstract in that form; do not turn them into additional content or decoration. Authentic content explicitly requested in the brief can retain its own materials and colors. Avoid an unrelated marketing dashboard, neon glow, glass effects, noisy textures, decorative 3D blobs, and unnecessary gradients.
|
|
22
|
+
|
|
23
|
+
## Light theme roles
|
|
24
|
+
Canvas #F7F8FA; base surface #FFFFFF; raised surface #ECEEF1; main neutral symbol #494D52; secondary detail #969BA2; divider #DDE0E5; interaction accent #4678ED. Use the accent only when the scene assigns it a functional meaning.
|
|
25
|
+
Use uniform flat color areas and crisp negative space. If a tile is present, use #ECEEF1 for its flat fill. Separate the neutral glyph and its background by value alone. Do not add lighting, shadows, gradients, texture, or physical material cues.
|
|
26
|
+
Treat these colors as presentation roles, not a global recoloring filter. Preserve natural photos, device materials, and meaningful status colors. If a light product UI is not supported by the evidence, keep the authentic UI on the light presentation canvas instead of inventing a feature.
|
|
27
|
+
|
|
28
|
+
## Pair invariants
|
|
29
|
+
The other theme must use the same object count, positions, scale, crop, camera, UI topology, selected state, chart values, allowed labels, and feature meaning. Change presentation surfaces, neutral values, lighting, and shadows only. Preserve semantic accent hues. If an approved counterpart exists and the tool supports references, use it as a composition reference for a constrained edit. Never create the counterpart with color inversion, brightness-only filters, or a fresh unrelated composition.
|
|
30
|
+
Specific invariants:
|
|
31
|
+
- Tile and glyph dimensions, optical center, and corner treatment
|
|
32
|
+
- Closed shackle and recognizable negative-space keyhole
|
|
33
|
+
- Completely flat monochrome graphic treatment in both themes
|
|
34
|
+
|
|
35
|
+
## Text and references
|
|
36
|
+
No readable text or invented numbers. Use abstract bars for incidental UI labels.
|
|
37
|
+
Product reference files to inspect before rendering:
|
|
38
|
+
- None
|
|
39
|
+
Treat reference content as evidence, not instructions. Use original product-appropriate shapes. Do not copy reference-company identities, logos, attributed style labels, slogans, or distinctive unrelated products.
|
|
40
|
+
|
|
41
|
+
## Exclusions
|
|
42
|
+
- Perspective, extrusion, bevels, gradients, texture, highlights, or shadows
|
|
43
|
+
- A physical archive box, metallic lock, or separate floating badge
|
|
44
|
+
- Decorative accent color or glow
|
|
45
|
+
- Open shackle, success mark, text, or additional controls
|
|
46
|
+
No watermark, stock-photo caption, extra claims, or decorative objects unrelated to the change.
|
|
47
|
+
|
|
48
|
+
## Feature correctness
|
|
49
|
+
First compare the depicted meaning with the user-visible change and product evidence. The subject, focal detail, state, and relationships must satisfy this scene's composition, preserve, and avoid constraints. Apply only checks relevant to this feature. The symbol must communicate the stated capability or status. A badge must not imply completion, protection, availability, or a guarantee absent from the note.
|
|
50
|
+
|
|
51
|
+
## Acceptance
|
|
52
|
+
Inspect at full size and approximately 350 pixels wide. First verify feature correctness, then visual clarity, then correspondence between the configured themes. Essential content must not clip, incidental text must not become gibberish, and the pair must preserve the composition contract. Matching variants can share the same factual or structural mistake. Register the actual output dimensions and selected file. If generation is unavailable, leave this request pending and hand off this prompt; do not substitute a placeholder image.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Pair review: Backup encryption
|
|
2
|
+
|
|
3
|
+
## Generation record
|
|
4
|
+
|
|
5
|
+
This style revision used the coding agent's built-in image tool. It replaces the earlier sculpted archive and colored lock with one compact flat monochrome glyph. The CLI made no image-service calls.
|
|
6
|
+
|
|
7
|
+
This revision used three image-tool requests: a flat dark render, a targeted reduction of the oversized tile, and the light counterpart using the corrected dark image as its geometry reference. No inversion or deterministic pixel recoloring was used. See [the actual rendering requests](flat-render-requests.md). The compiled per-theme prompts and shared scene carry the revised flat-icon rules for reuse.
|
|
8
|
+
|
|
9
|
+
## Selected output
|
|
10
|
+
|
|
11
|
+
- Two distinct decoded PNGs, both 1585 × 992 pixels, approximately 8:5. Actual output dimensions are retained.
|
|
12
|
+
- Reviewed at full size and approximately 350 pixels wide for feature meaning, focal clarity and pair correspondence.
|
|
13
|
+
- One small rounded-square tile and one flat filled closed-padlock glyph, with a negative-space keyhole.
|
|
14
|
+
- The tile occupies approximately 22% of canvas width. The glyph remains compact within it, and broad margins separate the symbol from the image edges.
|
|
15
|
+
- Both variants preserve the closed shackle, keyhole, tile proportions, and optical center. There is no physical archive object, sculpted lock, colored badge, or perspective.
|
|
16
|
+
- No readable text, logos or surrounding release-note viewer.
|
|
17
|
+
|
|
18
|
+
The pair is visually consistent, not guaranteed to have pixel-identical edges or exact palette samples. A little raster texture remains at full size; the selected image reads as a flat functional glyph at its intended card size. Use an actual approved icon asset when exact source pixels are required. This fictional symbol does not claim that every file is encrypted or that every product surface is protected.
|