@weasel-js/prefs 1.9.4 → 1.10.1

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.
@@ -1,4 +1,81 @@
1
- import { Display, UnitEntry, InfinityText, UnitSystem, Unit } from '@weasel-js/quantity';
1
+ import { StorageAdapter, SyncStorageAdapter } from '@weasel-js/storage';
2
+ import { UnitEntry, UnitSystem, Unit, Display, InfinityText } from '@weasel-js/quantity';
3
+
4
+ /** What a condition can compare a leaf's value against. `null` stands for
5
+ * nothing held: a missing value as well as a stored `null`. */
6
+ type PrefConditionValue = string | number | boolean | null;
7
+ /**
8
+ * One leaf's value, tested. `path` is the full path the value is read and
9
+ * written at (`'windows.paging'`), a field of an `object` or `union` leaf
10
+ * included (`'stroke.width'`, `'fill.type'`). With `from: 'item'` the path is
11
+ * read from the entry the test's node is drawn in instead: the entry of the
12
+ * nearest list or map the node is the `item` of, or sits inside the item of.
13
+ * `{ from: 'item', path: 'mode' }` on a field of a list's item reads `mode`
14
+ * of the same entry, and an empty path is the entry itself. The value compared is the one
15
+ * the leaf's control shows, where an `encoding` stores another. With
16
+ * several operators it holds while all of them do, so
17
+ * `{ path: 'count', atLeast: 1, below: 10 }` is a range.
18
+ *
19
+ * With no operator it holds while the value is set, which by kind is:
20
+ * a `boolean` that is on; a `number` other than 0; a `string`, a `color`, a
21
+ * `field`, or an `enum`'s option that is not the empty string; a `paint`
22
+ * that is not `null`; a `list` with an entry; a `map` with a key; an
23
+ * `object` or a `union` holding a field. Nothing held is never set, and the
24
+ * value of an app-defined kind is set by whichever of those its shape is,
25
+ * else while it is truthy.
26
+ */
27
+ interface PrefTest {
28
+ path: string;
29
+ /** Where `path` is read from: the entry the node is drawn in. Unset: the schema root. */
30
+ from?: 'item';
31
+ is?: PrefConditionValue;
32
+ isNot?: PrefConditionValue;
33
+ /** Holds while the value is any of these. */
34
+ in?: readonly PrefConditionValue[];
35
+ /** The ordering operators hold only for a number. */
36
+ above?: number;
37
+ below?: number;
38
+ atLeast?: number;
39
+ atMost?: number;
40
+ }
41
+ /** A condition a schema node holds in `showIf` or `enableIf`. An empty `all`
42
+ * holds and an empty `any` does not. */
43
+ type PrefCondition = PrefTest | {
44
+ all: readonly PrefCondition[];
45
+ } | {
46
+ any: readonly PrefCondition[];
47
+ } | {
48
+ not: PrefCondition;
49
+ };
50
+ /** Reads the value a test compares: at `path` from the schema root, or with `from`, from the entry being drawn. */
51
+ type PrefConditionReader = (path: string, from?: 'item') => unknown;
52
+ /** Whether `value` is a well-formed condition, all the way down. */
53
+ declare function isPrefCondition(value: unknown): value is PrefCondition;
54
+ /** Whether `condition` holds, given the value `read` finds at each path it names. */
55
+ declare function prefConditionHolds(condition: PrefCondition, read: PrefConditionReader): boolean;
56
+ /** Whether a node is drawn: its `showIf` holds, or it has none. A node
57
+ * answers for itself only; one inside a group that is not shown is not
58
+ * drawn either. */
59
+ declare function prefIsShown(node: {
60
+ showIf?: PrefCondition;
61
+ }, read: PrefConditionReader): boolean;
62
+ /** Whether a node's controls take input: its `enableIf` holds, or it has
63
+ * none. As {@link prefIsShown}, a node answers for itself only. */
64
+ declare function prefIsEnabled(node: {
65
+ enableIf?: PrefCondition;
66
+ }, read: PrefConditionReader): boolean;
67
+ /** Every path `condition` reads from the schema root, or with `from`, from the entry, once each, in the order met. */
68
+ declare function prefConditionPaths(condition: PrefCondition, from?: 'item'): string[];
69
+ /** Whether any test of `condition` reads from the entry, so that it has no answer until an entry is drawn. */
70
+ declare function prefConditionReadsItem(condition: PrefCondition): boolean;
71
+ /**
72
+ * `condition` with every path it reads put through `move`, for following a
73
+ * key that was renamed or a leaf that moved. `move` is told where a path is
74
+ * read from, and what it hands back is read from there too. A condition none
75
+ * of whose paths change comes back as the object it was given, and so does
76
+ * each part of one.
77
+ */
78
+ declare function mapPrefConditionPaths(condition: PrefCondition, move: (path: string, from?: 'item') => string): PrefCondition;
2
79
 
