brand-intent 0.32.0 → 0.34.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
package/parser/brandParser.ts
CHANGED
|
@@ -138,6 +138,8 @@ export interface ParsedPhotoLookDef {
|
|
|
138
138
|
duotone?: { shadow: string; highlight: string };
|
|
139
139
|
/** Keys the person cannot change. All others are starting values. */
|
|
140
140
|
fixed: PhotoLookKey[];
|
|
141
|
+
/** Darkest a gradient under light text on a photo may get, 0..100 (%). Absent → no scrim. */
|
|
142
|
+
scrim?: number;
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
export interface ParsedCameraDef {
|
|
@@ -1973,7 +1975,7 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
1973
1975
|
if (photoLookDeclared) {
|
|
1974
1976
|
const keys: PhotoLookKey[] = ['saturation', 'contrast', 'warmth', 'fade', 'mono', 'duotone'];
|
|
1975
1977
|
for (const [key, { line }] of Object.entries(photoLookRaw)) {
|
|
1976
|
-
if (key !== 'fixed' && !keys.includes(key as PhotoLookKey)) throw new Error(`${fileName}:${line}: unknown photo-look property "${key}"`);
|
|
1978
|
+
if (key !== 'fixed' && key !== 'scrim' && !keys.includes(key as PhotoLookKey)) throw new Error(`${fileName}:${line}: unknown photo-look property "${key}"`);
|
|
1977
1979
|
}
|
|
1978
1980
|
const int = (key: string, min: number, max: number): number => {
|
|
1979
1981
|
const p = photoLookRaw[key];
|
|
@@ -2021,6 +2023,14 @@ export function parseBrandFile(content: string, fileName: string): ParsedBrand {
|
|
|
2021
2023
|
look.fixed.push(key as PhotoLookKey);
|
|
2022
2024
|
}
|
|
2023
2025
|
}
|
|
2026
|
+
const scrim = photoLookRaw['scrim'];
|
|
2027
|
+
if (scrim) {
|
|
2028
|
+
const m = /^(\d+)%$/.exec(scrim.value);
|
|
2029
|
+
if (!m || Number(m[1]) < 1 || Number(m[1]) > 100) {
|
|
2030
|
+
throw new Error(`${fileName}:${scrim.line}: photo-look scrim must be a percentage from 1% to 100%, got "${scrim.value}"`);
|
|
2031
|
+
}
|
|
2032
|
+
look.scrim = Number(m[1]);
|
|
2033
|
+
}
|
|
2024
2034
|
result.photoLook = look;
|
|
2025
2035
|
}
|
|
2026
2036
|
|
package/parser/icons.ts
CHANGED
|
@@ -138,3 +138,10 @@ export function parseListItem(item: string): { icon?: IconRef; text: string } {
|
|
|
138
138
|
if (!m) return { text: item };
|
|
139
139
|
return { icon: parseIconRef(m[1], `list item "${item}"`), text: m[2] };
|
|
140
140
|
}
|
|
141
|
+
|
|
142
|
+
/** A `labels: above` list item: `Label: value`, split at the first colon followed by a space. */
|
|
143
|
+
export function splitLabelledItem(text: string): { label?: string; value: string } {
|
|
144
|
+
const at = text.indexOf(': ');
|
|
145
|
+
if (at <= 0) return { value: text };
|
|
146
|
+
return { label: text.slice(0, at).trim(), value: text.slice(at + 2).trim() };
|
|
147
|
+
}
|
package/parser/index.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type { ParsedBrand, ParsedFontDef, ParsedTypographyDef, ParsedDividerDef,
|
|
|
34
34
|
export { parseDateWindowRanges, isInDateWindow, SAMPLE_WINDOW_MARKER } from './dateWindows';
|
|
35
35
|
export type { DateWindowRange } from './dateWindows';
|
|
36
36
|
|
|
37
|
-
export { parseIconRef, parseIconSource, resolveIconRef, parseListItem, phosphorName, PHOSPHOR_WEIGHTS } from './icons';
|
|
37
|
+
export { parseIconRef, parseIconSource, resolveIconRef, parseListItem, splitLabelledItem, phosphorName, PHOSPHOR_WEIGHTS } from './icons';
|
|
38
38
|
export type { IconRef, IconSource, ParsedIconDef, ResolvedIcon, PhosphorWeight } from './icons';
|
|
39
39
|
export type {
|
|
40
40
|
ColorMode,
|
package/parser/purposeParser.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { SlotId } from './types';
|
|
9
|
-
import { parseIconRef, parseListItem } from './icons';
|
|
9
|
+
import { parseIconRef, parseListItem, splitLabelledItem } from './icons';
|
|
10
10
|
import type { IconRef } from './icons';
|
|
11
11
|
import { SAMPLE_WINDOW_MARKER } from './dateWindows';
|
|
12
12
|
|
|
@@ -105,6 +105,14 @@ export interface ParsedPurposeSlot {
|
|
|
105
105
|
itemGap?: number;
|
|
106
106
|
/** List layout direction: 'vertical' (default) or 'horizontal'. */
|
|
107
107
|
listDirection?: 'vertical' | 'horizontal';
|
|
108
|
+
/** Grid columns for a vertical list, filled row by row (list slots only). Default: 1 */
|
|
109
|
+
listColumns?: number;
|
|
110
|
+
/** 'above': each item is `Label: value`, the label set small above the value (list slots only). */
|
|
111
|
+
listLabels?: 'none' | 'above';
|
|
112
|
+
/** Label size relative to the item text, with `labels: above`. Default: 0.7 */
|
|
113
|
+
labelSize?: number;
|
|
114
|
+
/** Line between grid columns (list slots with `columns:` 2 or more). */
|
|
115
|
+
listDivider?: 'none' | 'hairline';
|
|
108
116
|
/** When false, this slot is locked — users cannot edit it in any mode. Default: true (editable). */
|
|
109
117
|
editable?: boolean;
|
|
110
118
|
typography: {
|
|
@@ -210,6 +218,9 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
|
|
|
210
218
|
if (s.marker && s.marker !== 'icon') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": iconOnly needs "marker: icon", got "${s.marker}"`);
|
|
211
219
|
s.marker = 'icon';
|
|
212
220
|
}
|
|
221
|
+
if (s.listColumns && s.listColumns > 1 && s.listDirection === 'horizontal') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": columns needs a vertical list`);
|
|
222
|
+
if (s.labelSize !== undefined && s.listLabels !== 'above') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": labelSize needs "labels: above"`);
|
|
223
|
+
if (s.listDivider === 'hairline' && !(s.listColumns && s.listColumns > 1)) throw new Error(`${fileName}:${lineNum}: slot "${s.id}": divider needs "columns:" 2 or more`);
|
|
213
224
|
if (s.defaultIcon && s.marker !== 'icon') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": defaultIcon needs "marker: icon"`);
|
|
214
225
|
if (!s.value && s.samples) {
|
|
215
226
|
// Trailing `@name` window marker, on the sample's first line only
|
|
@@ -644,6 +655,30 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
|
|
|
644
655
|
currentSlot.listDirection = d;
|
|
645
656
|
continue;
|
|
646
657
|
}
|
|
658
|
+
if (prop.startsWith('columns:')) {
|
|
659
|
+
const n = Number(prop.slice(8).trim());
|
|
660
|
+
if (!Number.isInteger(n) || n < 1 || n > 6) throw new Error(`${fileName}:${lineNum}: invalid columns "${prop.slice(8).trim()}" — must be 1 to 6`);
|
|
661
|
+
currentSlot.listColumns = n;
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
if (prop.startsWith('labels:')) {
|
|
665
|
+
const l = prop.slice(7).trim();
|
|
666
|
+
if (l !== 'none' && l !== 'above') throw new Error(`${fileName}:${lineNum}: invalid labels "${l}" — must be "none" or "above"`);
|
|
667
|
+
currentSlot.listLabels = l;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (prop.startsWith('labelSize:')) {
|
|
671
|
+
const n = parseFloat(prop.slice(10).trim());
|
|
672
|
+
if (isNaN(n) || n <= 0 || n > 1) throw new Error(`${fileName}:${lineNum}: invalid labelSize "${prop.slice(10).trim()}" — must be above 0 and at most 1`);
|
|
673
|
+
currentSlot.labelSize = n;
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (prop.startsWith('divider:')) {
|
|
677
|
+
const d = prop.slice(8).trim();
|
|
678
|
+
if (d !== 'none' && d !== 'hairline') throw new Error(`${fileName}:${lineNum}: invalid list divider "${d}" — must be "none" or "hairline"`);
|
|
679
|
+
currentSlot.listDivider = d;
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
647
682
|
throw new Error(`${fileName}:${lineNum}: unknown list property "${prop}"`);
|
|
648
683
|
}
|
|
649
684
|
}
|
|
@@ -828,6 +863,7 @@ function resolveExamples(
|
|
|
828
863
|
for (const item of text.split('\n')) {
|
|
829
864
|
const { icon } = parseListItem(item);
|
|
830
865
|
if (icon && slot.marker !== 'icon') throw new Error(`${at(slotId)}: slot "${slotId}": item "${item}" names an icon, but the list has no "marker: icon"`);
|
|
866
|
+
if (slot.listLabels === 'above' && !splitLabelledItem(item).label) throw new Error(`${at(slotId)}: slot "${slotId}": item "${item}" needs a label ("Label: value") for "labels: above"`);
|
|
831
867
|
}
|
|
832
868
|
}
|
|
833
869
|
}
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
},
|
|
233
233
|
"list-property": {
|
|
234
234
|
"comment": "4-space indented properties under type: list blocks",
|
|
235
|
-
"match": "^\\s{4,}(marker|icons|iconOnly|iconGap|itemGap|maxItems|direction)(:)\\s+(.*)",
|
|
235
|
+
"match": "^\\s{4,}(marker|icons|iconOnly|iconGap|itemGap|maxItems|direction|columns|labels|labelSize|divider)(:)\\s+(.*)",
|
|
236
236
|
"captures": {
|
|
237
237
|
"1": { "name": "support.type.property-name.brand-intent-purpose" },
|
|
238
238
|
"2": { "name": "punctuation.separator.brand-intent-purpose" },
|