brand-intent 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brand-intent",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "An open file format specification for making brand intent machine-readable.",
5
5
  "author": "Christophe Stoll <christophe@stoll.studio>",
6
6
  "license": "MIT",
@@ -35,6 +35,8 @@ export interface ParsedPurpose {
35
35
  defaultVideo?: string;
36
36
  /** When false, hides Take Photo / Upload buttons for this purpose. Default: true. */
37
37
  camera?: boolean;
38
+ /** When false, photo/video analysis skips this purpose (no AI text generation). Default: true. */
39
+ analyze?: boolean;
38
40
  /** Default icon (Phosphor PascalCase name, e.g. "ArrowRight"). */
39
41
  defaultIcon?: string;
40
42
  /** Default icon weight. */
@@ -48,6 +50,8 @@ export interface ParsedPurposeSlot {
48
50
  label: string;
49
51
  /** Sample text variants for this slot. Picked randomly on page creation. */
50
52
  samples: string[];
53
+ /** Fixed value — when set, this slot is never AI-generated or shuffled. Mutually exclusive with samples. */
54
+ value?: string;
51
55
  maxLength: number;
52
56
  /** 'text' (default) or 'list'. */
53
57
  slotType?: 'text' | 'list';
@@ -63,6 +67,8 @@ export interface ParsedPurposeSlot {
63
67
  itemGap?: number;
64
68
  /** List layout direction: 'vertical' (default) or 'horizontal'. */
65
69
  listDirection?: 'vertical' | 'horizontal';
70
+ /** When true, this slot is editable by the user in consume mode. Default: false. */
71
+ editable?: boolean;
66
72
  typography: {
67
73
  fontFamily: string;
68
74
  fontWeight: number;
@@ -122,6 +128,7 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
122
128
  let defaultPhoto: string | undefined;
123
129
  let defaultVideo: string | undefined;
124
130
  let camera: boolean | undefined;
131
+ let analyze: boolean | undefined;
125
132
  let defaultIcon: string | undefined;
126
133
  let defaultIconWeight: string | undefined;
127
134
  let bgPrompt: string | undefined;
@@ -143,7 +150,7 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
143
150
  const s = currentSlot;
144
151
  if (!s.id) throw new Error(`${fileName}:${lineNum}: slot missing id`);
145
152
  if (!s.label) throw new Error(`${fileName}:${lineNum}: slot "${s.id}" missing label`);
146
- if ((!s.samples || s.samples.length === 0) && !s.iconOnly) throw new Error(`${fileName}:${lineNum}: slot "${s.id}" missing samples`);
153
+ if ((!s.samples || s.samples.length === 0) && !s.iconOnly && !s.value) throw new Error(`${fileName}:${lineNum}: slot "${s.id}" missing samples or value`);
147
154
  if (!s.maxLength) throw new Error(`${fileName}:${lineNum}: slot "${s.id}" missing maxLength`);
148
155
  const t = s.typography as ParsedPurposeSlot['typography'];
149
156
  if (!s.typographyRef) {
@@ -296,6 +303,12 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
296
303
  continue;
297
304
  }
298
305
 
306
+ // Photo/video analysis control
307
+ if (line.startsWith('analyze:')) {
308
+ analyze = line.slice(8).trim() !== 'false';
309
+ continue;
310
+ }
311
+
299
312
  // Background placeholder prompt: "bg-prompt: Mach dein Selfie"
300
313
  if (line.startsWith('bg-prompt:')) {
301
314
  bgPrompt = line.slice(10).trim().replace(/^["']|["']$/g, '');
@@ -501,6 +514,14 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
501
514
  currentSlot.label = prop.slice(6).trim();
502
515
  continue;
503
516
  }
517
+ if (prop.startsWith('value:')) {
518
+ const val = prop.slice(6).trim();
519
+ if (!val) throw new Error(`${fileName}:${lineNum}: empty value`);
520
+ currentSlot.value = val;
521
+ // value implies a single-entry samples array for backward compat
522
+ currentSlot.samples = [val];
523
+ continue;
524
+ }
504
525
  if (prop.startsWith('samples:')) {
505
526
  const raw = prop.slice(8).trim();
506
527
  if (!raw) {
@@ -526,6 +547,8 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
526
547
  currentSlot.colorRef = prop.slice(6).trim();
527
548
  continue;
528
549
  }
550
+ if (prop === 'editable' || prop === 'editable: true' || prop === 'editable:true') { currentSlot.editable = true; continue; }
551
+ if (prop === 'editable: false' || prop === 'editable:false') { currentSlot.editable = false; continue; }
529
552
  if (prop.startsWith('font:')) {
530
553
  // "Die Grotesk 800" → family="Die Grotesk", weight=800 (legacy direct path)
531
554
  const value = prop.slice(5).trim();
@@ -601,5 +624,5 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
601
624
  context = contextLines.join('\n').trim();
602
625
  }
603
626
 
604
- return { id, name, description, preferredCompositions: compositions, slots, density, palette, scope, context, voice, defaultLogo, defaultPhoto, defaultVideo, camera, defaultIcon, defaultIconWeight, bgPrompt };
627
+ return { id, name, description, preferredCompositions: compositions, slots, density, palette, scope, context, voice, defaultLogo, defaultPhoto, defaultVideo, camera, analyze, defaultIcon, defaultIconWeight, bgPrompt };
605
628
  }