@texuli/spec 0.8.0 → 0.10.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/README.md +42 -11
- package/dist/build.d.ts +5 -0
- package/dist/build.js +1 -0
- package/dist/codegen/kotlin-tokens.d.ts +38 -0
- package/dist/codegen/kotlin-tokens.js +138 -0
- package/dist/codegen/kotlin.d.ts +3 -1
- package/dist/codegen/kotlin.js +5 -2
- package/dist/design-system/index.d.ts +11 -0
- package/dist/design-system/index.js +10 -0
- package/dist/design-system/schema.d.ts +3 -0
- package/dist/design-system/schema.js +164 -0
- package/dist/design-system/validate.d.ts +69 -0
- package/dist/design-system/validate.js +273 -0
- package/dist/design-system/vocabulary.d.ts +70 -0
- package/dist/design-system/vocabulary.js +351 -0
- package/dist/format.js +10 -1
- package/dist/generated/typescript/blueprint.d.ts +69 -12
- package/dist/generated/typescript/blueprint.js +1 -1
- package/dist/generated/typescript/catalog-schema.js +1 -1
- package/dist/generated/typescript/design-system.d.ts +1110 -0
- package/dist/generated/typescript/design-system.js +3 -0
- package/dist/generated/typescript/diagnostics.js +1 -1
- package/dist/generated/typescript/manifest.js +1 -1
- package/dist/generated/typescript/vocabulary.d.ts +15 -1
- package/dist/generated/typescript/vocabulary.js +2 -2
- package/dist/generated/typescript/workflow.js +1 -1
- package/dist/migrations/0.8.0-0.9.0.d.ts +8 -0
- package/dist/migrations/0.8.0-0.9.0.js +11 -0
- package/dist/migrations/0.9.0-0.10.0.d.ts +10 -0
- package/dist/migrations/0.9.0-0.10.0.js +13 -0
- package/dist/migrations/index.js +4 -0
- package/dist/validate.js +12 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The closed vocabulary of a design-system document (ADR-021), as a compact
|
|
3
|
+
* table: the validator walks it, and `scripts/design-system-schema.ts`
|
|
4
|
+
* projects it into `schemas/design-system.schema.json` (TypeScript and
|
|
5
|
+
* Kotlin types follow from that schema). Kept as data, not a bundled JSON
|
|
6
|
+
* Schema, so `@texuli/spec/design-system` stays a few KB.
|
|
7
|
+
*
|
|
8
|
+
* A node is a `$defs` name (a string), a closed object (a key ending in `!`
|
|
9
|
+
* is required), or a tagged tuple:
|
|
10
|
+
* `['e', 'a b']` enum · `['n' | 'i', min, max]` number or integer ·
|
|
11
|
+
* `['s', pattern, maxLength]` string · `['t', maxLength]` text without
|
|
12
|
+
* control or bidi characters · `['l', item, min, max, unique?]` list ·
|
|
13
|
+
* `['m', value, maxEntries, keyPattern]` map · `['b']` boolean ·
|
|
14
|
+
* `['k', value]` constant · `['u', 'i' | 'f', capBytes]` an image or font
|
|
15
|
+
* source · `['x']` an `extends` reference.
|
|
16
|
+
*/
|
|
17
|
+
export const DESIGN_SYSTEM_FORMAT = 'texuli/design-system';
|
|
18
|
+
export const DESIGN_SYSTEM_FORMAT_VERSION = 1;
|
|
19
|
+
/** Stable codes `validate` and `migrate` raise (ADR-021). */
|
|
20
|
+
export const DESIGN_SYSTEM_CODES = [
|
|
21
|
+
'unknown-key',
|
|
22
|
+
'invalid-type',
|
|
23
|
+
'invalid-value',
|
|
24
|
+
'required',
|
|
25
|
+
'format-version',
|
|
26
|
+
'extends-range',
|
|
27
|
+
'extends-cycle',
|
|
28
|
+
'extends-depth',
|
|
29
|
+
'url-refused',
|
|
30
|
+
'inline-bytes',
|
|
31
|
+
];
|
|
32
|
+
/** Guarantee codes the compiler raises (ADR-022); reserved here, never raised by the spec. */
|
|
33
|
+
export const DESIGN_SYSTEM_COMPILER_CODES = ['aa-text', 'aa-focus', 'target-size'];
|
|
34
|
+
/** Caps: visited values, depth, errors, the `extends` chain and inline bytes (decoded). */
|
|
35
|
+
export const DESIGN_SYSTEM_LIMITS = Object.freeze({
|
|
36
|
+
values: 20_000,
|
|
37
|
+
depth: 16,
|
|
38
|
+
errors: 64,
|
|
39
|
+
extendsDepth: 4,
|
|
40
|
+
logo: 32_768,
|
|
41
|
+
favicon: 8_192,
|
|
42
|
+
font: 131_072,
|
|
43
|
+
inlineTotal: 262_144,
|
|
44
|
+
});
|
|
45
|
+
const ID = '[a-z][a-z0-9]*(?:-[a-z0-9]+)*';
|
|
46
|
+
const SEMVER = '(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?';
|
|
47
|
+
const NUM = '(?:\\d{1,3}(?:\\.\\d{1,6})?|\\.\\d{1,6})';
|
|
48
|
+
const PATH = '(?:\\.?\\/)?(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\\/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*\\.';
|
|
49
|
+
/** Shared patterns (also written into the schema). */
|
|
50
|
+
export const DS_PATTERNS = {
|
|
51
|
+
id: new RegExp(`^${ID}$`),
|
|
52
|
+
semver: new RegExp(`^${SEMVER}$`),
|
|
53
|
+
ref: new RegExp(`^(?:texuli:${ID}|${ID}@${SEMVER})$`),
|
|
54
|
+
color: new RegExp(`^(?:#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|oklch\\(${NUM}%? ${NUM} ${NUM}(?: \\/ ${NUM}%?)?\\))$`),
|
|
55
|
+
image: new RegExp(`^${PATH}(?:png|jpe?g|gif|webp|avif|svg|ico)$`),
|
|
56
|
+
font: new RegExp(`^${PATH}(?:woff2?|ttf|otf)$`),
|
|
57
|
+
// Free text refuses control, bidi and invisible characters: the tokens
|
|
58
|
+
// importers' set (C0, C1, soft hyphen, Arabic letter mark, Mongolian vowel
|
|
59
|
+
// separator, zero-width space, non-joiner and joiner, LRM and RLM, word
|
|
60
|
+
// joiner and invisible operators, bidi embeddings, overrides and isolates,
|
|
61
|
+
// byte order mark), plus the line and paragraph separators and the tag
|
|
62
|
+
// characters. The schema compiles patterns with the `u` flag.
|
|
63
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: text refuses control, bidi and invisible characters
|
|
64
|
+
text: /^[^\u0000-\u001f\u007f-\u009f\u00ad\u061c\u180e\u200b-\u200f\u2028-\u202e\u2060-\u2069\ufeff\u{e0000}-\u{e007f}]*$/u,
|
|
65
|
+
key: /^[a-z][a-z0-9-]{0,47}$/,
|
|
66
|
+
};
|
|
67
|
+
const P = DS_PATTERNS;
|
|
68
|
+
const color = 'colorValue';
|
|
69
|
+
const keys = (names, node) => Object.fromEntries(names.split(' ').map((name) => [name, node]));
|
|
70
|
+
/** Map keys no map may use, whatever its pattern. */
|
|
71
|
+
export const DS_RESERVED_KEYS = new Set([
|
|
72
|
+
'__proto__',
|
|
73
|
+
'constructor',
|
|
74
|
+
'prototype',
|
|
75
|
+
]);
|
|
76
|
+
/** The semantic roles of `@texuli/tokens` (`SEMANTIC_ROLES`); a test keeps them equal. */
|
|
77
|
+
export const DS_ROLES = 'background foreground surface surfaceForeground elevated elevatedForeground muted mutedForeground subtle subtleForeground border borderStrong input ring primary primaryForeground primarySoft primarySoftForeground secondary secondaryForeground accent accentForeground destructive destructiveForeground destructiveSoft destructiveSoftForeground success successForeground successSoft successSoftForeground warning warningForeground warningSoft warningSoftForeground info infoForeground infoSoft infoSoftForeground sidebar sidebarForeground sidebarBorder sidebarPrimary overlay scrim shadow link linkVisited selection selectionForeground canvas';
|
|
78
|
+
/** Component token kinds: the letter in {@link DS_COMPONENT_TOKENS} → its value `$defs`. */
|
|
79
|
+
export const DS_TOKEN_KINDS = {
|
|
80
|
+
r: 'radiusStep',
|
|
81
|
+
d: 'dimension',
|
|
82
|
+
t: 'typeRoleName',
|
|
83
|
+
b: 'borderStep',
|
|
84
|
+
e: 'elevationLevel',
|
|
85
|
+
o: 'opacityStep',
|
|
86
|
+
c: 'roleName',
|
|
87
|
+
k: 'strokeWidth',
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Every component of the token registry (`COMPONENT_TOKEN_FAMILIES` of
|
|
91
|
+
* `@texuli/tokens`, z-index tokens left out) and the defaults vocabulary
|
|
92
|
+
* (`DESIGN_DEFAULTS_VOCABULARY` of `texuli`): `key:kind` pairs. A test
|
|
93
|
+
* keeps it equal to both.
|
|
94
|
+
*/
|
|
95
|
+
export const DS_COMPONENT_TOKENS = {
|
|
96
|
+
accordion: 'trigger-gap:d trigger-padding-block:d trigger-font-size:t trigger-font-weight:t disabled-opacity:o',
|
|
97
|
+
alert: 'radius:r border-width:b padding-block:d padding-inline:d gap:d font-size:t icon-size:d',
|
|
98
|
+
'aspect-ratio': 'radius:r',
|
|
99
|
+
avatar: 'size-xs:d size-sm:d size-md:d size-lg:d radius:r font-weight:t',
|
|
100
|
+
badge: 'radius:r padding-block:d padding-inline:d gap:d font-size:t font-weight:t line-height:t border-width:b',
|
|
101
|
+
board: 'column-gap:d column-radius:r column-padding:d card-radius:r card-elevation:e',
|
|
102
|
+
breadcrumb: '',
|
|
103
|
+
button: 'height-sm:d height-md:d height-lg:d height-touch:d padding-inline-sm:d padding-inline-md:d padding-inline-lg:d gap:d radius:r font-size:t font-size-sm:t font-size-lg:t font-weight:t line-height:t icon-size:d border-width:b elevation:e disabled-opacity:o',
|
|
104
|
+
'canvas-toolbar': 'gap:d padding:d button-radius:r button-font-size:t',
|
|
105
|
+
card: 'radius:r border-width:b elevation:e padding-inline:d padding-block:d background:c title-font-size:t title-font-weight:t title-line-height:t description-font-size:t',
|
|
106
|
+
carousel: 'radius:r fallback-padding:d',
|
|
107
|
+
'chart-frame': 'title-font-size:t',
|
|
108
|
+
'chat-bubble': 'radius:r tail-radius:r padding-block:d padding-inline:d',
|
|
109
|
+
'chat-composer': 'radius:r padding:d gap:d',
|
|
110
|
+
chip: '',
|
|
111
|
+
choice: 'gap:d switch-gap:d label-font-size:t label-font-weight:t description-font-size:t disabled-opacity:o',
|
|
112
|
+
code: 'font-size:t font-weight:t block-padding-block:d block-padding-inline:d block-radius:r block-line-height:t block-border-width:b',
|
|
113
|
+
'color-picker': 'gap:d swatch-size:d swatch-radius:r',
|
|
114
|
+
combobox: 'height-sm:d height-md:d height-lg:d radius:r border-width:b font-size-sm:t font-size-md:t font-size-lg:t',
|
|
115
|
+
command: 'radius:r border-width:b font-size-sm:t font-size-md:t font-size-lg:t input-gap:d input-padding-inline:d',
|
|
116
|
+
'copy-button': '',
|
|
117
|
+
'data-grid': 'gap:d radius:r pager-font-size:t',
|
|
118
|
+
date: 'font-size:t label-font-weight:t control-radius:r control-border-width:b panel-padding:d panel-radius:r panel-elevation:e description-font-size:t',
|
|
119
|
+
dialog: 'radius:r border-width:b elevation:e font-size:t line-height:t title-font-size:t title-font-weight:t title-line-height:t alert-padding:d scrim-opacity:o',
|
|
120
|
+
empty: 'gap:d padding-inline:d padding-block:d title-font-size:t title-font-weight:t description-font-size:t',
|
|
121
|
+
field: 'gap:d label-font-size:t label-font-weight:t label-line-height:t description-font-size:t description-line-height:t',
|
|
122
|
+
'file-upload': 'gap:d zone-padding-block:d zone-padding-inline:d zone-border-width:b zone-radius:r browse-height:d',
|
|
123
|
+
forms: 'gap:d',
|
|
124
|
+
icon: 'size-xs:d size-sm:d size-md:d size-lg:d stroke-width:k',
|
|
125
|
+
'icon-button': '',
|
|
126
|
+
input: 'height:d height-touch:d padding-block:d padding-inline:d radius:r border-width:b elevation:e font-size:t font-size-touch:t font-weight:t textarea-padding-block:d disabled-opacity:o',
|
|
127
|
+
kbd: 'min-size:d padding-block:d radius:r border-width:b font-weight:t',
|
|
128
|
+
label: '',
|
|
129
|
+
menu: 'trigger-height-sm:d trigger-height-md:d trigger-height-lg:d trigger-radius:r trigger-border-width:b trigger-font-size:t trigger-font-weight:t content-padding-block:d content-radius:r content-elevation:e content-font-size:t',
|
|
130
|
+
navigation: 'font-size:t list-gap:d trigger-radius:r chevron-opacity:o',
|
|
131
|
+
'number-field': 'radius:r border-width:b padding-block:d padding-inline:d stepper-size:d',
|
|
132
|
+
pagination: '',
|
|
133
|
+
'pin-input': 'gap:d cell-inline-size:d cell-block-size:d cell-radius:r cell-font-size:t',
|
|
134
|
+
popover: 'padding-block:d padding-inline:d radius:r border-width:b elevation:e font-size:t title-font-size:t title-font-weight:t title-line-height:t',
|
|
135
|
+
progress: 'gap:d font-size:t radius:r track-height-sm:d track-height-md:d track-height-lg:d',
|
|
136
|
+
'radio-group': '',
|
|
137
|
+
rating: 'stars-gap:d label-font-size:t label-font-weight:t star-radius:r',
|
|
138
|
+
resizable: 'radius:r border-width:b',
|
|
139
|
+
'resizable-handle': '',
|
|
140
|
+
'scroll-area': 'radius:r border-width:b padding:d scrollbar-size:d scrollbar-radius:r thumb-min-size:d',
|
|
141
|
+
select: 'height-sm:d height-md:d height-lg:d padding-inline-sm:d padding-inline-md:d padding-inline-lg:d gap:d radius:r border-width:b font-size-sm:t font-size-md:t font-size-lg:t font-weight:t chevron-size:d chevron-opacity:o',
|
|
142
|
+
separator: 'thickness:b',
|
|
143
|
+
sheet: 'gap:d padding:d border-width:b elevation:e font-size:t line-height:t title-font-size:t title-font-weight:t title-line-height:t scrim-opacity:o',
|
|
144
|
+
sidebar: 'font-size:t inset-radius:r header-padding-inline:d header-gap:d body-gap:d body-padding-block:d body-padding-inline:d',
|
|
145
|
+
skeleton: 'radius:r height-sm:d height-md:d height-lg:d circle-size:d',
|
|
146
|
+
slider: 'thumb-border-width:b thumb-elevation:e disabled-opacity:o',
|
|
147
|
+
spinner: 'radius:r',
|
|
148
|
+
spreadsheet: 'gap:d tab-radius:r tab-padding-inline:d bar-radius:r',
|
|
149
|
+
stat: 'gap:d padding:d radius:r border-width:b elevation:e label-font-size:t label-font-weight:t value-font-size:t value-font-weight:t icon-size:d icon-radius:r',
|
|
150
|
+
stepper: 'gap:d trigger-radius:r marker-size:d marker-radius:r marker-border-width:b marker-font-size:t marker-font-weight:t check-size:d',
|
|
151
|
+
table: 'radius:r border-width:b font-size:t head-font-size:t head-font-weight:t',
|
|
152
|
+
tabs: 'gap:d list-gap:d list-radius:r trigger-gap:d trigger-height-touch:d trigger-font-size:t trigger-font-weight:t disabled-opacity:o',
|
|
153
|
+
tag: '',
|
|
154
|
+
'tags-input': 'height:d height-touch:d gap:d padding-inline:d radius:r border-width:b elevation:e tag-radius:r tag-font-size:t disabled-opacity:o',
|
|
155
|
+
timeline: 'marker-size:d marker-radius:r time-font-size:t time-font-weight:t',
|
|
156
|
+
toast: 'gap:d padding-block:d radius:r border-width:b elevation:e icon-size:d title-font-size:t title-font-weight:t',
|
|
157
|
+
toggle: 'height-sm:d height-md:d height-lg:d padding-inline-sm:d padding-inline-md:d padding-inline-lg:d gap:d radius:r border-width:b font-size:t font-weight:t line-height:t disabled-opacity:o',
|
|
158
|
+
'toggle-group': '',
|
|
159
|
+
toolbar: 'gap:d padding:d radius:r border-width:b item-height-sm:d item-font-size:t item-font-weight:t disabled-opacity:o',
|
|
160
|
+
tooltip: 'radius:r font-size:t font-weight:t line-height:t elevation:e',
|
|
161
|
+
tree: 'gap:d padding:d radius:r row-gap:d row-padding-block:d checkbox-size:d checkbox-radius:r',
|
|
162
|
+
'workflow-node': 'padding-block:d padding-inline:d radius:r elevation:e',
|
|
163
|
+
};
|
|
164
|
+
/** Component defaults: `[variants, sizes]`, space-separated; a test keeps it equal to `texuli`. */
|
|
165
|
+
export const DS_COMPONENT_DEFAULTS = {
|
|
166
|
+
accordion: ['default bordered', ''],
|
|
167
|
+
avatar: ['', 'xs sm md lg'],
|
|
168
|
+
badge: ['default secondary outline success warning destructive info neutral', ''],
|
|
169
|
+
breadcrumb: ['', 'sm md lg'],
|
|
170
|
+
button: ['default secondary outline ghost destructive link', 'sm md lg icon'],
|
|
171
|
+
card: ['elevated flat', ''],
|
|
172
|
+
chip: ['default secondary outline success warning destructive info neutral', ''],
|
|
173
|
+
choice: ['', 'sm md'],
|
|
174
|
+
code: ['inline block', ''],
|
|
175
|
+
combobox: ['outline soft', 'sm md lg'],
|
|
176
|
+
command: ['outline soft', 'sm md lg'],
|
|
177
|
+
'copy-button': ['', 'sm md'],
|
|
178
|
+
date: ['', 'sm md'],
|
|
179
|
+
dialog: ['', 'sm md lg xl'],
|
|
180
|
+
empty: ['', 'sm md'],
|
|
181
|
+
'file-upload': ['default compact', ''],
|
|
182
|
+
icon: ['', 'xs sm md lg'],
|
|
183
|
+
'icon-button': ['default secondary outline ghost destructive link', 'sm md lg icon'],
|
|
184
|
+
label: ['', 'sm md'],
|
|
185
|
+
menu: ['default soft', 'sm md lg'],
|
|
186
|
+
navigation: ['', 'sm md lg'],
|
|
187
|
+
'number-field': ['', 'sm md lg'],
|
|
188
|
+
pagination: ['outline soft', 'sm md lg'],
|
|
189
|
+
'pin-input': ['', 'sm md lg'],
|
|
190
|
+
popover: ['elevated flat', 'sm md lg'],
|
|
191
|
+
progress: ['', 'sm md lg'],
|
|
192
|
+
'radio-group': ['default card', 'sm md'],
|
|
193
|
+
rating: ['', 'sm md lg'],
|
|
194
|
+
resizable: ['plain bordered', ''],
|
|
195
|
+
'resizable-handle': ['', 'sm md'],
|
|
196
|
+
'scroll-area': ['', 'sm md lg'],
|
|
197
|
+
select: ['outline soft', 'sm md lg'],
|
|
198
|
+
separator: ['default subtle strong', ''],
|
|
199
|
+
sheet: ['', 'sm md lg xl'],
|
|
200
|
+
sidebar: ['default inset', ''],
|
|
201
|
+
skeleton: ['', 'sm md lg'],
|
|
202
|
+
slider: ['', 'sm md lg'],
|
|
203
|
+
spinner: ['', 'sm md lg'],
|
|
204
|
+
stat: ['elevated flat', ''],
|
|
205
|
+
tabs: ['line pills enclosed', ''],
|
|
206
|
+
tag: ['default secondary outline success warning destructive info neutral', ''],
|
|
207
|
+
toggle: ['default outline', 'sm md lg'],
|
|
208
|
+
'toggle-group': ['default outline', 'sm md lg'],
|
|
209
|
+
toolbar: ['default outline ghost', 'sm md'],
|
|
210
|
+
tree: ['default bordered', ''],
|
|
211
|
+
};
|
|
212
|
+
const components = {};
|
|
213
|
+
for (const [id, pairs] of Object.entries(DS_COMPONENT_TOKENS)) {
|
|
214
|
+
const shape = {};
|
|
215
|
+
for (const pair of pairs ? pairs.split(' ') : []) {
|
|
216
|
+
const [key, kind] = pair.split(':');
|
|
217
|
+
shape[key] = DS_TOKEN_KINDS[kind];
|
|
218
|
+
}
|
|
219
|
+
const defaults = DS_COMPONENT_DEFAULTS[id];
|
|
220
|
+
if (defaults) {
|
|
221
|
+
const [variant, size] = defaults;
|
|
222
|
+
shape.defaults = {
|
|
223
|
+
...(variant ? { variant: ['e', variant] } : {}),
|
|
224
|
+
...(size ? { size: ['e', size] } : {}),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
components[id] = shape;
|
|
228
|
+
}
|
|
229
|
+
const sections = keys('color typography shape space elevation surface motion layout icons components assets voice accessibility', '');
|
|
230
|
+
for (const name of Object.keys(sections))
|
|
231
|
+
sections[name] = name;
|
|
232
|
+
/** Every named definition. */
|
|
233
|
+
export const DS_DEFS = {
|
|
234
|
+
colorValue: ['s', P.color, 64],
|
|
235
|
+
roleName: ['e', DS_ROLES],
|
|
236
|
+
roleMap: keys(DS_ROLES, color),
|
|
237
|
+
dataVizPalette: {
|
|
238
|
+
categorical: ['l', color, 8, 8],
|
|
239
|
+
sequential: ['l', color, 9, 9],
|
|
240
|
+
diverging: ['l', color, 9, 9],
|
|
241
|
+
grid: color,
|
|
242
|
+
axis: color,
|
|
243
|
+
},
|
|
244
|
+
fontFile: {
|
|
245
|
+
'src!': ['u', 'f', DESIGN_SYSTEM_LIMITS.font],
|
|
246
|
+
weight: ['s', /^[1-9]00(?: [1-9]00)?$/, 7],
|
|
247
|
+
style: ['e', 'normal italic'],
|
|
248
|
+
display: ['e', 'swap fallback optional block auto'],
|
|
249
|
+
preload: ['b'],
|
|
250
|
+
},
|
|
251
|
+
fontFamily: {
|
|
252
|
+
'stack!': ['l', ['s', /^-?[A-Za-z0-9][A-Za-z0-9 _.-]{0,31}$/, 33], 1, 8],
|
|
253
|
+
files: ['l', 'fontFile', 0, 8],
|
|
254
|
+
},
|
|
255
|
+
typeRole: {
|
|
256
|
+
family: ['e', 'sans heading mono serif'],
|
|
257
|
+
size: ['n', 8, 128],
|
|
258
|
+
weight: ['i', 100, 900],
|
|
259
|
+
line: ['n', 1, 2.5],
|
|
260
|
+
tracking: ['n', -0.1, 0.2],
|
|
261
|
+
},
|
|
262
|
+
typeRoleName: ['e', 'display title heading body label caption code'],
|
|
263
|
+
radiusStep: ['e', 'none xs sm md lg xl full'],
|
|
264
|
+
spaceStep: [
|
|
265
|
+
'e',
|
|
266
|
+
'space-0 space-1 space-2 space-3 space-4 space-5 space-6 space-7 space-8 space-9 space-10 space-11 space-12',
|
|
267
|
+
],
|
|
268
|
+
dimension: [
|
|
269
|
+
'e',
|
|
270
|
+
'space-0 space-1 space-2 space-3 space-4 space-5 space-6 space-7 space-8 space-9 space-10 space-11 space-12 control-xs control-sm control-md control-lg icon-xs icon-sm icon-md icon-lg target-min target-touch',
|
|
271
|
+
],
|
|
272
|
+
borderStep: ['e', 'none thin thick'],
|
|
273
|
+
elevationLevel: ['i', 0, 5],
|
|
274
|
+
opacityStep: ['e', 'disabled muted scrim'],
|
|
275
|
+
strokeWidth: ['n', 0.5, 4],
|
|
276
|
+
asset: ['u', 'i', DESIGN_SYSTEM_LIMITS.logo],
|
|
277
|
+
color: {
|
|
278
|
+
brand: {
|
|
279
|
+
'seed!': color,
|
|
280
|
+
secondary: color,
|
|
281
|
+
accent: color,
|
|
282
|
+
neutral: ['e', 'zinc slate stone gray warm cool tinted'],
|
|
283
|
+
},
|
|
284
|
+
pin: keys('primary secondary accent destructive success warning info', color),
|
|
285
|
+
roles: { light: 'roleMap', dark: 'roleMap' },
|
|
286
|
+
modes: ['l', ['e', 'light dark'], 1, 2, 1],
|
|
287
|
+
dataViz: { light: 'dataVizPalette', dark: 'dataVizPalette', cvdSafe: ['b'] },
|
|
288
|
+
status: keys('success warning danger info neutral', color),
|
|
289
|
+
},
|
|
290
|
+
typography: {
|
|
291
|
+
families: keys('sans heading mono serif', 'fontFamily'),
|
|
292
|
+
scale: { base: ['n', 12, 24], ratio: ['n', 1.05, 1.618] },
|
|
293
|
+
roles: keys('display title heading body label caption code', 'typeRole'),
|
|
294
|
+
},
|
|
295
|
+
shape: { radius: ['n', 0, 48], radiusScale: ['n', 0, 4], borderWidth: ['n', 0, 4] },
|
|
296
|
+
space: { base: ['n', 2, 8], density: ['n', 0.75, 1.25] },
|
|
297
|
+
elevation: { style: ['e', 'flat soft crisp glow layered'] },
|
|
298
|
+
surface: { style: ['e', 'solid glass'], glass: { alpha: ['n', 0, 1], blur: ['n', 0, 64] } },
|
|
299
|
+
motion: {
|
|
300
|
+
personality: ['e', 'none subtle standard expressive'],
|
|
301
|
+
durations: keys('fast base slow', ['i', 0, 2000]),
|
|
302
|
+
easing: ['e', 'standard emphasized enter exit linear'],
|
|
303
|
+
},
|
|
304
|
+
layout: {
|
|
305
|
+
contentMax: ['i', 320, 2560],
|
|
306
|
+
wideMax: ['i', 320, 2560],
|
|
307
|
+
breakpoints: keys('sm md lg', ['i', 320, 2560]),
|
|
308
|
+
grid: {
|
|
309
|
+
columns: keys('base sm md lg', ['i', 1, 24]),
|
|
310
|
+
gutter: keys('base sm md lg', 'spaceStep'),
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
icons: {
|
|
314
|
+
style: ['e', 'outline filled'],
|
|
315
|
+
stroke: ['n', 0.5, 3],
|
|
316
|
+
size: keys('sm md lg', ['n', 8, 64]),
|
|
317
|
+
},
|
|
318
|
+
components,
|
|
319
|
+
assets: {
|
|
320
|
+
logo: keys('light dark mark wordmark', 'asset'),
|
|
321
|
+
favicon: ['u', 'i', DESIGN_SYSTEM_LIMITS.favicon],
|
|
322
|
+
},
|
|
323
|
+
voice: {
|
|
324
|
+
tone: ['l', ['s', /^[a-z][a-z-]{0,31}$/, 32], 0, 8, 1],
|
|
325
|
+
lexicon: ['m', ['t', 64], 200, P.key],
|
|
326
|
+
guidelines: ['l', ['t', 200], 0, 12],
|
|
327
|
+
},
|
|
328
|
+
accessibility: {
|
|
329
|
+
contrast: ['e', 'AA AAA'],
|
|
330
|
+
targetMin: ['i', 24, 64],
|
|
331
|
+
focus: ['e', 'ring outline glow inset'],
|
|
332
|
+
focusWidth: ['n', 1, 6],
|
|
333
|
+
focusOffset: ['n', 0, 8],
|
|
334
|
+
},
|
|
335
|
+
variant: sections,
|
|
336
|
+
};
|
|
337
|
+
/** The document root. */
|
|
338
|
+
export const DS_ROOT = {
|
|
339
|
+
'format!': ['k', DESIGN_SYSTEM_FORMAT],
|
|
340
|
+
'formatVersion!': ['k', DESIGN_SYSTEM_FORMAT_VERSION],
|
|
341
|
+
'id!': ['s', P.id, 64],
|
|
342
|
+
'version!': ['s', P.semver, 64],
|
|
343
|
+
'name!': ['t', 80],
|
|
344
|
+
description: ['t', 500],
|
|
345
|
+
owner: ['t', 80],
|
|
346
|
+
license: ['t', 80],
|
|
347
|
+
tags: ['l', ['s', /^[a-z0-9][a-z0-9-]{0,31}$/, 32], 0, 16, 1],
|
|
348
|
+
extends: ['x'],
|
|
349
|
+
...sections,
|
|
350
|
+
variants: ['m', 'variant', 16, P.key],
|
|
351
|
+
};
|
package/dist/format.js
CHANGED
|
@@ -9,7 +9,16 @@ export const LINE_WIDTH = 100;
|
|
|
9
9
|
*/
|
|
10
10
|
export function formatJson(value) {
|
|
11
11
|
// Round-trip through canonical JSON: sorted keys, undefined dropped.
|
|
12
|
-
return `${print(JSON.parse(canonicalJson(value)), 0, '', 0)}\n
|
|
12
|
+
return escapeInvisible(`${print(JSON.parse(canonicalJson(value)), 0, '', 0)}\n`);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Invisible and bidi control characters as `\u` escapes (the same JSON
|
|
16
|
+
* value): the repository's Trojan Source check refuses them literally in
|
|
17
|
+
* committed files.
|
|
18
|
+
*/
|
|
19
|
+
const INVISIBLE = /[\u200b-\u200f\u202a-\u202e\u2066-\u2069\ufeff]/g;
|
|
20
|
+
function escapeInvisible(text) {
|
|
21
|
+
return text.replace(INVISIBLE, (ch) => `\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
|
13
22
|
}
|
|
14
23
|
function isScalar(value) {
|
|
15
24
|
return value === null || typeof value !== 'object';
|
|
@@ -191,18 +191,21 @@ export interface BlueprintFormMessages {
|
|
|
191
191
|
/**
|
|
192
192
|
* One field of a closed type. Which members apply to which type, value types, option membership,
|
|
193
193
|
* ranges and the conditions' field references are checked by @texuli/forms/model
|
|
194
|
-
* (validateFormSchema). Formats are names; a pattern is never accepted.
|
|
194
|
+
* (validateFormSchema). Formats are names; a pattern is never accepted. password and otp are
|
|
195
|
+
* credential fields that agent output may use only where the host allows them; custom is painted by
|
|
196
|
+
* the host for a kind it registered (refused otherwise).
|
|
195
197
|
*/
|
|
196
198
|
export interface BlueprintFormField {
|
|
197
199
|
/** The key in the submitted values, unique in the form. */
|
|
198
200
|
name: string;
|
|
199
|
-
type: 'text' | 'textarea' | 'number' | 'email' | 'url' | 'date' | 'time' | 'select' | 'multiselect' | 'checkbox' | 'radio' | 'switch' | 'slider' | 'file' | 'tags';
|
|
201
|
+
type: 'text' | 'textarea' | 'number' | 'email' | 'url' | 'date' | 'time' | 'select' | 'multiselect' | 'checkbox' | 'radio' | 'switch' | 'slider' | 'file' | 'tags' | 'password' | 'otp' | 'color' | 'datetime' | 'custom';
|
|
200
202
|
label: string;
|
|
201
203
|
description?: string;
|
|
202
204
|
placeholder?: string;
|
|
203
205
|
/**
|
|
204
|
-
* A value of the field's type (a string, number, boolean, YYYY-MM-DD, HH:MM
|
|
205
|
-
* values). Not for file
|
|
206
|
+
* A value of the field's type (a string, number, boolean, YYYY-MM-DD, HH:MM, YYYY-MM-DDTHH:MM, a
|
|
207
|
+
* #hex color, a list of option values, or any JSON value for custom). Not for file, password or
|
|
208
|
+
* otp fields.
|
|
206
209
|
*/
|
|
207
210
|
default?: unknown;
|
|
208
211
|
options?: readonly BlueprintFormOption[];
|
|
@@ -210,9 +213,15 @@ export interface BlueprintFormField {
|
|
|
210
213
|
when?: BlueprintFormWhen;
|
|
211
214
|
minLength?: number;
|
|
212
215
|
maxLength?: number;
|
|
213
|
-
/**
|
|
216
|
+
/**
|
|
217
|
+
* A number for number and slider; YYYY-MM-DD for date; HH:MM for time; YYYY-MM-DDTHH:MM for
|
|
218
|
+
* datetime.
|
|
219
|
+
*/
|
|
214
220
|
min?: unknown;
|
|
215
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* A number for number and slider; YYYY-MM-DD for date; HH:MM for time; YYYY-MM-DDTHH:MM for
|
|
223
|
+
* datetime.
|
|
224
|
+
*/
|
|
216
225
|
max?: unknown;
|
|
217
226
|
/** Above 0, for number and slider. */
|
|
218
227
|
step?: number;
|
|
@@ -230,9 +239,32 @@ export interface BlueprintFormField {
|
|
|
230
239
|
accept?: readonly string[];
|
|
231
240
|
/** Largest accepted file in bytes; the host uploads files. */
|
|
232
241
|
maxSize?: number;
|
|
233
|
-
/**
|
|
234
|
-
|
|
242
|
+
/**
|
|
243
|
+
* An autocomplete token; payment and one-time-code tokens are absent, and current-password and
|
|
244
|
+
* new-password apply to password fields only.
|
|
245
|
+
*/
|
|
246
|
+
autocomplete?: 'off' | 'name' | 'given-name' | 'family-name' | 'nickname' | 'email' | 'tel' | 'organization' | 'organization-title' | 'street-address' | 'address-level1' | 'address-level2' | 'postal-code' | 'country-name' | 'language' | 'url' | 'bday' | 'current-password' | 'new-password';
|
|
235
247
|
link?: BlueprintFormLink;
|
|
248
|
+
/**
|
|
249
|
+
* Custom fields: the kind the host paints; agent output may name only a kind the host registered.
|
|
250
|
+
*/
|
|
251
|
+
kind?: string;
|
|
252
|
+
/**
|
|
253
|
+
* Custom fields: at most 20 scalar settings under field-like names, handed to the host painter.
|
|
254
|
+
*/
|
|
255
|
+
config?: Readonly<Record<string, unknown>>;
|
|
256
|
+
/** Otp fields: the number of digits (default 6). */
|
|
257
|
+
length?: number;
|
|
258
|
+
/**
|
|
259
|
+
* Url fields: the allowed schemes (default https only); an http answer passes the untrusted URL
|
|
260
|
+
* policy as its https twin.
|
|
261
|
+
*/
|
|
262
|
+
protocols?: readonly ('http' | 'https')[];
|
|
263
|
+
/**
|
|
264
|
+
* Select fields: whether an empty choice clears the answer (default: while the field is not
|
|
265
|
+
* required).
|
|
266
|
+
*/
|
|
267
|
+
clearable?: boolean;
|
|
236
268
|
}
|
|
237
269
|
/** A group of fields (a fieldset when titled). */
|
|
238
270
|
export interface BlueprintFormSection {
|
|
@@ -407,7 +439,9 @@ export interface BlueprintEnvelopeMotion {
|
|
|
407
439
|
}
|
|
408
440
|
/**
|
|
409
441
|
* A Texuli document (spec 0.7.0, ADR-011): the format, version, metadata, page settings, block list
|
|
410
|
-
* and annotations of @texuli/document
|
|
442
|
+
* and annotations of @texuli/document, and from spec 0.10.0 the design system it follows
|
|
443
|
+
* (designSystem, the blueprint's $defs.designSystemReference; exactly one of ref and inline is
|
|
444
|
+
* @texuli/document/schema's check). This contract checks the envelope of the document only; the
|
|
411
445
|
* closed block vocabulary, text safety, the URL policy and the caps are checked by
|
|
412
446
|
* @texuli/document/schema (schemas/document.schema.json) inside the runtime validator,
|
|
413
447
|
* @texuli/catalog's validateDraft, at the host's tier.
|
|
@@ -427,6 +461,7 @@ export interface BlueprintDocument {
|
|
|
427
461
|
blocks: readonly Readonly<Record<string, unknown>>[];
|
|
428
462
|
/** Comment threads, suggestions and named versions, kept apart from content. */
|
|
429
463
|
annotations?: Readonly<Record<string, unknown>>;
|
|
464
|
+
designSystem?: BlueprintDesignSystemReference;
|
|
430
465
|
}
|
|
431
466
|
/**
|
|
432
467
|
* One RFC 6902 operation on a document. Paths are RFC 6901 pointers into the document;
|
|
@@ -550,6 +585,18 @@ export interface BlueprintSourceBinding {
|
|
|
550
585
|
query?: BlueprintSourceQuery;
|
|
551
586
|
map?: Readonly<Record<string, BlueprintSourceFieldName>>;
|
|
552
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* A design system (spec 0.10.0, ADR-021 and ADR-024): exactly one of ref (an exact id@x.y.z the
|
|
590
|
+
* host resolves, never a range, a URL or a path; or texuli:<theme>, a library theme) and inline (a
|
|
591
|
+
* whole texuli/design-system document, checked by checkDesignSystemReference from
|
|
592
|
+
* @texuli/spec/design-system against schemas/design-system.schema.json, at the untrusted level for
|
|
593
|
+
* agent output). The validator refuses an empty reference or both keys.
|
|
594
|
+
*/
|
|
595
|
+
export interface BlueprintDesignSystemReference {
|
|
596
|
+
ref?: string;
|
|
597
|
+
/** A whole design-system document. */
|
|
598
|
+
inline?: Readonly<Record<string, unknown>>;
|
|
599
|
+
}
|
|
553
600
|
/**
|
|
554
601
|
* Envelope version 1 around one catalog tree (ADR-004, audit G13). Mirrors what
|
|
555
602
|
* @texuli/catalog/envelope reads; a declared status, activation or tier is display data, never
|
|
@@ -562,8 +609,12 @@ export interface BlueprintSourceBinding {
|
|
|
562
609
|
* ({"$source": name, "query": {...}}), with a closed query, and forms as data: the closed form
|
|
563
610
|
* schema a `schema-form` node carries ($defs.formSchema) with conditions and cross-field rules in
|
|
564
611
|
* the closed expression AST of @texuli/core/expression ($defs.expression). Spec 0.8.0 (ADR-012)
|
|
565
|
-
* adds the canvas shape a `canvas` node carries ($defs.canvas).
|
|
566
|
-
*
|
|
612
|
+
* adds the canvas shape a `canvas` node carries ($defs.canvas). Spec 0.9.0 refreshes the catalog
|
|
613
|
+
* projection only (an additive minor; the envelope shape is unchanged). Spec 0.10.0 (ADR-021,
|
|
614
|
+
* ADR-024) adds the design-system document (schemas/design-system.schema.json) and the optional
|
|
615
|
+
* envelope designSystem ($defs.designSystemReference): the design system the whole blueprint
|
|
616
|
+
* follows, including a deck or document at its root (an additive minor). Planned minor additions:
|
|
617
|
+
* the native profile and several roots.
|
|
567
618
|
*/
|
|
568
619
|
export interface BlueprintEnvelope {
|
|
569
620
|
format: 'texuli-blueprint';
|
|
@@ -587,7 +638,7 @@ export interface BlueprintEnvelope {
|
|
|
587
638
|
/**
|
|
588
639
|
* The spec version the envelope was written for. Omitted means 0.2.0; migrateBlueprint stamps it.
|
|
589
640
|
*/
|
|
590
|
-
spec?: '0.3.0' | '0.4.0' | '0.5.0' | '0.6.0' | '0.7.0' | '0.8.0';
|
|
641
|
+
spec?: '0.3.0' | '0.4.0' | '0.5.0' | '0.6.0' | '0.7.0' | '0.8.0' | '0.9.0' | '0.10.0';
|
|
591
642
|
/**
|
|
592
643
|
* Local state declarations (ADR-008), at most 64. Names, counts and per-type rules are checked by
|
|
593
644
|
* the validator.
|
|
@@ -595,4 +646,10 @@ export interface BlueprintEnvelope {
|
|
|
595
646
|
state?: Readonly<Record<string, BlueprintStateDeclaration>>;
|
|
596
647
|
flow?: BlueprintFlow;
|
|
597
648
|
motion?: BlueprintEnvelopeMotion;
|
|
649
|
+
/**
|
|
650
|
+
* The design system the blueprint follows (spec 0.10.0, ADR-024): a reference the host resolves
|
|
651
|
+
* or a whole inline document. Players apply it to their root scope; a reference nothing resolves,
|
|
652
|
+
* or a document the compiler refuses, paints with the default theme and a visible notice.
|
|
653
|
+
*/
|
|
654
|
+
designSystem?: BlueprintDesignSystemReference;
|
|
598
655
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Generated by @texuli/spec from schemas/blueprint.schema.json. Do not edit: run the spec generator.
|
|
2
|
-
// Spec 0.
|
|
2
|
+
// Spec 0.10.0, digest afcb9e3ed6b74b833a0f7477a657168621785ee98602ff582aa047e2d86aef17
|
|
3
3
|
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Generated by @texuli/spec from schemas/catalog.schema.json. Do not edit: run the spec generator.
|
|
2
|
-
// Spec 0.
|
|
2
|
+
// Spec 0.10.0, digest afcb9e3ed6b74b833a0f7477a657168621785ee98602ff582aa047e2d86aef17
|
|
3
3
|
export {};
|