3
80
  /** What a {@link PrefAction}'s `run` is told. */
4
81
  interface PrefActionContext {
@@ -52,6 +129,12 @@ declare function prefAliasTarget(alias: PrefAlias, leafAt: (path: string) => Pre
52
129
  path: string;
53
130
  leaf: PrefLeaf;
54
131
  } | undefined;
132
+ /**
133
+ * What an alias is called wherever one is drawn or named: its own name, else
134
+ * that of `target`, the leaf it shows, else `Alias` for one that shows
135
+ * nothing.
136
+ */
137
+ declare function prefAliasName(alias: PrefAlias, target?: PrefLeaf): string;
55
138
  /** A leaf drawn through an alias: the target, under the alias's own name and description where it gives them. */
56
139
  declare function prefAliasedLeaf(alias: PrefAlias, target: PrefLeaf): PrefLeaf;
57
140
 
@@ -81,6 +164,33 @@ interface PrefList extends PrefBase<'list', readonly unknown[]> {
81
164
  maxItems?: number;
82
165
  }
83
166
 
167
+ /**
168
+ * A leaf's value split by another leaf: one member for each option of the
169
+ * `enum` leaf at `$by`. A member may itself be a locus, by a second selector.
170
+ * The alternatives are folded by weight when the value is read, and a plain
171
+ * setting of the selector gives one of them all of it.
172
+ */
173
+ interface PrefLocus {
174
+ /** The full path of the `enum` leaf whose options key `$of`. */
175
+ $by: string;
176
+ $of: Record<string, unknown>;
177
+ }
178
+ /** A value for each selector a read is made under: its path, then the option chosen. */
179
+ type PrefSelection = Readonly<Record<string, string>>;
180
+ /** A weight for each option of each selector, by the selector's path. An option left out weighs nothing. */
181
+ type PrefWeights = Readonly<Record<string, Readonly<Record<string, number>>>>;
182
+ /** One alternative of a split value, and its share of the whole. */
183
+ interface PrefWeighted {
184
+ value: unknown;
185
+ weight: number;
186
+ }
187
+ /** Whether `value` is a {@link PrefLocus}: an object of exactly `$by` and `$of`. */
188
+ declare function isPrefLocus(value: unknown): value is PrefLocus;
189
+ /** The one member `selection` names. A selector it leaves out, or an option the locus holds nothing for, reads as `absent`. */
190
+ declare function memberAt(stored: unknown, selection: PrefSelection, absent: unknown): unknown;
191
+ /** The path of every selector `stored` is split by, outermost first. */
192
+ declare function locusSelectors(stored: unknown): Set<string>;
193
+
84
194
  /**
85
195
  * A leaf whose value is an object keyed by strings nobody declared, every
86
196
  * value described by `item`: the keyed counterpart of a {@link PrefList}.
@@ -110,13 +220,67 @@ declare function prefVariantOf(leaf: PrefUnion, value: unknown): [key: string, v
110
220
  /** The value a union takes on switching to the variant at `key`: that variant's default, tagged. */
111
221
  declare function prefVariantDefault(leaf: PrefUnion, key: string): Record<string, unknown>;
112
222
 
113
- /** The value types a built-in pref leaf can hold. */
114
- type PrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint' | 'object' | 'list' | 'map' | 'union' | 'field' | 'action' | 'alias' | 'label' | 'separator';
223
+ /** What both forms of a unit say about the text around the number. */
224
+ interface PrefUnitText {
225
+ /** Shown after the input, e.g. `'°'`. */
226
+ suffix?: string;
227
+ /** Suffixes a person may type, each mapped to the scale that turns a
228
+ * number in that unit into a display number: `{ mm: 0.1, cm: 1 }` for a
229
+ * field showing centimeters. A unit that disagrees with the display unit
230
+ * about zero carries an `offset` as well. */
231
+ accepts?: Readonly<Record<string, UnitEntry>>;
232
+ }
115
233
  /**
116
- * Leaves that share one row in a compact property UI (weasel-ui
117
- * `SelectionPanel`, labkit's `ControlPanel`), declared on the first of them.
118
- * Purely presentational.
234
+ * A display unit as data, for a conversion that is linear: `stored = display * scale + offset`, the direction a
235
+ * `UnitEntry` runs toward its base, so `scale` is what one display unit is worth in stored units. Degrees shown
236
+ * over radians stored is `{ suffix: '°', scale: Math.PI / 180 }`, and `{ scale: 1 }` changes nothing. Being data,
237
+ * it is written to JSON, printed in a schema literal, and edited in `PrefSchemaEditor` like any other attribute.
119
238
  */
239
+ interface PrefLinearUnit extends PrefUnitText {
240
+ /** Stored units per display unit. Never 0. */
241
+ scale: number;
242
+ /** The stored value a display of 0 stands for. Absent is 0. */
243
+ offset?: number;
244
+ /** Decimal places the shown value is rounded to. Absent, the value shows unrounded and formats to two places.
245
+ * What is typed is stored unrounded either way: a decimal typed at a decimal `scale` is stored as the
246
+ * decimal the two multiply to, without the dust of multiplying them in binary. */
247
+ precision?: number;
248
+ }
249
+ /** A display unit as code, for a conversion that is not linear. It cannot be written to JSON or edited as data. */
250
+ interface PrefFunctionUnit extends PrefUnitText {
251
+ toDisplay: (stored: number) => number;
252
+ fromDisplay: (display: number) => number;
253
+ /** The stored value as display text, suffix included. */
254
+ format?: (stored: number) => string;
255
+ }
256
+ /** Display-unit conversion for number leaves whose stored value uses a
257
+ * canonical unit the user shouldn't see (e.g. radians stored, degrees
258
+ * shown). Read one through {@link resolvePrefUnit}, which answers for both forms. */
259
+ type PrefNumberUnit = PrefLinearUnit | PrefFunctionUnit;
260
+ /** A unit of either form as the functions that convert and format with it. */
261
+ interface ResolvedPrefUnit extends PrefFunctionUnit {
262
+ format: (stored: number) => string;
263
+ }
264
+ /** Whether `unit` is the data form, and so can be written out and edited. */
265
+ declare function isPrefLinearUnit(unit: PrefNumberUnit): unit is PrefLinearUnit;
266
+ /** How to convert and format with `unit`, whichever form it was written in. */
267
+ declare function resolvePrefUnit(unit: PrefNumberUnit): ResolvedPrefUnit;
268
+ /**
269
+ * A display unit built from a {@link UnitSystem}: values are stored in the
270
+ * system's base and shown in `display`, rounded to `precision` places when
271
+ * given, and every unit in the system is accepted as typed text. `suffix`
272
+ * defaults to the unit's name and is accepted too. Every unit of a system is
273
+ * linear, so what comes back is data.
274
+ */
275
+ declare function prefUnit(system: UnitSystem, display: Unit, opts?: {
276
+ precision?: number;
277
+ suffix?: string;
278
+ }): PrefLinearUnit;
279
+
280
+ /** The value types a built-in pref leaf can hold. */
281
+ type PrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint' | 'object' | 'list' | 'map' | 'union' | 'field' | 'action' | 'alias' | 'label' | 'separator';
282
+ /** Leaves that share one row in a compact property UI, declared on the first
283
+ * of them. Purely presentational. */
120
284
  interface PrefPair {
121
285
  /** The other leaves on the row, by the full path their values are read and
122
286
  * written at (`'pose.y'`, `'camera.type'`), in order. Each must follow the
@@ -136,6 +300,11 @@ interface PrefBase<K extends string, Value> {
136
300
  default: Value;
137
301
  /** Hide from a host app's settings UI by default. */
138
302
  hidden?: boolean;
303
+ /** Drawn only while this holds. Presentational: the value stays stored
304
+ * and readable while the leaf is not drawn. */
305
+ showIf?: PrefCondition;
306
+ /** Drawn, and disabled while this does not hold. */
307
+ enableIf?: PrefCondition;
139
308
  /** Nothing held where this leaf's value is read means `default`, because
140
309
  * whoever reads the value takes it that way (`pose.rotation ?? 0`). A
141
310
  * surface over sparse settings reads every leaf so already. One over a
@@ -147,6 +316,11 @@ interface PrefBase<K extends string, Value> {
147
316
  * (weasel-ui `PrefsForm` honors this for leaves whose control brings
148
317
  * its own chrome). */
149
318
  block?: boolean;
319
+ /** Draw the control in the heading of the group or section holding the
320
+ * leaf, in place of a row of its own. Only a built-in `boolean` or `enum`
321
+ * fits there (`prefFitsHeading`); any other leaf stays a row. Honored by
322
+ * weasel-ui `PrefsForm` and labkit's `ControlPanel`. */
323
+ heading?: boolean;
150
324
  /** Glyph naming this leaf in a host UI's icon set (weasel-ui resolves it
151
325
  * against `ICON_PATHS`). A plain string because this package ships no
152
326
  * icon set and cannot depend on one. Read where a leaf's `name` has nowhere to go — a
@@ -173,6 +347,19 @@ interface PrefBase<K extends string, Value> {
173
347
  * schema editor shows such a leaf as one closed thing and prints the name
174
348
  * in place of its literal. */
175
349
  type?: string;
350
+ /** The `enum` leaves this one's value may be split by, each by the full
351
+ * path its value is read at: the value may then be held once per option
352
+ * of any of them (a {@link PrefLocus}), which the store folds into the one
353
+ * value read. Whether it is split is the stored value's to say. The
354
+ * order is the order a value split by several is stored in, the first
355
+ * outermost. Each must be a plain `enum`: not `clearable`, with no
356
+ * `encoding`, and not split itself. */
357
+ splitBy?: readonly string[];
358
+ /** What the leaf holds while nothing is stored for it, where that is a
359
+ * split value: a default that differs by option. Split only by entries of
360
+ * `splitBy`. `default` stays what a member it leaves out reads as, and
361
+ * what the leaf's value type is read from. */
362
+ defaultLocus?: PrefLocus;
176
363
  }
177
364
  /** How a schema-driven UI should present a number pref. */
178
365
  type PrefNumberControl = 'input' | 'slider';
@@ -182,32 +369,6 @@ type PrefBooleanControl = 'checkbox' | 'switch' | 'toggle';
182
369
  type PrefStringControl = 'input' | 'textarea';
183
370
  /** How a schema-driven UI should present an enum pref. */
184
371
  type PrefEnumControl = 'select' | 'radio' | 'toggle' | 'menu';
185
- /** Display-unit conversion for number leaves whose stored value uses a
186
- * canonical unit the user shouldn't see (e.g. radians stored, degrees
187
- * shown). The stored value stays canonical; UIs convert at the edge. */
188
- interface PrefNumberUnit {
189
- toDisplay: (stored: number) => number;
190
- fromDisplay: (display: number) => number;
191
- /** Shown after the input, e.g. `'°'`. */
192
- suffix?: string;
193
- /** Suffixes a person may type, each mapped to the scale that turns a
194
- * number in that unit into a display number: `{ mm: 0.1, cm: 1 }` for a
195
- * field showing centimeters. A unit that disagrees with the display unit
196
- * about zero carries an `offset` as well. */
197
- accepts?: Readonly<Record<string, UnitEntry>>;
198
- /** The stored value as display text, suffix included. */
199
- format?: (stored: number) => string;
200
- }
201
- /**
202
- * A display unit built from a {@link UnitSystem}: values are stored in the
203
- * system's base and shown in `display`, rounded to `precision` places when
204
- * given, and every unit in the system is accepted as typed text. `suffix`
205
- * defaults to the unit's name and is accepted too.
206
- */
207
- declare function prefUnit(system: UnitSystem, display: Unit, opts?: {
208
- precision?: number;
209
- suffix?: string;
210
- }): PrefNumberUnit;
211
372
  /** A numeric pref, optionally bounded and stepped, and optionally stored in a
212
373
  * different unit from the one shown. */
213
374
  interface PrefNumber extends PrefBase<'number', number> {
@@ -251,8 +412,7 @@ interface PrefString extends PrefBase<'string', string> {
251
412
  *
252
413
  * A dash array is the case that needs it: `Stroke.dash` stores lengths, and
253
414
  * the thing a person chooses is a style. The presets scale by the stroke's
254
- * width, so both directions are given the object's other fields — a style is
255
- * meaningless without the width it is a multiple of.
415
+ * width, so both directions are given the object's other fields.
256
416
  */
257
417
  interface PrefEnumEncoding<T extends string = string> {
258
418
  /**
@@ -315,13 +475,12 @@ interface PrefColor extends PrefBase<'color', string> {
315
475
  }
316
476
  /**
317
477
  * Open leaf: any node with a `kind` outside the built-ins. Schema-driven
318
- * UIs (weasel-ui `PrefsForm` / `SelectionPanel`) dispatch it to an
319
- * app-supplied renderer. Deliberately NOT index-signatured so concrete
320
- * app interfaces stay assignable. Mirrors weasel-ui's `PrefCustom`.
478
+ * UIs dispatch it to an app-supplied renderer. Deliberately NOT
479
+ * index-signatured so concrete app interfaces stay assignable.
321
480
  */
322
481
  type PrefCustom = PrefBase<string, unknown>;
323
482
  /**
324
- * A whole `FillStyle`, not a color inside one. Use it wherever the value is
483
+ * A whole `Paint`, not a color inside one. Use it wherever the value is
325
484
  * the tagged paint union — a solid color, a pattern, a gradient — rather
326
485
  * than a hex string.
327
486
  *
@@ -336,21 +495,16 @@ interface PrefPaint extends PrefBase<'paint', unknown> {
336
495
  alpha?: boolean;
337
496
  }
338
497
  /**
339
- * A leaf whose value is one object, with its own fields hanging off it.
340
- *
341
- * A compound value — a stroke, a shadow, a pattern spec — could be described
342
- * as several sibling leaves addressing into it (`data.stroke.width`,
343
- * `data.stroke.cap`). It shouldn't be: each control would then write one field
344
- * of a value it can only half see, and writing into something that is not an
345
- * object yet corrupts it. Here the fields are `children` of one leaf, and
346
- * every edit commits the parent object whole.
498
+ * A leaf whose value is one object, with its own fields hanging off it as
499
+ * `children`; every edit commits the parent object whole. Sibling leaves
500
+ * addressing into the value (`data.stroke.width`, `data.stroke.cap`) would
501
+ * each write one field of a value they can only half see, and writing into
502
+ * something that is not an object yet corrupts it.
347
503
  *
348
504
  * `children` paths are relative to the object. They are ordinary leaves, so a
349
505
  * field that is itself a union (a stroke's `paint`) declares the kind that
350
506
  * edits that union. A child may also be a {@link PrefSection}, which
351
- * organizes the fields under a heading without contributing to the path. A
352
- * `TextStyle` needs it: its character and paragraph fields belong to one
353
- * value but read as two lists.
507
+ * organizes the fields under a heading without contributing to the path.
354
508
  */
355
509
  interface PrefObject extends PrefBase<'object', unknown> {
356
510
  children: Record<string, PrefLeaf | PrefSection>;
@@ -377,8 +531,7 @@ type BuiltinPref = PrefNumber | PrefBoolean | PrefString | PrefEnum | PrefColor
377
531
  /**
378
532
  * The built-in kinds, as a table. A kind added to {@link PrefKind} is a
379
533
  * compile error here, and from here it is one in every renderer's `never`
380
- * guard — the only thing standing between a new kind and rendering as
381
- * nothing in four places at once.
534
+ * guard.
382
535
  */
383
536
  declare const PREF_KINDS: Record<PrefKind, true>;
384
537
  /** Which row each pairing among `leaves` puts a path on: a `key` the row's
@@ -408,6 +561,13 @@ type PrefLeaf = BuiltinPref | PrefCustom;
408
561
  * together: one key for their values to nest under, one node to move.
409
562
  */
410
563
  type PrefGroupAs = 'page' | 'tab' | 'panel' | 'section' | 'fragment' | 'bar';
564
+ /**
565
+ * What a group or a section drawn `as: 'bar'` does with cells its line has no
566
+ * room for: `scroll` keeps them all on it, and `menu` opens the ones past its
567
+ * end from a button there. A bar set to `menu` takes all the room its host
568
+ * has along the line, which is why it is asked for and not the default.
569
+ */
570
+ type PrefBarOverflow = 'scroll' | 'menu';
411
571
  /** How a {@link PrefSection} is drawn. */
412
572
  type PrefSectionAs = Exclude<PrefGroupAs, 'page'>;
413
573
  /**
@@ -427,6 +587,14 @@ interface PrefGroup {
427
587
  /** How the group is drawn. Unset, its depth decides: a top-level group is a
428
588
  * `page`, and one inside another is a `section`. */
429
589
  as?: PrefGroupAs;
590
+ /** Drawn `as: 'bar'`: what happens to the cells its line has no room for.
591
+ * Unset: `scroll`. A surface that takes drops keeps every cell on the line
592
+ * whatever this says, so each is there to drop beside. */
593
+ overflow?: PrefBarOverflow;
594
+ /** Drawn, with everything under it, only while this holds. */
595
+ showIf?: PrefCondition;
596
+ /** Everything under it is disabled while this does not hold. */
597
+ enableIf?: PrefCondition;
430
598
  children: Record<string, PrefLeaf | PrefGroup>;
431
599
  }
432
600
  /**
@@ -445,11 +613,26 @@ interface PrefSection {
445
613
  * `page`, since nothing that draws sections has a rail. Unset: a heading
446
614
  * over its rows. */
447
615
  as?: PrefSectionAs;
616
+ /** As a {@link PrefGroup}'s. */
617
+ overflow?: PrefBarOverflow;
618
+ /** As a {@link PrefGroup}'s. */
619
+ showIf?: PrefCondition;
620
+ /** As a {@link PrefGroup}'s. */
621
+ enableIf?: PrefCondition;
448
622
  members: Record<string, PrefLeaf | PrefSection>;
449
623
  }
450
624
  /** Whether a top-level group gets a page of its own, and not a place on the page of the root's own leaves. */
451
625
  declare function prefGroupIsPage(group: PrefGroup): boolean;
452
626
 
627
+ /**
628
+ * Folds a split value's alternatives into the one value read. `members` holds
629
+ * two or more, each weighing more than nothing, and their weights need not
630
+ * sum to 1.
631
+ */
632
+ type PrefBlend = (members: readonly PrefWeighted[], leaf: PrefLeaf) => unknown;
633
+ /** The blends of the built-in kinds that have one. Every other kind takes its heaviest member. */
634
+ declare const PREF_BLENDS: Readonly<Record<string, PrefBlend>>;
635
+
453
636
  type Join<A extends string, B extends string> = A extends '' ? B : `${A}.${B}`;
454
637
  /** Every value's dotted path in a `PrefGroup` schema, where a group's key is a
455
638
  * segment; an `object` leaf is one path, its fields are not, and an `action`,
@@ -466,10 +649,12 @@ type PrefPath<G, Prefix extends string = ''> = G extends {
466
649
  children: Record<string, unknown>;
467
650
  } ? PrefPath<C[K], Join<Prefix, K>> : never;
468
651
  }[keyof C & string] : never;
469
- /** The leaf at `P`. */
652
+ /** The leaf at `P`. `unknown` under a group whose keys are not literal, as a
653
+ * schema built at run time has them: nothing says which leaf is there, so
654
+ * its value reads as `unknown`. */
470
655
  type PrefAtPath<G, P extends string> = G extends {
471
656
  children: infer C;
472
- } ? P extends `${infer H}.${infer Rest}` ? H extends keyof C ? C[H] extends {
657
+ } ? string extends keyof C ? unknown : P extends `${infer H}.${infer Rest}` ? H extends keyof C ? C[H] extends {
473
658
  kind: string;
474
659
  } ? never : PrefAtPath<C[H], Rest> : never : P extends keyof C ? C[P] extends {
475
660
  kind: string;
@@ -528,6 +713,18 @@ type PrefValueOf<L> = L extends {
528
713
  /** What the leaf at `P` holds. */
529
714
  type PrefValueAt<G, P extends string> = PrefValueOf<PrefAtPath<G, P>>;
530
715
 
716
+ /** Takes stored values from one version to the next: rename, transform or
717
+ * delete entries in `records`, keyed by leaf path. It sees the values, not
718
+ * the stamps they are stored under, and only those at its own version. */
719
+ type PrefsMigration = (records: Map<string, unknown>) => void;
720
+
721
+ /** Decides what a stored value reads as for one kind of leaf: the value to
722
+ * read, or `undefined` for "invalid, read the default". */
723
+ type PrefValidator = (stored: unknown, leaf: PrefLeaf) => unknown;
724
+ /** What `stored` reads as for `leaf`. Never writes anything back: a schema
725
+ * rolled back finds the original still in storage. */
726
+ declare function repairPrefValue(leaf: PrefLeaf, stored: unknown, validators?: Readonly<Record<string, PrefValidator>>): unknown;
727
+
531
728
  /** One leaf changing, as a store subscriber hears it: the value readers now
532
729
  * see, and whether this store or another writer made the change. */
533
730
  interface PrefChange {
@@ -538,26 +735,59 @@ interface PrefChange {
538
735
  /** Values for one schema, held in memory and persisted behind. */
539
736
  interface PrefsStore<S extends PrefGroup> {
540
737
  readonly schema: S;
541
- /** The leaf's value, repaired against the schema; its default when unset. */
542
- get<P extends PrefPath<S>>(path: P): PrefValueAt<S, P>;
543
- set<P extends PrefPath<S>>(path: P, value: PrefValueAt<S, P>): void;
738
+ /**
739
+ * The leaf's value, repaired against the schema; its default when unset. A
740
+ * split value is folded: under the selectors' own values it is the member
741
+ * they name, and under `weights` it is the blend its kind makes of the
742
+ * members that weigh something. A selector `weights` leaves out keeps its
743
+ * own value.
744
+ */
745
+ get<P extends PrefPath<S>>(path: P, read?: {
746
+ weights?: PrefWeights;
747
+ }): PrefValueAt<S, P>;
748
+ /** On a split leaf, writes the member the selectors' own values name, or
749
+ * with `at` the one named with each selector there set to the option given.
750
+ * A {@link PrefLocus} given as the value replaces the whole of what is held. */
751
+ set<P extends PrefPath<S>>(path: P, value: PrefValueAt<S, P> | PrefLocus, write?: {
752
+ at?: PrefSelection;
753
+ }): void;
754
+ /** Hold the leaf's value once per option of the selector at `by`, one of
755
+ * the leaf's `splitBy`, each option starting as the value it had. */
756
+ split(path: PrefPath<S>, by: string): void;
757
+ /** Stop splitting the leaf's value by the selector at `by`, keeping the
758
+ * member that selector's own value names. */
759
+ join(path: PrefPath<S>, by: string): void;
760
+ /** What the leaf holds where its value is split, each member repaired. */
761
+ locus(path: PrefPath<S>): PrefLocus | undefined;
762
+ /** What every split leaf holds, by its path. The same map until something changes. */
763
+ loci(): ReadonlyMap<string, PrefLocus>;
544
764
  /** Unset every leaf at or under `path`; with no path, all. */
545
765
  reset(path?: string): void;
546
766
  /** Whether the leaf has a value of its own rather than following its default. */
547
767
  isSet(path: PrefPath<S>): boolean;
548
- /** Every leaf's value as a nested tree. The same object until something changes. */
549
- values(): Record<string, unknown>;
768
+ /** Every leaf's value as a nested tree. The same object until something
769
+ * changes. With `at`, the tree as it would read with each selector named
770
+ * there set to the option given, built for the call. */
771
+ values(read?: {
772
+ at?: PrefSelection;
773
+ }): Record<string, unknown>;
550
774
  /** The leaves following their default. The same set until something changes. */
551
775
  unset(): ReadonlySet<string>;
552
776
  /** Every stored record as a nested tree, unrepaired, including records no
553
777
  * leaf describes. The same object until something changes. */
554
778
  stored(): Record<string, unknown>;
555
- /** `changes` is empty when only records no leaf describes changed. */
779
+ /** `changes` is empty when only records no leaf describes changed. A
780
+ * selector changing is a change to every leaf split by it. */
556
781
  subscribe(listener: (changes: PrefChange[]) => void): () => void;
557
782
  /** False while the store cannot persist: storage has not been read yet, or
558
783
  * was written by a newer schema. Changes still apply for this session, and
559
- * ones made before a late read lands are persisted when it does. */
784
+ * ones made before a late read lands are persisted when it does. False for
785
+ * good once the store is closed. */
560
786
  readonly writable: boolean;
787
+ /** True once `close` was called. A write after that changes what this store
788
+ * reads and reaches neither storage nor its subscribers, and the first one
789
+ * warns. */
790
+ readonly closed: boolean;
561
791
  /** Try again now to read storage that could not be read at open, ahead of
562
792
  * the store's own retries. Resolves whether storage has been read. */
563
793
  read(): Promise<boolean>;
@@ -569,4 +799,28 @@ interface PrefsStore<S extends PrefGroup> {
569
799
  close(): Promise<void>;
570
800
  }
571
801
 
572
- export { type PrefList as A, type BuiltinPref as B, type PrefMap as C, type PrefNumberControl as D, type PrefNumberUnit as E, type PrefPaint as F, type PrefPair as G, type PrefPath as H, type PrefSectionAs as I, type PrefSeparator as J, type PrefString as K, type PrefStringControl as L, type PrefUnion as M, type PrefValueAt as N, type PrefValueOf as O, type PrefLeaf as P, isBuiltinPref as Q, pairRowsOf as R, prefAliasTarget as S, prefAliasedLeaf as T, prefGroupIsPage as U, prefUnit as V, prefVariantDefault as W, prefVariantOf as X, type PrefGroup as a, type PrefSection as b, type PrefNumber as c, type PrefObject as d, type PrefsStore as e, type PrefKind as f, PREF_KINDS as g, type PrefAction as h, type PrefActionContext as i, type PrefActionControl as j, type PrefAlias as k, type PrefAtPath as l, type PrefBase as m, type PrefBoolean as n, type PrefBooleanControl as o, type PrefBooleanEncoding as p, type PrefChange as q, type PrefColor as r, type PrefCustom as s, type PrefEnum as t, type PrefEnumBase as u, type PrefEnumControl as v, type PrefEnumEncoding as w, type PrefField as x, type PrefGroupAs as y, type PrefLabel as z };
802
+ /** Options for `openPrefs` and `openPrefsSync`. */
803
+ interface PrefsOptions {
804
+ storage: StorageAdapter;
805
+ /** Prepended to every record's key: `'myapp.prefs.'`. */
806
+ prefix: string;
807
+ /** `migrations[i]` takes stored records from version `i` to `i + 1`. The
808
+ * current version is `migrations.length`. */
809
+ migrations?: readonly PrefsMigration[];
810
+ /** Validators for app-defined kinds, keyed by `kind`. One given for a
811
+ * built-in kind replaces that kind's own rule. */
812
+ validators?: Readonly<Record<string, PrefValidator>>;
813
+ /** How a split value's alternatives fold into one for app-defined kinds,
814
+ * keyed by `kind`. One given for a built-in kind replaces that kind's
815
+ * own. A kind with none takes its heaviest alternative. */
816
+ blends?: Readonly<Record<string, PrefBlend>>;
817
+ }
818
+ /** Load every stored value for `schema`, migrate it, and return the store. */
819
+ declare function openPrefs<S extends PrefGroup>(schema: S, options: PrefsOptions): Promise<PrefsStore<S>>;
820
+ /** `openPrefs` for an adapter that reads synchronously: the store is ready
821
+ * on return, so values can be read before anything renders. */
822
+ declare function openPrefsSync<S extends PrefGroup>(schema: S, options: PrefsOptions & {
823
+ storage: SyncStorageAdapter;
824
+ }): PrefsStore<S>;
825
+
826
+ export { type PrefWeighted as $, type PrefBooleanEncoding as A, type BuiltinPref as B, type PrefChange as C, type PrefConditionValue as D, type PrefCustom as E, type PrefEnum as F, type PrefEnumControl as G, type PrefEnumEncoding as H, type PrefField as I, type PrefFunctionUnit as J, type PrefGroupAs as K, type PrefLabel as L, type PrefLinearUnit as M, type PrefNumberControl as N, type PrefNumberUnit as O, type PrefLeaf as P, type PrefPaint as Q, type PrefPair as R, type PrefPath as S, type PrefSectionAs as T, type PrefSeparator as U, type PrefStringControl as V, type PrefTest as W, type PrefUnion as X, type PrefValidator as Y, type PrefValueAt as Z, type PrefValueOf as _, type PrefGroup as a, type PrefWeights as a0, type PrefsMigration as a1, type PrefsOptions as a2, type PrefsStore as a3, type ResolvedPrefUnit as a4, isBuiltinPref as a5, isPrefCondition as a6, isPrefLinearUnit as a7, isPrefLocus as a8, mapPrefConditionPaths as a9, openPrefs as aa, openPrefsSync as ab, pairRowsOf as ac, prefAliasName as ad, prefAliasTarget as ae, prefAliasedLeaf as af, prefConditionHolds as ag, prefConditionPaths as ah, prefConditionReadsItem as ai, prefGroupIsPage as aj, prefIsEnabled as ak, prefIsShown as al, memberAt as am, locusSelectors as an, prefUnit as ao, prefVariantDefault as ap, prefVariantOf as aq, repairPrefValue as ar, resolvePrefUnit as as, type PrefSection as b, type PrefNumber as c, type PrefList as d, type PrefMap as e, type PrefCondition as f, type PrefConditionReader as g, type PrefBoolean as h, type PrefString as i, type PrefColor as j, type PrefEnumBase as k, type PrefSelection as l, type PrefLocus as m, type PrefAlias as n, type PrefObject as o, type PrefKind as p, PREF_BLENDS as q, PREF_KINDS as r, type PrefAction as s, type PrefActionContext as t, type PrefActionControl as u, type PrefAtPath as v, type PrefBarOverflow as w, type PrefBase as x, type PrefBlend as y, type PrefBooleanControl as z };
package/dist/react.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { a as PrefGroup, H as PrefPath, e as PrefsStore, N as PrefValueAt } from './store-CNFnA_3c.js';
1
+ import { a as PrefGroup, S as PrefPath, a3 as PrefsStore, Z as PrefValueAt, m as PrefLocus, l as PrefSelection, a2 as PrefsOptions } from './open-C83pU7Va.js';
2
+ import { SyncStorageAdapter } from '@weasel-js/storage';
2
3
  import '@weasel-js/quantity';
3
4
 
4
5
  /** One leaf of `store`, as React state. */
@@ -7,14 +8,42 @@ declare function usePref<S extends PrefGroup, P extends PrefPath<S>>(store: Pref
7
8
  (next: PrefValueAt<S, P> | ((prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)) => void
8
9
  ];
9
10
  /** The whole of `store` as React state, shaped for `PrefsForm`: `values`,
10
- * `onChange={set}`, `auto={unset}`, and `reset` for `onAutoChange`. `stored`
11
- * is `store.stored()`. */
11
+ * `onChange={set}`, `auto={unset}`, and `reset` for `onAutoChange`; for its
12
+ * split values, `loci`, `onSplit={split}`, `onJoin={join}`, and
13
+ * `onChangeAt={setAt}`. `stored` is `store.stored()`. */
12
14
  declare function usePrefsValues<S extends PrefGroup>(store: PrefsStore<S>): {
13
15
  values: Record<string, unknown>;
14
16
  set: (path: string, value: unknown) => void;
15
17
  unset: ReadonlySet<string>;
16
18
  stored: Record<string, unknown>;
17
19
  reset: (path?: string) => void;
20
+ loci: ReadonlyMap<string, PrefLocus>;
21
+ split: (path: string, by: string) => void;
22
+ join: (path: string, by: string) => void;
23
+ setAt: (path: string, value: unknown, at: PrefSelection) => void;
18
24
  };
19
25
 
20
- export { usePref, usePrefsValues };
26
+ type SyncOptions = PrefsOptions & {
27
+ storage: SyncStorageAdapter;
28
+ };
29
+ /**
30
+ * A store for `schema`, open while the component is mounted: opened during
31
+ * the first render, so that render has values, and closed, with its queued
32
+ * writes sent, when the component unmounts.
33
+ *
34
+ * Components naming the same `storage`, `prefix`, and `schema` share one
35
+ * store, which closes when the last of them unmounts. Components naming the
36
+ * same `storage` and `prefix` with different schemas each get a store of
37
+ * their own over the same records, so a write through one is read by the
38
+ * others at once, and each hears it as a `local` change. They must pass the
39
+ * same `migrations`: the records are migrated once, when the first opens.
40
+ *
41
+ * `storage` and `schema` are compared by identity and `prefix` by value, so
42
+ * keep the first two out of the render body. A change to any of the three
43
+ * closes the store and returns the one for the new three. The `options`
44
+ * object itself may be new every render: `migrations` and `validators` are
45
+ * read when a store opens, and a change to either alone opens nothing.
46
+ */
47
+ declare function usePrefsStore<S extends PrefGroup>(schema: S, options: SyncOptions): PrefsStore<S>;
48
+
49
+ export { usePref, usePrefsStore, usePrefsValues };