brand-intent 0.14.0 → 0.16.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 +1 -1
- package/parser/brandParser.ts +135 -4
- package/parser/colorSystem.ts +17 -0
- package/parser/index.ts +2 -1
- package/parser/purposeParser.ts +16 -2
- package/parser/types.ts +2 -0
- package/vscode-extension/syntaxes/brand.tmLanguage.json +23 -2
- package/vscode-extension/syntaxes/purpose.tmLanguage.json +10 -2
package/package.json
CHANGED
package/parser/brandParser.ts
CHANGED
|
@@ -112,6 +112,27 @@ export interface ParsedBrand {
|
|
|
112
112
|
icons?: ParsedIconDef[];
|
|
113
113
|
/** List counter component (`counter` block). */
|
|
114
114
|
counter?: ParsedCounterDef;
|
|
115
|
+
/** Photo framing modules (`framing NAME` blocks), in declaration order. Absent → no camera guides. */
|
|
116
|
+
framings?: ParsedFramingDef[];
|
|
117
|
+
/** Camera defaults (`camera` block). Absent → rear camera, no grid. */
|
|
118
|
+
camera?: ParsedCameraDef;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface ParsedCameraDef {
|
|
122
|
+
/** Which camera opens first. */
|
|
123
|
+
facing: 'front' | 'rear';
|
|
124
|
+
/** Viewfinder grid as rows × columns of cells. Absent → no grid. */
|
|
125
|
+
grid?: { rows: number; cols: number };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ParsedFramingDef {
|
|
129
|
+
id: string;
|
|
130
|
+
/** Display name. Defaults to the id. */
|
|
131
|
+
label: string;
|
|
132
|
+
/** Horizon position as % of image height, measured from the top. null → no horizon (close-ups). */
|
|
133
|
+
horizon: number | null;
|
|
134
|
+
/** The photographic task, one line. */
|
|
135
|
+
description?: string;
|
|
115
136
|
}
|
|
116
137
|
|
|
117
138
|
export type CounterFormat = '1' | '01' | '1.' | '(1)' | 'i';
|
|
@@ -229,6 +250,14 @@ const VALID_STRATEGIES = new Set<PaletteStrategy>(['monochrome', 'duotone', 'tri
|
|
|
229
250
|
const VALID_ADHERENCES = new Set<Adherence>(['strict', 'on-brand', 'flexible']);
|
|
230
251
|
const TONE_CLASSES = new Set<ToneClass>(['light', 'mid', 'dark']);
|
|
231
252
|
const FAMILY_ROLES = new Set<FamilyRole>(['core', 'accent', 'contrast']);
|
|
253
|
+
|
|
254
|
+
const DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
255
|
+
function isMonthDay(value: string): boolean {
|
|
256
|
+
const m = value.match(/^(\d{2})-(\d{2})$/);
|
|
257
|
+
if (!m) return false;
|
|
258
|
+
const month = Number(m[1]), day = Number(m[2]);
|
|
259
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= DAYS_IN_MONTH[month - 1];
|
|
260
|
+
}
|
|
232
261
|
const FAMILY_ORDERS = new Set<FamilyOrder>(['cycle', 'sequence']);
|
|
233
262
|
const FAMILY_BORROWS = new Set<FamilyBorrow>(['previous', 'next', 'both', 'none']);
|
|
234
263
|
const PAIR_KEYS = ['ground', 'figure', 'text'] as const;
|
|
@@ -351,7 +380,7 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
351
380
|
tokensSource: 'figma',
|
|
352
381
|
};
|
|
353
382
|
|
|
354
|
-
type ActiveBlock = 'none' | 'brand-colors' | 'state-colors' | 'color-bounds' | 'color-adherence' | 'color-rules' | 'color-families' | 'theme' | 'spacing' | 'divider' | 'typography' | 'font' | 'logo' | 'photo' | 'video' | 'illustration' | 'label' | 'cta' | 'icons' | 'counter';
|
|
383
|
+
type ActiveBlock = 'none' | 'brand-colors' | 'state-colors' | 'color-bounds' | 'color-adherence' | 'color-rules' | 'color-families' | 'theme' | 'spacing' | 'divider' | 'typography' | 'font' | 'logo' | 'photo' | 'video' | 'illustration' | 'label' | 'cta' | 'icons' | 'counter' | 'framing' | 'camera';
|
|
355
384
|
let activeBlock: ActiveBlock = 'none';
|
|
356
385
|
let currentThemeName = '';
|
|
357
386
|
let currentDividerName = '';
|
|
@@ -374,7 +403,7 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
374
403
|
const adherenceOverridesRaw: Record<string, string> = {};
|
|
375
404
|
const colorRulesRaw: Record<string, string> = {};
|
|
376
405
|
let colorRulesDeclared = false;
|
|
377
|
-
type RawFamily = { name: string; line: number; colors: Array<{ role: string; value: string; line: number }>; pairs: Array<{ ground: string; figure: string; line: number }> };
|
|
406
|
+
type RawFamily = { name: string; line: number; from?: string; colors: Array<{ role: string; value: string; line: number }>; pairs: Array<{ ground: string; figure: string; line: number }> };
|
|
378
407
|
const familiesProps: Record<string, string> = {};
|
|
379
408
|
const familiesRaw: RawFamily[] = [];
|
|
380
409
|
let familiesDeclared = false;
|
|
@@ -398,6 +427,9 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
398
427
|
let iconsDeclared = false;
|
|
399
428
|
const counterRaw: Record<string, { value: string; line: number }> = {};
|
|
400
429
|
let counterDeclared = false;
|
|
430
|
+
const cameraRaw: Record<string, { value: string; line: number }> = {};
|
|
431
|
+
let cameraDeclared = false;
|
|
432
|
+
const framingsRaw: Array<{ id: string; line: number; props: Record<string, { value: string; line: number }> }> = [];
|
|
401
433
|
|
|
402
434
|
for (let i = 0; i < lines.length; i++) {
|
|
403
435
|
const raw = lines[i];
|
|
@@ -629,8 +661,16 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
629
661
|
}
|
|
630
662
|
const colonIdx = trimmed.indexOf(':');
|
|
631
663
|
const key = colonIdx !== -1 ? trimmed.slice(0, colonIdx).trim() : trimmed;
|
|
664
|
+
if (key === 'from' && colonIdx !== -1) {
|
|
665
|
+
const value = trimmed.slice(colonIdx + 1).trim();
|
|
666
|
+
if (currentFamily.from !== undefined) throw new Error(`${fileName}:${lineNum}: family "${currentFamily.name}" declares from: twice`);
|
|
667
|
+
if (!isMonthDay(value)) throw new Error(`${fileName}:${lineNum}: family "${currentFamily.name}" from: must be "MM-DD" (e.g. 12-01), got "${value}"`);
|
|
668
|
+
inPairs = false;
|
|
669
|
+
currentFamily.from = value;
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
632
672
|
if (colonIdx === -1 || !FAMILY_ROLES.has(key as FamilyRole)) {
|
|
633
|
-
throw new Error(`${fileName}:${lineNum}: family "${currentFamily.name}": expected core / accent / contrast / pairs, got "${trimmed}"`);
|
|
673
|
+
throw new Error(`${fileName}:${lineNum}: family "${currentFamily.name}": expected from / core / accent / contrast / pairs, got "${trimmed}"`);
|
|
634
674
|
}
|
|
635
675
|
inPairs = false;
|
|
636
676
|
currentFamily.colors.push({ role: key, value: trimmed.slice(colonIdx + 1).trim(), line: lineNum });
|
|
@@ -767,6 +807,24 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
767
807
|
counterRaw[entry.slice(0, colonIdx).trim()] = { value: entry.slice(colonIdx + 1).trim(), line: lineNum };
|
|
768
808
|
continue;
|
|
769
809
|
}
|
|
810
|
+
if (activeBlock === 'camera') {
|
|
811
|
+
const trimmed = raw.trim();
|
|
812
|
+
if (trimmed.startsWith('#')) continue;
|
|
813
|
+
const commentIdx = trimmed.search(/\s+#/);
|
|
814
|
+
const entry = commentIdx !== -1 ? trimmed.slice(0, commentIdx) : trimmed;
|
|
815
|
+
const colonIdx = entry.indexOf(':');
|
|
816
|
+
if (colonIdx === -1) throw new Error(`${fileName}:${lineNum}: camera property must be "KEY: VALUE", got "${trimmed}"`);
|
|
817
|
+
cameraRaw[entry.slice(0, colonIdx).trim()] = { value: entry.slice(colonIdx + 1).trim(), line: lineNum };
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
if (activeBlock === 'framing') {
|
|
821
|
+
const trimmed = raw.trim();
|
|
822
|
+
if (trimmed.startsWith('#')) continue;
|
|
823
|
+
const colonIdx = trimmed.indexOf(':');
|
|
824
|
+
if (colonIdx === -1) throw new Error(`${fileName}:${lineNum}: framing property must be "KEY: VALUE", got "${trimmed}"`);
|
|
825
|
+
framingsRaw[framingsRaw.length - 1].props[trimmed.slice(0, colonIdx).trim()] = { value: trimmed.slice(colonIdx + 1).trim(), line: lineNum };
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
770
828
|
// Indented line with no active block — ignore
|
|
771
829
|
continue;
|
|
772
830
|
}
|
|
@@ -831,6 +889,24 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
831
889
|
continue;
|
|
832
890
|
}
|
|
833
891
|
|
|
892
|
+
// camera — camera defaults (facing, viewfinder grid)
|
|
893
|
+
if (raw.trim() === 'camera') {
|
|
894
|
+
if (cameraDeclared) throw new Error(`${fileName}:${lineNum}: duplicate camera block`);
|
|
895
|
+
cameraDeclared = true;
|
|
896
|
+
activeBlock = 'camera';
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// framing <id> — photo framing module (horizon position)
|
|
901
|
+
const framingMatch = raw.match(/^framing\s+(\S+)\s*$/);
|
|
902
|
+
if (framingMatch) {
|
|
903
|
+
const id = framingMatch[1];
|
|
904
|
+
if (framingsRaw.some(f => f.id === id)) throw new Error(`${fileName}:${lineNum}: duplicate framing "${id}"`);
|
|
905
|
+
framingsRaw.push({ id, line: lineNum, props: {} });
|
|
906
|
+
activeBlock = 'framing';
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
|
|
834
910
|
// theme ThemeName — starts a theme block
|
|
835
911
|
const themeMatch = raw.match(/^theme\s+(\S+)/);
|
|
836
912
|
if (themeMatch) {
|
|
@@ -1090,7 +1166,16 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
1090
1166
|
}
|
|
1091
1167
|
brandColors[c.key] = c.hex;
|
|
1092
1168
|
}
|
|
1093
|
-
families.push({ name: raw.name, colors, pairs: [] });
|
|
1169
|
+
families.push({ name: raw.name, ...(raw.from ? { from: raw.from } : {}), colors, pairs: [] });
|
|
1170
|
+
}
|
|
1171
|
+
const dated = familiesRaw.filter(f => f.from !== undefined);
|
|
1172
|
+
if (dated.length > 0 && dated.length < familiesRaw.length) {
|
|
1173
|
+
const missing = familiesRaw.filter(f => f.from === undefined).map(f => f.name).join(', ');
|
|
1174
|
+
throw new Error(`${fileName}: color-families: from: must be declared on every family or on none (missing: ${missing})`);
|
|
1175
|
+
}
|
|
1176
|
+
for (const f of dated) {
|
|
1177
|
+
const twin = dated.find(o => o !== f && o.from === f.from);
|
|
1178
|
+
if (twin) throw new Error(`${fileName}:${f.line}: families "${twin.name}" and "${f.name}" share from: ${f.from}`);
|
|
1094
1179
|
}
|
|
1095
1180
|
colorFamilies = { order, borrow, distance, shared, families };
|
|
1096
1181
|
if (borrow !== 'none' && reachCoversAll(families.length, { order, borrow, distance })) {
|
|
@@ -1473,6 +1558,52 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
1473
1558
|
result.counter = counter;
|
|
1474
1559
|
}
|
|
1475
1560
|
|
|
1561
|
+
// Finalise camera defaults
|
|
1562
|
+
if (cameraDeclared) {
|
|
1563
|
+
const known = new Set(['facing', 'grid']);
|
|
1564
|
+
for (const [key, { line }] of Object.entries(cameraRaw)) {
|
|
1565
|
+
if (!known.has(key)) throw new Error(`${fileName}:${line}: unknown camera property "${key}"`);
|
|
1566
|
+
}
|
|
1567
|
+
const facing = cameraRaw['facing']?.value ?? 'rear';
|
|
1568
|
+
if (facing !== 'front' && facing !== 'rear') {
|
|
1569
|
+
throw new Error(`${fileName}:${cameraRaw['facing'].line}: camera facing must be front | rear, got "${facing}"`);
|
|
1570
|
+
}
|
|
1571
|
+
const camera: ParsedCameraDef = { facing };
|
|
1572
|
+
const g = cameraRaw['grid'];
|
|
1573
|
+
if (g) {
|
|
1574
|
+
// "3" → 3 × 3, "3 4" → 3 rows × 4 columns
|
|
1575
|
+
const nums = g.value.split(/\s+/).map(Number);
|
|
1576
|
+
const [rows, cols = rows] = nums;
|
|
1577
|
+
if (nums.length > 2 || ![rows, cols].every(n => Number.isInteger(n) && n >= 2 && n <= 12)) {
|
|
1578
|
+
throw new Error(`${fileName}:${g.line}: camera grid must be "N" or "ROWS COLS" (2–12), got "${g.value}"`);
|
|
1579
|
+
}
|
|
1580
|
+
camera.grid = { rows, cols };
|
|
1581
|
+
}
|
|
1582
|
+
result.camera = camera;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// Finalise framing modules
|
|
1586
|
+
if (framingsRaw.length > 0) {
|
|
1587
|
+
const known = new Set(['label', 'horizon', 'description']);
|
|
1588
|
+
result.framings = framingsRaw.map(({ id, line, props }) => {
|
|
1589
|
+
for (const [key, { line: l }] of Object.entries(props)) {
|
|
1590
|
+
if (!known.has(key)) throw new Error(`${fileName}:${l}: unknown framing property "${key}"`);
|
|
1591
|
+
}
|
|
1592
|
+
const h = props['horizon'];
|
|
1593
|
+
if (!h) throw new Error(`${fileName}:${line}: framing "${id}" needs "horizon: N%" or "horizon: none"`);
|
|
1594
|
+
let horizon: number | null = null;
|
|
1595
|
+
if (h.value !== 'none') {
|
|
1596
|
+
const m = h.value.match(/^(\d+(?:\.\d+)?)%$/);
|
|
1597
|
+
const n = m ? parseFloat(m[1]) : NaN;
|
|
1598
|
+
if (!(n > 0 && n < 100)) throw new Error(`${fileName}:${h.line}: framing horizon must be a percentage between 0% and 100% or "none", got "${h.value}"`);
|
|
1599
|
+
horizon = n;
|
|
1600
|
+
}
|
|
1601
|
+
const def: ParsedFramingDef = { id, label: props['label']?.value || id, horizon };
|
|
1602
|
+
if (props['description']?.value) def.description = props['description'].value;
|
|
1603
|
+
return def;
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1476
1607
|
// Validate required fields
|
|
1477
1608
|
const required: (keyof ParsedBrand)[] = ['id', 'name', 'language', 'locale'];
|
|
1478
1609
|
for (const field of required) {
|
package/parser/colorSystem.ts
CHANGED
|
@@ -193,6 +193,23 @@ export function colorOrigin(
|
|
|
193
193
|
return null;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Family in season on `date`: the one with the latest `from:` on or before that
|
|
198
|
+
* month/day, wrapping to the year's last start before the first one. Uses the
|
|
199
|
+
* date's local calendar day. -1 when the families declare no dates.
|
|
200
|
+
*/
|
|
201
|
+
export function familyIndexForDate(families: Pick<ColorFamily, 'from'>[], date: Date): number {
|
|
202
|
+
if (families.length === 0 || families.some(f => !f.from)) return -1;
|
|
203
|
+
const today = `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
204
|
+
let best = -1;
|
|
205
|
+
let latest = -1;
|
|
206
|
+
families.forEach((f, i) => {
|
|
207
|
+
if (f.from! <= today && (best === -1 || f.from! > families[best].from!)) best = i;
|
|
208
|
+
if (latest === -1 || f.from! > families[latest].from!) latest = i;
|
|
209
|
+
});
|
|
210
|
+
return best !== -1 ? best : latest;
|
|
211
|
+
}
|
|
212
|
+
|
|
196
213
|
/** Name of the family that declares `hex` (first match), for error messages. */
|
|
197
214
|
export function owningFamily(hex: string, families: ColorFamily[]): string | undefined {
|
|
198
215
|
return families.find(f => f.colors.some(c => c.hex === hex))?.name;
|
package/parser/index.ts
CHANGED
|
@@ -20,10 +20,11 @@ export {
|
|
|
20
20
|
familyTextCandidates,
|
|
21
21
|
checkFamilyPair,
|
|
22
22
|
enumerateFamilyPairs,
|
|
23
|
+
familyIndexForDate,
|
|
23
24
|
DEFAULT_COLOR_RULES,
|
|
24
25
|
} from './colorSystem';
|
|
25
26
|
export type { ReachConfig } from './colorSystem';
|
|
26
|
-
export type { ParsedBrand, ParsedFontDef, ParsedTypographyDef, ParsedDividerDef, BrandColorPrintSpec, ParsedAssetDef, ParsedBadgeStyle, LogoAlign, ParsedCounterDef, CounterFormat, CounterShape } from './brandParser';
|
|
27
|
+
export type { ParsedBrand, ParsedFontDef, ParsedTypographyDef, ParsedDividerDef, BrandColorPrintSpec, ParsedAssetDef, ParsedBadgeStyle, LogoAlign, ParsedCounterDef, CounterFormat, CounterShape, ParsedFramingDef, ParsedCameraDef } from './brandParser';
|
|
27
28
|
|
|
28
29
|
export { parseIconRef, parseIconSource, resolveIconRef, parseListItem, phosphorName, PHOSPHOR_WEIGHTS } from './icons';
|
|
29
30
|
export type { IconRef, IconSource, ParsedIconDef, ResolvedIcon, PhosphorWeight } from './icons';
|
package/parser/purposeParser.ts
CHANGED
|
@@ -38,6 +38,10 @@ export interface ParsedPurpose {
|
|
|
38
38
|
defaultVideo?: string;
|
|
39
39
|
/** When false, hides Take Photo / Upload buttons for this purpose. Default: true. */
|
|
40
40
|
camera?: boolean;
|
|
41
|
+
/** `camera: front | rear` — overrides the brand's camera facing for this purpose. */
|
|
42
|
+
cameraFacing?: 'front' | 'rear';
|
|
43
|
+
/** `framing: <id>` — the brand framing module photos for this purpose are shot in. */
|
|
44
|
+
framing?: string;
|
|
41
45
|
/** When false, photo/video analysis skips this purpose (no AI text generation). Default: true. */
|
|
42
46
|
analyze?: boolean;
|
|
43
47
|
/** Default icon for the icon slot. Resolves in the brand set first, then Phosphor (see resolveIconRef). */
|
|
@@ -132,6 +136,8 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
|
|
|
132
136
|
let defaultPhoto: string | undefined;
|
|
133
137
|
let defaultVideo: string | undefined;
|
|
134
138
|
let camera: boolean | undefined;
|
|
139
|
+
let cameraFacing: 'front' | 'rear' | undefined;
|
|
140
|
+
let framing: string | undefined;
|
|
135
141
|
let analyze: boolean | undefined;
|
|
136
142
|
let defaultIcon: IconRef | undefined;
|
|
137
143
|
let bgPrompt: string | undefined;
|
|
@@ -315,7 +321,15 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
|
|
|
315
321
|
|
|
316
322
|
// Camera control
|
|
317
323
|
if (line.startsWith('camera:')) {
|
|
318
|
-
|
|
324
|
+
const value = line.slice(7).trim();
|
|
325
|
+
camera = value !== 'false';
|
|
326
|
+
if (value === 'front' || value === 'rear') cameraFacing = value;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Framing module: one of the brand's `framing NAME` blocks
|
|
331
|
+
if (line.startsWith('framing:')) {
|
|
332
|
+
framing = line.slice(8).trim() || undefined;
|
|
319
333
|
continue;
|
|
320
334
|
}
|
|
321
335
|
|
|
@@ -652,5 +666,5 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
|
|
|
652
666
|
context = contextLines.join('\n').trim();
|
|
653
667
|
}
|
|
654
668
|
|
|
655
|
-
return { id, name, description, preferredCompositions: compositions, slots, density, palette, scope, context, voice, defaultLogo, defaultPhoto, defaultVideo, camera, analyze, defaultIcon, bgPrompt };
|
|
669
|
+
return { id, name, description, preferredCompositions: compositions, slots, density, palette, scope, context, voice, defaultLogo, defaultPhoto, defaultVideo, camera, cameraFacing, framing, analyze, defaultIcon, bgPrompt };
|
|
656
670
|
}
|
package/parser/types.ts
CHANGED
|
@@ -156,6 +156,8 @@ export interface FamilyPair extends PairCheck {
|
|
|
156
156
|
|
|
157
157
|
export interface ColorFamily {
|
|
158
158
|
name: string;
|
|
159
|
+
/** Start date `MM-DD` (`from:`). Declared on every family or on none. */
|
|
160
|
+
from?: string;
|
|
159
161
|
colors: FamilyColor[];
|
|
160
162
|
pairs: FamilyPair[];
|
|
161
163
|
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
{ "include": "#color-rules-entry" },
|
|
16
16
|
{ "include": "#color-families-property" },
|
|
17
17
|
{ "include": "#family-header" },
|
|
18
|
+
{ "include": "#family-from" },
|
|
18
19
|
{ "include": "#family-role" },
|
|
19
20
|
{ "include": "#family-pairs-keyword" },
|
|
20
21
|
{ "include": "#color-mode-enum" },
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
{ "include": "#icons-counter-header" },
|
|
32
33
|
{ "include": "#icon-entry" },
|
|
33
34
|
{ "include": "#counter-value" },
|
|
35
|
+
{ "include": "#framing-horizon" },
|
|
34
36
|
{ "include": "#theme-header" },
|
|
35
37
|
{ "include": "#brand-color-entry" },
|
|
36
38
|
{ "include": "#brand-color-print-sub" },
|
|
@@ -142,6 +144,15 @@
|
|
|
142
144
|
"5": { "name": "string.unquoted.brand-intent-brand" }
|
|
143
145
|
}
|
|
144
146
|
},
|
|
147
|
+
"family-from": {
|
|
148
|
+
"comment": "Family start date: from: MM-DD",
|
|
149
|
+
"match": "^(\\s{4,})(from)(:\\s*)(\\d{2}-\\d{2})\\s*$",
|
|
150
|
+
"captures": {
|
|
151
|
+
"2": { "name": "support.type.property-name.brand-intent-brand" },
|
|
152
|
+
"3": { "name": "punctuation.separator.key-value.brand-intent-brand" },
|
|
153
|
+
"4": { "name": "constant.numeric.brand-intent-brand" }
|
|
154
|
+
}
|
|
155
|
+
},
|
|
145
156
|
"family-pairs-keyword": {
|
|
146
157
|
"match": "^(\\s{4,})(pairs)\\s*$",
|
|
147
158
|
"captures": {
|
|
@@ -366,7 +377,7 @@
|
|
|
366
377
|
}
|
|
367
378
|
},
|
|
368
379
|
"asset-header": {
|
|
369
|
-
"match": "^(logo|photo|video|illustration)\\s+(\\S+)",
|
|
380
|
+
"match": "^(logo|photo|video|illustration|framing)\\s+(\\S+)",
|
|
370
381
|
"captures": {
|
|
371
382
|
"1": { "name": "keyword.control.brand-intent-brand" },
|
|
372
383
|
"2": { "name": "entity.name.type.brand-intent-brand" }
|
|
@@ -410,6 +421,16 @@
|
|
|
410
421
|
"11": { "name": "comment.line.number-sign.brand-intent-brand" }
|
|
411
422
|
}
|
|
412
423
|
},
|
|
424
|
+
"framing-horizon": {
|
|
425
|
+
"comment": "framing block horizon: N% | none",
|
|
426
|
+
"match": "^(\\s{2,})(horizon)(:\\s*)(\\d+(?:\\.\\d+)?%|none)\\s*(#.*)?$",
|
|
427
|
+
"captures": {
|
|
428
|
+
"2": { "name": "support.type.property-name.brand-intent-brand" },
|
|
429
|
+
"3": { "name": "punctuation.separator.key-value.brand-intent-brand" },
|
|
430
|
+
"4": { "name": "constant.numeric.brand-intent-brand" },
|
|
431
|
+
"5": { "name": "comment.line.number-sign.brand-intent-brand" }
|
|
432
|
+
}
|
|
433
|
+
},
|
|
413
434
|
"counter-value": {
|
|
414
435
|
"comment": "counter block properties (typography: is covered by typography-value)",
|
|
415
436
|
"match": "^(\\s{2,})(format|shape|size|padding)(:\\s*)(\\S+)\\s*(#.*)?$",
|
|
@@ -445,7 +466,7 @@
|
|
|
445
466
|
},
|
|
446
467
|
"keyword-partial": {
|
|
447
468
|
"comment": "Fallback: color the keyword token on incomplete lines (while typing)",
|
|
448
|
-
"match": "^(id:|name:|language:|locale:|domain:|app-icon:|favicon:|og-image:|tokens:|figma-file-key:|figma-collection:|tokens-file:|spacing:|label:|cta:|color-mode:|color-strategy:|color-adherence:|brand-colors|state-colors|color-bounds|color-rules|color-families|theme|divider|font|typography|logo|photo|video|illustration|icons|counter)(?=\\s|$)",
|
|
469
|
+
"match": "^(id:|name:|language:|locale:|domain:|app-icon:|favicon:|og-image:|tokens:|figma-file-key:|figma-collection:|tokens-file:|spacing:|label:|cta:|color-mode:|color-strategy:|color-adherence:|brand-colors|state-colors|color-bounds|color-rules|color-families|theme|divider|font|typography|logo|photo|video|illustration|icons|counter|framing)(?=\\s|$)",
|
|
449
470
|
"captures": {
|
|
450
471
|
"1": { "name": "keyword.control.brand-intent-brand" }
|
|
451
472
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
{ "include": "#palette-header" },
|
|
15
15
|
{ "include": "#asset-field" },
|
|
16
16
|
{ "include": "#camera-field" },
|
|
17
|
+
{ "include": "#framing-field" },
|
|
17
18
|
{ "include": "#analyze-field" },
|
|
18
19
|
{ "include": "#bg-prompt-field" },
|
|
19
20
|
{ "include": "#context-header" },
|
|
@@ -114,12 +115,19 @@
|
|
|
114
115
|
}
|
|
115
116
|
},
|
|
116
117
|
"camera-field": {
|
|
117
|
-
"match": "^(camera:)\\s+(true|false)",
|
|
118
|
+
"match": "^(camera:)\\s+(true|false|front|rear)",
|
|
118
119
|
"captures": {
|
|
119
120
|
"1": { "name": "keyword.control.brand-intent-purpose" },
|
|
120
121
|
"2": { "name": "constant.language.brand-intent-purpose" }
|
|
121
122
|
}
|
|
122
123
|
},
|
|
124
|
+
"framing-field": {
|
|
125
|
+
"match": "^(framing:)\\s+([\\w-]+)",
|
|
126
|
+
"captures": {
|
|
127
|
+
"1": { "name": "keyword.control.brand-intent-purpose" },
|
|
128
|
+
"2": { "name": "entity.name.type.brand-intent-purpose" }
|
|
129
|
+
}
|
|
130
|
+
},
|
|
123
131
|
"analyze-field": {
|
|
124
132
|
"match": "^(analyze:)\\s+(true|false)",
|
|
125
133
|
"captures": {
|
|
@@ -242,7 +250,7 @@
|
|
|
242
250
|
"comment": "Fallback: color the keyword token on incomplete lines (while typing)",
|
|
243
251
|
"patterns": [
|
|
244
252
|
{
|
|
245
|
-
"match": "^(id:|name:|compositions:|density:|defaultIcon:|scope|voice|palette:|logo:|photo:|video:|camera:|analyze:|bg-prompt:|context|slot)(?=\\s|$)",
|
|
253
|
+
"match": "^(id:|name:|compositions:|density:|defaultIcon:|scope|voice|palette:|logo:|photo:|video:|camera:|framing:|analyze:|bg-prompt:|context|slot)(?=\\s|$)",
|
|
246
254
|
"captures": {
|
|
247
255
|
"1": { "name": "keyword.control.brand-intent-purpose" }
|
|
248
256
|
}
|