@weasel-js/prefs 1.10.1 → 1.11.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 +51 -0
- package/dist/chunk-OZFER7VN.js +706 -0
- package/dist/chunk-OZFER7VN.js.map +1 -0
- package/dist/{chunk-ASSX3JHY.js → chunk-WN6QQICK.js} +27 -589
- package/dist/chunk-WN6QQICK.js.map +1 -0
- package/dist/chunk-ZLXZRX73.js +32 -0
- package/dist/chunk-ZLXZRX73.js.map +1 -0
- package/dist/css-v-LpmTll.d.ts +49 -0
- package/dist/{open-C83pU7Va.d.ts → groups-BDZC-ywb.d.ts} +115 -229
- package/dist/index.d.ts +51 -53
- package/dist/index.js +60 -103
- package/dist/index.js.map +1 -1
- package/dist/open-VJ1zlFJz.d.ts +203 -0
- package/dist/react.d.ts +27 -4
- package/dist/react.js +75 -3
- package/dist/react.js.map +1 -1
- package/dist/vite.d.ts +23 -0
- package/dist/vite.js +61 -0
- package/dist/vite.js.map +1 -0
- package/package.json +16 -6
- package/dist/chunk-ASSX3JHY.js.map +0 -1
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
import { formatUnit, unitScale, endlessAllows } from '@weasel-js/quantity';
|
|
2
|
+
import { matchQuery, isPlainObject } from '@weasel-js/registry';
|
|
3
|
+
import { hexToRgba, interpolateSrgb, rgbaToHex } from '@weasel-js/paint';
|
|
4
|
+
|
|
5
|
+
// src/alias.ts
|
|
6
|
+
function prefAliasTarget(alias, leafAt) {
|
|
7
|
+
const passed = /* @__PURE__ */ new Set();
|
|
8
|
+
let path = alias.of;
|
|
9
|
+
for (; ; ) {
|
|
10
|
+
if (passed.has(path)) return void 0;
|
|
11
|
+
passed.add(path);
|
|
12
|
+
const leaf = leafAt(path);
|
|
13
|
+
if (leaf === void 0) return void 0;
|
|
14
|
+
if (leaf.kind !== "alias") return { path, leaf };
|
|
15
|
+
path = leaf.of;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function prefAliasName(alias, target) {
|
|
19
|
+
if (alias.name !== "") return alias.name;
|
|
20
|
+
return target === void 0 ? "Alias" : target.name;
|
|
21
|
+
}
|
|
22
|
+
function prefAliasedLeaf(alias, target) {
|
|
23
|
+
return {
|
|
24
|
+
...target,
|
|
25
|
+
name: prefAliasName(alias, target),
|
|
26
|
+
description: alias.description === "" ? target.description : alias.description,
|
|
27
|
+
...alias.access ? { access: alias.access } : {},
|
|
28
|
+
...alias.tags ? { tags: [...target.tags ?? [], ...alias.tags] } : {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function prefUnitAccepts(unit) {
|
|
32
|
+
return unit.suffix === void 0 ? { ...unit.accepts } : { [unit.suffix]: 1, ...unit.accepts };
|
|
33
|
+
}
|
|
34
|
+
function isPrefLinearUnit(unit) {
|
|
35
|
+
return typeof unit.toDisplay !== "function";
|
|
36
|
+
}
|
|
37
|
+
var SHOWN = "shown";
|
|
38
|
+
function decimalPlaces(n) {
|
|
39
|
+
const text = String(n);
|
|
40
|
+
if (text.includes("e")) return void 0;
|
|
41
|
+
const dot = text.indexOf(".");
|
|
42
|
+
return dot === -1 ? 0 : text.length - dot - 1;
|
|
43
|
+
}
|
|
44
|
+
var DECIMAL_PLACES = 12;
|
|
45
|
+
function storedOf(shown, scale, offset) {
|
|
46
|
+
const raw = shown * scale + offset;
|
|
47
|
+
const [a, b, c] = [decimalPlaces(shown), decimalPlaces(scale), decimalPlaces(offset)];
|
|
48
|
+
if (a === void 0 || b === void 0 || c === void 0 || !Number.isFinite(raw)) return raw;
|
|
49
|
+
const places = Math.max(a + b, c);
|
|
50
|
+
return places > DECIMAL_PLACES ? raw : Number(raw.toFixed(places));
|
|
51
|
+
}
|
|
52
|
+
function resolvePrefUnit(unit) {
|
|
53
|
+
if (!isPrefLinearUnit(unit)) {
|
|
54
|
+
return { ...unit, format: unit.format ?? ((stored) => `${unit.toDisplay(stored)}${unit.suffix ?? ""}`) };
|
|
55
|
+
}
|
|
56
|
+
const { scale, offset = 0, precision, suffix, accepts } = unit;
|
|
57
|
+
const round = precision === void 0 ? void 0 : 10 ** precision;
|
|
58
|
+
const system = { base: "stored", units: { [SHOWN]: { factor: scale, offset } } };
|
|
59
|
+
return {
|
|
60
|
+
toDisplay: (stored) => {
|
|
61
|
+
const shown = (stored - offset) / scale;
|
|
62
|
+
return round === void 0 ? shown : Math.round(shown * round) / round;
|
|
63
|
+
},
|
|
64
|
+
fromDisplay: (shown) => storedOf(shown, scale, offset),
|
|
65
|
+
format: (stored) => `${formatUnit(stored, SHOWN, system, { precision: precision ?? 2, suffix: false })}${suffix ?? ""}`,
|
|
66
|
+
...suffix === void 0 ? {} : { suffix },
|
|
67
|
+
...accepts === void 0 ? {} : { accepts }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function prefUnit(system, display, opts) {
|
|
71
|
+
let self;
|
|
72
|
+
try {
|
|
73
|
+
self = unitScale(system, display);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
throw new Error(`prefUnit: ${e instanceof Error ? e.message : String(e)}`);
|
|
76
|
+
}
|
|
77
|
+
const suffix = opts?.suffix ?? display;
|
|
78
|
+
const accepts = { [suffix]: 1 };
|
|
79
|
+
for (const name of Object.keys(system.units)) {
|
|
80
|
+
const other = unitScale(system, name);
|
|
81
|
+
const factor = other.factor / self.factor;
|
|
82
|
+
const offset = (other.offset - self.offset) / self.factor;
|
|
83
|
+
accepts[name] = offset === 0 ? factor : { factor, offset };
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
suffix,
|
|
87
|
+
scale: self.factor,
|
|
88
|
+
...self.offset === 0 ? {} : { offset: self.offset },
|
|
89
|
+
accepts,
|
|
90
|
+
...opts?.precision === void 0 ? {} : { precision: opts.precision }
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function prefDisplayBounds(p) {
|
|
94
|
+
const ends = p.endless === void 0 ? {} : { endless: p.endless };
|
|
95
|
+
if (!p.unit) return { min: p.min, max: p.max, step: p.step ?? 1, ...ends };
|
|
96
|
+
const { toDisplay } = resolvePrefUnit(p.unit);
|
|
97
|
+
const backward = toDisplay(1) < toDisplay(0);
|
|
98
|
+
const lo = p.min === void 0 ? void 0 : toDisplay(p.min);
|
|
99
|
+
const hi = p.max === void 0 ? void 0 : toDisplay(p.max);
|
|
100
|
+
const flipped = lo !== void 0 && hi !== void 0 && lo > hi;
|
|
101
|
+
const step = p.step === void 0 ? 1 : Math.abs(toDisplay(p.step) - toDisplay(0)) || p.step;
|
|
102
|
+
const flip = { min: "max", max: "min", both: "both" };
|
|
103
|
+
const endless = backward && p.endless !== void 0 ? flip[p.endless] : p.endless;
|
|
104
|
+
return { min: flipped ? hi : lo, max: flipped ? lo : hi, step, ...endless === void 0 ? {} : { endless } };
|
|
105
|
+
}
|
|
106
|
+
function isPrefLeaf(node) {
|
|
107
|
+
return "kind" in node;
|
|
108
|
+
}
|
|
109
|
+
var VALUELESS = /* @__PURE__ */ new Set(["action", "actions", "alias", "label", "separator"]);
|
|
110
|
+
function prefHoldsValue(leaf) {
|
|
111
|
+
return !VALUELESS.has(leaf.kind);
|
|
112
|
+
}
|
|
113
|
+
function isPrefSection(node) {
|
|
114
|
+
return !isPrefLeaf(node) && "members" in node;
|
|
115
|
+
}
|
|
116
|
+
function prefSectionLeaves(members) {
|
|
117
|
+
return Object.entries(members).flatMap(
|
|
118
|
+
([key, child]) => isPrefLeaf(child) ? [[key, child]] : prefSectionLeaves(child.members)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
function prefLeafAt(schema, path) {
|
|
122
|
+
if (isPrefSection(schema)) return prefSectionLeaves(schema.members).find(([key]) => key === path)?.[1];
|
|
123
|
+
let node = schema;
|
|
124
|
+
for (const seg of path.split(".")) {
|
|
125
|
+
if (isPrefLeaf(node) || !Object.hasOwn(node.children, seg)) return void 0;
|
|
126
|
+
node = node.children[seg];
|
|
127
|
+
}
|
|
128
|
+
return isPrefLeaf(node) ? node : void 0;
|
|
129
|
+
}
|
|
130
|
+
function prefValueAtPath(values, path) {
|
|
131
|
+
let cur = values;
|
|
132
|
+
for (const seg of path.split(".")) {
|
|
133
|
+
if (cur == null || typeof cur !== "object" || !Object.hasOwn(cur, seg)) return void 0;
|
|
134
|
+
cur = cur[seg];
|
|
135
|
+
}
|
|
136
|
+
return cur;
|
|
137
|
+
}
|
|
138
|
+
function visibleFields(fields, keepEmpty) {
|
|
139
|
+
const out = {};
|
|
140
|
+
for (const [key, child] of Object.entries(fields)) {
|
|
141
|
+
if (isPrefLeaf(child)) {
|
|
142
|
+
const kept = visiblePrefSubtree(child, false, keepEmpty);
|
|
143
|
+
if (kept) out[key] = kept;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const members = visibleFields(child.members, keepEmpty);
|
|
147
|
+
if (Object.keys(members).length > 0 || keepEmpty) out[key] = { ...child, members };
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
function visiblePrefSubtree(node, showHidden, keepEmpty = false) {
|
|
152
|
+
if (isPrefLeaf(node)) {
|
|
153
|
+
if (node.hidden && !showHidden) return null;
|
|
154
|
+
if (showHidden || node.kind !== "object") return node;
|
|
155
|
+
return { ...node, children: visibleFields(node.children, keepEmpty) };
|
|
156
|
+
}
|
|
157
|
+
const group = node;
|
|
158
|
+
const children = {};
|
|
159
|
+
for (const [key, child] of Object.entries(group.children)) {
|
|
160
|
+
const kept = visiblePrefSubtree(child, showHidden, keepEmpty);
|
|
161
|
+
if (kept) children[key] = kept;
|
|
162
|
+
}
|
|
163
|
+
if (Object.keys(children).length === 0 && !keepEmpty) return null;
|
|
164
|
+
return { ...node, children };
|
|
165
|
+
}
|
|
166
|
+
var VERSION_RECORD = "$version";
|
|
167
|
+
function prefLeaves(schema) {
|
|
168
|
+
const out = /* @__PURE__ */ new Map();
|
|
169
|
+
const walk = (group, prefix) => {
|
|
170
|
+
for (const [key, child] of Object.entries(group.children)) {
|
|
171
|
+
if (key.includes(".")) {
|
|
172
|
+
throw new Error(`[prefs] schema key "${key}" contains "."; group keys are path segments`);
|
|
173
|
+
}
|
|
174
|
+
if (prefix === "" && key === VERSION_RECORD) {
|
|
175
|
+
throw new Error(`[prefs] schema key "${VERSION_RECORD}" is reserved`);
|
|
176
|
+
}
|
|
177
|
+
const path = prefix === "" ? key : `${prefix}.${key}`;
|
|
178
|
+
if (isPrefLeaf(child)) out.set(path, child);
|
|
179
|
+
else walk(child, path);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
walk(schema, "");
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
function setOwnPrefValue(tree, key, value) {
|
|
186
|
+
Object.defineProperty(tree, key, { value, writable: true, enumerable: true, configurable: true });
|
|
187
|
+
}
|
|
188
|
+
function assignPrefValueAtPath(root, path, value) {
|
|
189
|
+
const parts = path.split(".");
|
|
190
|
+
let cursor = root;
|
|
191
|
+
for (const part of parts.slice(0, -1)) {
|
|
192
|
+
if (!Object.hasOwn(cursor, part) || cursor[part] == null) setOwnPrefValue(cursor, part, {});
|
|
193
|
+
cursor = cursor[part];
|
|
194
|
+
}
|
|
195
|
+
setOwnPrefValue(cursor, parts[parts.length - 1], value);
|
|
196
|
+
}
|
|
197
|
+
function flattenPrefValues(schema, tree) {
|
|
198
|
+
const out = [];
|
|
199
|
+
for (const path of prefLeaves(schema).keys()) {
|
|
200
|
+
const value = prefValueAtPath(tree, path);
|
|
201
|
+
if (value !== void 0) out.push([path, value]);
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
function prefLeafMatches(pref, path, query) {
|
|
206
|
+
return matchQuery(query, { text: [pref.name, pref.description, path], tags: pref.tags }) !== null;
|
|
207
|
+
}
|
|
208
|
+
function filterPrefSubtree(node, query, path = "", over) {
|
|
209
|
+
const q = query.trim().toLowerCase();
|
|
210
|
+
if (q === "") return node;
|
|
211
|
+
const within = over ?? (isPrefLeaf(node) ? void 0 : node);
|
|
212
|
+
const find = (p) => within && prefLeafAt(within, p);
|
|
213
|
+
if (isPrefLeaf(node)) {
|
|
214
|
+
const target = node.kind === "alias" ? prefAliasTarget(node, find) : void 0;
|
|
215
|
+
const drawn = target ? prefAliasedLeaf(node, target.leaf) : node;
|
|
216
|
+
return prefLeafMatches(drawn, path, q) ? node : null;
|
|
217
|
+
}
|
|
218
|
+
const group = node;
|
|
219
|
+
if (matchQuery(q, { text: [group.name], tags: group.tags }) !== null) return node;
|
|
220
|
+
const children = {};
|
|
221
|
+
for (const [key, child] of Object.entries(group.children)) {
|
|
222
|
+
const kept = filterPrefSubtree(child, q, path === "" ? key : `${path}.${key}`, within);
|
|
223
|
+
if (kept) children[key] = kept;
|
|
224
|
+
}
|
|
225
|
+
if (Object.keys(children).length === 0) return null;
|
|
226
|
+
return { ...node, children };
|
|
227
|
+
}
|
|
228
|
+
function prefNumberAt(leaf, read) {
|
|
229
|
+
const { minAt, maxAt, ...rest } = leaf;
|
|
230
|
+
if (minAt === void 0 && maxAt === void 0) return leaf;
|
|
231
|
+
const lo = minAt?.(read);
|
|
232
|
+
const hi = maxAt?.(read);
|
|
233
|
+
return {
|
|
234
|
+
...rest,
|
|
235
|
+
...lo === void 0 ? {} : { min: leaf.min === void 0 ? lo : Math.max(leaf.min, lo) },
|
|
236
|
+
...hi === void 0 ? {} : { max: leaf.max === void 0 ? hi : Math.min(leaf.max, hi) }
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function prefClampNumber(leaf, n) {
|
|
240
|
+
if (endlessAllows(leaf.endless, n)) return n;
|
|
241
|
+
let v = n;
|
|
242
|
+
if (leaf.min !== void 0 && v < leaf.min) v = leaf.min;
|
|
243
|
+
if (leaf.max !== void 0 && v > leaf.max) v = leaf.max;
|
|
244
|
+
return v;
|
|
245
|
+
}
|
|
246
|
+
function isPrefLocus(value) {
|
|
247
|
+
if (!isPlainObject(value)) return false;
|
|
248
|
+
const keys = Object.keys(value);
|
|
249
|
+
return keys.length === 2 && typeof value.$by === "string" && isPlainObject(value.$of);
|
|
250
|
+
}
|
|
251
|
+
function selectionWeights(selection) {
|
|
252
|
+
return Object.fromEntries(Object.entries(selection).map(([path, option]) => [path, { [option]: 1 }]));
|
|
253
|
+
}
|
|
254
|
+
var member = (locus, option, absent) => Object.hasOwn(locus.$of, option) ? locus.$of[option] : absent;
|
|
255
|
+
function weighLocus(stored, weights, absent) {
|
|
256
|
+
const out = [];
|
|
257
|
+
const walk = (value, weight) => {
|
|
258
|
+
if (!isPrefLocus(value)) {
|
|
259
|
+
out.push({ value, weight });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
for (const [option, share] of Object.entries(weights[value.$by] ?? {})) {
|
|
263
|
+
if (share > 0) walk(member(value, option, absent), weight * share);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
walk(stored, 1);
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
function memberAt(stored, selection, absent) {
|
|
270
|
+
let value = stored;
|
|
271
|
+
while (isPrefLocus(value)) {
|
|
272
|
+
const option = selection[value.$by];
|
|
273
|
+
if (option === void 0) return absent;
|
|
274
|
+
value = member(value, option, absent);
|
|
275
|
+
}
|
|
276
|
+
return value;
|
|
277
|
+
}
|
|
278
|
+
function writeMember(stored, selection, next) {
|
|
279
|
+
if (!isPrefLocus(stored)) return next;
|
|
280
|
+
const option = selection[stored.$by];
|
|
281
|
+
if (option === void 0) return stored;
|
|
282
|
+
return { $by: stored.$by, $of: { ...stored.$of, [option]: writeMember(stored.$of[option], selection, next) } };
|
|
283
|
+
}
|
|
284
|
+
function splitLocus(stored, by, options, order) {
|
|
285
|
+
const across = (value) => ({ $by: by, $of: Object.fromEntries(options.map((option) => [option, value])) });
|
|
286
|
+
if (!isPrefLocus(stored)) return across(stored);
|
|
287
|
+
if (stored.$by === by) return stored;
|
|
288
|
+
if (order.indexOf(stored.$by) > order.indexOf(by)) return across(stored);
|
|
289
|
+
return mapMembers(stored, (inner) => splitLocus(inner, by, options, order));
|
|
290
|
+
}
|
|
291
|
+
function joinLocus(stored, by, selection, absent) {
|
|
292
|
+
if (!isPrefLocus(stored)) return stored;
|
|
293
|
+
if (stored.$by !== by) return mapMembers(stored, (inner) => joinLocus(inner, by, selection, absent));
|
|
294
|
+
const option = selection[by];
|
|
295
|
+
return option === void 0 ? absent : member(stored, option, absent);
|
|
296
|
+
}
|
|
297
|
+
function mapMembers(locus, fn) {
|
|
298
|
+
let out;
|
|
299
|
+
for (const [option, value] of Object.entries(locus.$of)) {
|
|
300
|
+
const next = fn(value);
|
|
301
|
+
if (next !== value) (out ??= { ...locus.$of })[option] = next;
|
|
302
|
+
}
|
|
303
|
+
return out ? { $by: locus.$by, $of: out } : locus;
|
|
304
|
+
}
|
|
305
|
+
function mapLocusMembers(stored, fn) {
|
|
306
|
+
return isPrefLocus(stored) ? mapMembers(stored, (inner) => mapLocusMembers(inner, fn)) : fn(stored);
|
|
307
|
+
}
|
|
308
|
+
function locusSelectors(stored) {
|
|
309
|
+
const out = /* @__PURE__ */ new Set();
|
|
310
|
+
const walk = (value) => {
|
|
311
|
+
if (!isPrefLocus(value)) return;
|
|
312
|
+
out.add(value.$by);
|
|
313
|
+
for (const inner of Object.values(value.$of)) walk(inner);
|
|
314
|
+
};
|
|
315
|
+
walk(stored);
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
function repairLocus(stored, allowed, optionsOf, repairMember) {
|
|
319
|
+
const BAD = /* @__PURE__ */ Symbol("bad");
|
|
320
|
+
const walk = (value, met) => {
|
|
321
|
+
if (!isPrefLocus(value)) return repairMember(value);
|
|
322
|
+
const options = allowed.includes(value.$by) && !met.includes(value.$by) ? optionsOf(value.$by) : void 0;
|
|
323
|
+
if (!options) return BAD;
|
|
324
|
+
const of = {};
|
|
325
|
+
for (const option of options) {
|
|
326
|
+
if (!Object.hasOwn(value.$of, option)) continue;
|
|
327
|
+
const read2 = walk(value.$of[option], [...met, value.$by]);
|
|
328
|
+
if (read2 === BAD) return BAD;
|
|
329
|
+
of[option] = read2;
|
|
330
|
+
}
|
|
331
|
+
return { $by: value.$by, $of: of };
|
|
332
|
+
};
|
|
333
|
+
const read = walk(stored, []);
|
|
334
|
+
return read === BAD ? void 0 : read;
|
|
335
|
+
}
|
|
336
|
+
var heaviest = (members) => members.reduce((most, m) => m.weight > most.weight ? m : most).value;
|
|
337
|
+
var blendNumber = (members, leaf) => {
|
|
338
|
+
let sum = 0;
|
|
339
|
+
let total = 0;
|
|
340
|
+
for (const { value, weight } of members) {
|
|
341
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return heaviest(members);
|
|
342
|
+
sum += value * weight;
|
|
343
|
+
total += weight;
|
|
344
|
+
}
|
|
345
|
+
return sum / total;
|
|
346
|
+
};
|
|
347
|
+
var blendColor = (members, leaf) => {
|
|
348
|
+
let mixed;
|
|
349
|
+
let total = 0;
|
|
350
|
+
for (const { value, weight } of members) {
|
|
351
|
+
let next;
|
|
352
|
+
try {
|
|
353
|
+
next = hexToRgba(value);
|
|
354
|
+
} catch {
|
|
355
|
+
return heaviest(members);
|
|
356
|
+
}
|
|
357
|
+
total += weight;
|
|
358
|
+
if (!mixed) {
|
|
359
|
+
mixed = next;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const t = weight / total;
|
|
363
|
+
const [r, g, b] = interpolateSrgb([mixed[0], mixed[1], mixed[2]], [next[0], next[1], next[2]], t, "oklch");
|
|
364
|
+
mixed = [r, g, b, mixed[3] + (next[3] - mixed[3]) * t];
|
|
365
|
+
}
|
|
366
|
+
return rgbaToHex(mixed);
|
|
367
|
+
};
|
|
368
|
+
var PREF_BLENDS = { number: blendNumber, color: blendColor };
|
|
369
|
+
function blendPref(leaf, members, blends) {
|
|
370
|
+
if (members.length === 0) return leaf.default;
|
|
371
|
+
if (members.length === 1) return members[0].value;
|
|
372
|
+
const blend = (blends && Object.hasOwn(blends, leaf.kind) ? blends[leaf.kind] : void 0) ?? (Object.hasOwn(PREF_BLENDS, leaf.kind) ? PREF_BLENDS[leaf.kind] : void 0) ?? heaviest;
|
|
373
|
+
return blend(members, leaf);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/splitCheck.ts
|
|
377
|
+
var refusal = (leaf) => {
|
|
378
|
+
if (!leaf) return ["no-leaf", "names no leaf"];
|
|
379
|
+
if (leaf.kind !== "enum") return ["not-enum", `names a ${leaf.kind} leaf, and only an enum has options to split by`];
|
|
380
|
+
const selector = leaf;
|
|
381
|
+
if (selector.encoding) return ["encoded", "names an enum with an encoding, whose stored value is not its option"];
|
|
382
|
+
if (selector.clearable) return ["clearable", "names a clearable enum, which may hold none of its options"];
|
|
383
|
+
if (selector.splitBy?.length) return ["split", "names a leaf that may itself be split"];
|
|
384
|
+
return void 0;
|
|
385
|
+
};
|
|
386
|
+
function prefSelectors(leaves) {
|
|
387
|
+
const out = /* @__PURE__ */ new Map();
|
|
388
|
+
for (const leaf of leaves.values()) {
|
|
389
|
+
for (const path of leaf.splitBy ?? []) {
|
|
390
|
+
const selector = leaves.get(path);
|
|
391
|
+
if (out.has(path) || refusal(selector)) continue;
|
|
392
|
+
out.set(path, selector.options.map((option) => option.value));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return out;
|
|
396
|
+
}
|
|
397
|
+
function checkPrefSplits(schema) {
|
|
398
|
+
const leaves = prefLeaves(schema);
|
|
399
|
+
const out = [];
|
|
400
|
+
for (const [at, leaf] of leaves) {
|
|
401
|
+
for (const path of locusSelectors(leaf.defaultLocus)) {
|
|
402
|
+
if (leaf.splitBy?.includes(path)) continue;
|
|
403
|
+
out.push({ code: "default", at, path, problem: `its defaultLocus is split by "${path}", which its splitBy does not list` });
|
|
404
|
+
}
|
|
405
|
+
if (!leaf.splitBy?.length) continue;
|
|
406
|
+
if (!prefHoldsValue(leaf)) {
|
|
407
|
+
out.push({ code: "valueless", at, problem: `a ${leaf.kind} leaf holds no value to split` });
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
leaf.splitBy.forEach((path, i) => {
|
|
411
|
+
if (leaf.splitBy.indexOf(path) !== i) {
|
|
412
|
+
out.push({ code: "twice", at, path, problem: `"${path}" is listed twice` });
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const why = refusal(leaves.get(path));
|
|
416
|
+
if (why) out.push({ code: why[0], at, path, problem: `"${path}" ${why[1]}` });
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
return out;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/css.ts
|
|
423
|
+
var kebab = (path) => path.replace(/\./g, "-").replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
424
|
+
var CSS_UNITS = {
|
|
425
|
+
px: "px",
|
|
426
|
+
"%": "%",
|
|
427
|
+
em: "em",
|
|
428
|
+
rem: "rem",
|
|
429
|
+
ch: "ch",
|
|
430
|
+
pt: "pt",
|
|
431
|
+
vw: "vw",
|
|
432
|
+
vh: "vh",
|
|
433
|
+
ms: "ms",
|
|
434
|
+
s: "s",
|
|
435
|
+
"\xB0": "deg",
|
|
436
|
+
deg: "deg",
|
|
437
|
+
rad: "rad",
|
|
438
|
+
turn: "turn"
|
|
439
|
+
};
|
|
440
|
+
var IDENT = /^-?[A-Za-z_][\w-]*$/;
|
|
441
|
+
var quoted = (text) => `'${text.replace(/[\\']/g, "\\$&")}'`;
|
|
442
|
+
function cssValue(leaf, value) {
|
|
443
|
+
switch (leaf.kind) {
|
|
444
|
+
case "number": {
|
|
445
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
446
|
+
const { unit } = leaf;
|
|
447
|
+
const resolved = unit ? resolvePrefUnit(unit) : void 0;
|
|
448
|
+
const shown = resolved ? resolved.toDisplay(value) : value;
|
|
449
|
+
const suffix = resolved?.suffix !== void 0 && Object.hasOwn(CSS_UNITS, resolved.suffix) ? CSS_UNITS[resolved.suffix] : "";
|
|
450
|
+
return `${Number(shown.toFixed(6))}${suffix}`;
|
|
451
|
+
}
|
|
452
|
+
case "boolean":
|
|
453
|
+
return typeof value === "boolean" ? value ? "1" : "0" : null;
|
|
454
|
+
case "color":
|
|
455
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
456
|
+
case "enum":
|
|
457
|
+
if (typeof value !== "string" || leaf.encoding) return null;
|
|
458
|
+
return IDENT.test(value) ? value : quoted(value);
|
|
459
|
+
case "string":
|
|
460
|
+
return typeof value === "string" ? quoted(value) : null;
|
|
461
|
+
default:
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function toldApart(held, optionsOf, absent) {
|
|
466
|
+
const out = [];
|
|
467
|
+
const walk = (value, under) => {
|
|
468
|
+
if (!isPrefLocus(value)) {
|
|
469
|
+
out.push({ under, value });
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
for (const option of optionsOf(value.$by)) {
|
|
473
|
+
walk(Object.hasOwn(value.$of, option) ? value.$of[option] : absent, [...under, [value.$by, option]]);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
walk(held, []);
|
|
477
|
+
return out;
|
|
478
|
+
}
|
|
479
|
+
function prefsCss(store, options = {}) {
|
|
480
|
+
const root = options.root ?? ":root";
|
|
481
|
+
const property = options.property ?? ((path) => `--${kebab(path)}`);
|
|
482
|
+
const attribute = options.attribute ?? ((selector) => `data-${kebab(selector)}`);
|
|
483
|
+
const leaves = new Map([...prefLeaves(store.schema)].filter(([, leaf]) => prefHoldsValue(leaf)));
|
|
484
|
+
const selectors = prefSelectors(leaves);
|
|
485
|
+
const defaults = Object.fromEntries([...selectors.keys()].map((path) => [path, leaves.get(path).default]));
|
|
486
|
+
const base = store.values({ at: defaults });
|
|
487
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
488
|
+
const declare = (under, path, leaf, value) => {
|
|
489
|
+
const name = property(path, leaf);
|
|
490
|
+
if (name === null) return;
|
|
491
|
+
const given = options.value?.(value, leaf, path);
|
|
492
|
+
const text = given === void 0 ? cssValue(leaf, value) : given;
|
|
493
|
+
if (text === null) return;
|
|
494
|
+
const key = under.map(([selector, option]) => `${selector}=${option}`).join("&");
|
|
495
|
+
if (!blocks.has(key)) blocks.set(key, { under, lines: [] });
|
|
496
|
+
blocks.get(key).lines.push(` ${name}: ${text};`);
|
|
497
|
+
};
|
|
498
|
+
for (const [path, leaf] of leaves) {
|
|
499
|
+
declare([], path, leaf, prefValueAtPath(base, path));
|
|
500
|
+
const own = selectors.get(path);
|
|
501
|
+
if (own) for (const option of own) declare([[path, option]], path, leaf, option);
|
|
502
|
+
const held = store.locus(path);
|
|
503
|
+
if (!held) continue;
|
|
504
|
+
for (const { under, value } of toldApart(held, (selector) => selectors.get(selector) ?? [], leaf.default)) {
|
|
505
|
+
declare(under, path, leaf, value);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return [...blocks.values()].map((block, i) => ({ ...block, i })).sort((a, b) => a.under.length - b.under.length || a.i - b.i).map(({ under, lines }) => {
|
|
509
|
+
const matched = under.map(([selector, option]) => `[${attribute(selector)}=${quoted(option)}]`).join("");
|
|
510
|
+
const at = root === ":host" && matched !== "" ? `:host(${matched})` : `${root}${matched}`;
|
|
511
|
+
return `:where(${at}) {
|
|
512
|
+
${lines.join("\n")}
|
|
513
|
+
}`;
|
|
514
|
+
}).join("\n\n");
|
|
515
|
+
}
|
|
516
|
+
function prefsCssAttributes(store, options = {}) {
|
|
517
|
+
const attribute = options.attribute ?? ((selector) => `data-${kebab(selector)}`);
|
|
518
|
+
const leaves = prefLeaves(store.schema);
|
|
519
|
+
return Object.fromEntries([...prefSelectors(leaves).keys()].map((path) => [attribute(path), store.get(path)]));
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/union.ts
|
|
523
|
+
function prefVariantOf(leaf, value) {
|
|
524
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
525
|
+
const key = value[leaf.tag];
|
|
526
|
+
if (typeof key !== "string" || !Object.hasOwn(leaf.variants, key)) return void 0;
|
|
527
|
+
return [key, leaf.variants[key]];
|
|
528
|
+
}
|
|
529
|
+
function prefVariantDefault(leaf, key) {
|
|
530
|
+
const held = leaf.variants[key]?.default;
|
|
531
|
+
const fields = held !== null && typeof held === "object" ? held : {};
|
|
532
|
+
return { ...fields, [leaf.tag]: key };
|
|
533
|
+
}
|
|
534
|
+
function repairPrefValue(leaf, stored, validators) {
|
|
535
|
+
if (leaf.kind === "number") {
|
|
536
|
+
if (stored === "Infinity") stored = Infinity;
|
|
537
|
+
else if (stored === "-Infinity") stored = -Infinity;
|
|
538
|
+
}
|
|
539
|
+
const validate = validators?.[leaf.kind];
|
|
540
|
+
if (validate) {
|
|
541
|
+
try {
|
|
542
|
+
const out = validate(stored, leaf);
|
|
543
|
+
return out === void 0 ? leaf.default : out;
|
|
544
|
+
} catch {
|
|
545
|
+
return leaf.default;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
switch (leaf.kind) {
|
|
549
|
+
case "number":
|
|
550
|
+
return repairNumber(leaf, stored);
|
|
551
|
+
case "boolean":
|
|
552
|
+
return leaf.encoding || typeof stored === "boolean" ? stored : leaf.default;
|
|
553
|
+
case "string":
|
|
554
|
+
case "color":
|
|
555
|
+
case "field":
|
|
556
|
+
return typeof stored === "string" ? stored : leaf.default;
|
|
557
|
+
case "enum": {
|
|
558
|
+
const e = leaf;
|
|
559
|
+
if (e.encoding) return stored;
|
|
560
|
+
return e.options.some((o) => o.value === stored) ? stored : leaf.default;
|
|
561
|
+
}
|
|
562
|
+
case "object":
|
|
563
|
+
if (isPlainObject(stored)) return repairFields(leaf, stored, validators);
|
|
564
|
+
return leaf.fromScalar ? stored : leaf.default;
|
|
565
|
+
case "map":
|
|
566
|
+
return repairMap(leaf, stored, validators);
|
|
567
|
+
case "union": {
|
|
568
|
+
const variant = prefVariantOf(leaf, stored);
|
|
569
|
+
return variant ? repairFields(variant[1], stored, validators) : leaf.default;
|
|
570
|
+
}
|
|
571
|
+
case "list":
|
|
572
|
+
return repairList(leaf, stored, validators);
|
|
573
|
+
default:
|
|
574
|
+
return stored;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function repairFields(leaf, stored, validators) {
|
|
578
|
+
let out;
|
|
579
|
+
for (const [key, field] of prefSectionLeaves(leaf.children)) {
|
|
580
|
+
if (!Object.hasOwn(stored, key) || stored[key] === void 0) continue;
|
|
581
|
+
const read = repairPrefValue(field, stored[key], validators);
|
|
582
|
+
if (read !== stored[key]) (out ??= { ...stored })[key] = read;
|
|
583
|
+
}
|
|
584
|
+
return out ?? stored;
|
|
585
|
+
}
|
|
586
|
+
function repairMap(leaf, stored, validators) {
|
|
587
|
+
if (!isPlainObject(stored)) return leaf.default;
|
|
588
|
+
const held = stored;
|
|
589
|
+
let out;
|
|
590
|
+
for (const [key, value] of Object.entries(held)) {
|
|
591
|
+
const read = repairPrefValue(leaf.item, value, validators);
|
|
592
|
+
if (read !== value) (out ??= { ...held })[key] = read;
|
|
593
|
+
}
|
|
594
|
+
return out ?? stored;
|
|
595
|
+
}
|
|
596
|
+
function repairList(leaf, stored, validators) {
|
|
597
|
+
if (!Array.isArray(stored)) return leaf.default;
|
|
598
|
+
if (leaf.minItems !== void 0 && stored.length < leaf.minItems) return leaf.default;
|
|
599
|
+
const kept = leaf.maxItems !== void 0 && stored.length > leaf.maxItems ? stored.slice(0, leaf.maxItems) : stored;
|
|
600
|
+
const read = kept.map((entry) => repairPrefValue(leaf.item, entry, validators));
|
|
601
|
+
return kept === stored && read.every((entry, i) => entry === stored[i]) ? stored : read;
|
|
602
|
+
}
|
|
603
|
+
function repairNumber(leaf, stored) {
|
|
604
|
+
if (typeof stored !== "number" || Number.isNaN(stored)) return leaf.default;
|
|
605
|
+
if (endlessAllows(leaf.endless, stored)) return stored;
|
|
606
|
+
const v = prefClampNumber(leaf, stored);
|
|
607
|
+
return Number.isFinite(v) ? v : leaf.default;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/stamp.ts
|
|
611
|
+
var isVersion = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0;
|
|
612
|
+
function stampRecord(value, at) {
|
|
613
|
+
return { $at: at, value };
|
|
614
|
+
}
|
|
615
|
+
function isStampedRecord(record) {
|
|
616
|
+
if (record === null || typeof record !== "object") return false;
|
|
617
|
+
const keys = Object.keys(record);
|
|
618
|
+
return keys.length === 2 && Object.hasOwn(record, "value") && isVersion(record.$at);
|
|
619
|
+
}
|
|
620
|
+
function readRecord(record, unstamped) {
|
|
621
|
+
return isStampedRecord(record) ? { at: record.$at, value: record.value, stamped: true } : { at: unstamped, value: record, stamped: false };
|
|
622
|
+
}
|
|
623
|
+
function recordValue(record) {
|
|
624
|
+
return isStampedRecord(record) ? record.value : record;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// src/snapshot.ts
|
|
628
|
+
function readStored(source, leaf, stored) {
|
|
629
|
+
const repair = (value) => repairPrefValue(leaf, value, source.validators);
|
|
630
|
+
if (!leaf.splitBy?.length || !isPrefLocus(stored)) return repair(stored);
|
|
631
|
+
const read = repairLocus(stored, leaf.splitBy, (path) => source.selectors.get(path), repair);
|
|
632
|
+
return read === void 0 ? leaf.default : read;
|
|
633
|
+
}
|
|
634
|
+
function resolveHeld(source, leaf, held, weights) {
|
|
635
|
+
return isPrefLocus(held) ? blendPref(leaf, weighLocus(held, weights, leaf.default), source.blends) : held;
|
|
636
|
+
}
|
|
637
|
+
function createSnapshots(source) {
|
|
638
|
+
const { leaves, cache, selectors } = source;
|
|
639
|
+
let snapshot = null;
|
|
640
|
+
let previous = null;
|
|
641
|
+
const unsetLoci = /* @__PURE__ */ new Map();
|
|
642
|
+
const unsetHeld = (path, leaf) => {
|
|
643
|
+
if (leaf.defaultLocus === void 0) return leaf.default;
|
|
644
|
+
if (!unsetLoci.has(path)) unsetLoci.set(path, readStored(source, leaf, leaf.defaultLocus));
|
|
645
|
+
return unsetLoci.get(path);
|
|
646
|
+
};
|
|
647
|
+
const build = () => {
|
|
648
|
+
const held = /* @__PURE__ */ new Map();
|
|
649
|
+
const raw = /* @__PURE__ */ new Map();
|
|
650
|
+
const unset = /* @__PURE__ */ new Set();
|
|
651
|
+
for (const [path, leaf] of leaves) {
|
|
652
|
+
if (!cache.has(path)) {
|
|
653
|
+
held.set(path, unsetHeld(path, leaf));
|
|
654
|
+
unset.add(path);
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
const record = cache.get(path);
|
|
658
|
+
raw.set(path, record);
|
|
659
|
+
const same = previous?.raw.has(path) && previous.raw.get(path) === record;
|
|
660
|
+
held.set(path, same ? previous.held.get(path) : readStored(source, leaf, recordValue(record)));
|
|
661
|
+
}
|
|
662
|
+
previous = { raw, held };
|
|
663
|
+
const selection = {};
|
|
664
|
+
for (const path of selectors.keys()) selection[path] = held.get(path);
|
|
665
|
+
const weights = selectionWeights(selection);
|
|
666
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
667
|
+
const splitBy = /* @__PURE__ */ new Map();
|
|
668
|
+
const loci = /* @__PURE__ */ new Map();
|
|
669
|
+
const tree = {};
|
|
670
|
+
for (const [path, leaf] of leaves) {
|
|
671
|
+
const value = held.get(path);
|
|
672
|
+
if (isPrefLocus(value)) loci.set(path, value);
|
|
673
|
+
for (const selector of locusSelectors(value)) splitBy.set(selector, [...splitBy.get(selector) ?? [], path]);
|
|
674
|
+
const read = resolveHeld(source, leaf, value, weights);
|
|
675
|
+
byPath.set(path, read);
|
|
676
|
+
assignPrefValueAtPath(tree, path, read);
|
|
677
|
+
}
|
|
678
|
+
return { byPath, held, loci, selection, splitBy, tree, unset };
|
|
679
|
+
};
|
|
680
|
+
return {
|
|
681
|
+
current: () => snapshot ??= build(),
|
|
682
|
+
peek: () => snapshot,
|
|
683
|
+
drop: () => {
|
|
684
|
+
snapshot = null;
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function weightsUnder(selectors, selection, given) {
|
|
689
|
+
const out = {};
|
|
690
|
+
for (const [path, options] of selectors) {
|
|
691
|
+
const own = Object.hasOwn(given, path) ? given[path] : void 0;
|
|
692
|
+
out[path] = own ? Object.fromEntries(options.filter((option) => (own[option] ?? 0) > 0).map((option) => [option, own[option]])) : { [selection[path]]: 1 };
|
|
693
|
+
}
|
|
694
|
+
return out;
|
|
695
|
+
}
|
|
696
|
+
function selectionAt(selectors, selection, at) {
|
|
697
|
+
const out = { ...selection };
|
|
698
|
+
for (const [path, option] of Object.entries(at)) {
|
|
699
|
+
if (selectors.get(path)?.includes(option)) out[path] = option;
|
|
700
|
+
}
|
|
701
|
+
return out;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export { PREF_BLENDS, VERSION_RECORD, assignPrefValueAtPath, checkPrefSplits, createSnapshots, filterPrefSubtree, flattenPrefValues, isPrefLeaf, isPrefLinearUnit, isPrefLocus, isPrefSection, joinLocus, kebab, locusSelectors, mapLocusMembers, memberAt, prefAliasName, prefAliasTarget, prefAliasedLeaf, prefClampNumber, prefDisplayBounds, prefHoldsValue, prefLeafAt, prefLeaves, prefNumberAt, prefSectionLeaves, prefSelectors, prefUnit, prefUnitAccepts, prefValueAtPath, prefVariantDefault, prefVariantOf, prefsCss, prefsCssAttributes, readRecord, readStored, recordValue, repairLocus, repairPrefValue, resolveHeld, resolvePrefUnit, selectionAt, selectionWeights, setOwnPrefValue, splitLocus, stampRecord, visiblePrefSubtree, weightsUnder, writeMember };
|
|
705
|
+
//# sourceMappingURL=chunk-OZFER7VN.js.map
|
|
706
|
+
//# sourceMappingURL=chunk-OZFER7VN.js.map
|