tweakit 0.5.2 → 0.7.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/AGENTS.md +32 -30
- package/README.md +55 -29
- package/THIRD-PARTY-NOTICES.md +0 -1
- package/dist/tweaks/auto.js +2 -0
- package/dist/tweaks/auto.js.map +6 -0
- package/dist/tweaks/bezier-S2BOXQBM.js +2 -0
- package/dist/tweaks/bezier-S2BOXQBM.js.map +6 -0
- package/dist/tweaks/chunk-3MZAAMC2.js +2 -0
- package/dist/tweaks/chunk-3MZAAMC2.js.map +6 -0
- package/dist/tweaks/chunk-ETB35KFV.js +2 -0
- package/dist/tweaks/chunk-ETB35KFV.js.map +6 -0
- package/dist/tweaks/chunk-YB5GKWZI.js +2 -0
- package/dist/tweaks/chunk-YB5GKWZI.js.map +6 -0
- package/dist/tweaks/clip-A3VI53BK.js +2 -0
- package/dist/tweaks/clip-A3VI53BK.js.map +6 -0
- package/dist/tweaks/color-UGYMAF3W.js +2 -0
- package/dist/tweaks/color-UGYMAF3W.js.map +6 -0
- package/dist/tweaks/core.js +2 -1
- package/dist/tweaks/core.js.map +6 -0
- package/dist/tweaks/fold-GY47HIQA.js +2 -0
- package/dist/tweaks/fold-GY47HIQA.js.map +6 -0
- package/dist/tweaks/gradient-BTX25WSH.js +2 -0
- package/dist/tweaks/gradient-BTX25WSH.js.map +6 -0
- package/dist/tweaks/heavy-NPFIGHRM.js +2 -0
- package/dist/tweaks/heavy-NPFIGHRM.js.map +6 -0
- package/dist/tweaks/image-HTQQ4WX6.js +4 -0
- package/dist/tweaks/image-HTQQ4WX6.js.map +6 -0
- package/dist/tweaks/image-store-QD5SMZXG.js +2 -0
- package/dist/tweaks/image-store-QD5SMZXG.js.map +6 -0
- package/dist/tweaks/interval-BHPI4LRN.js +2 -0
- package/dist/tweaks/interval-BHPI4LRN.js.map +6 -0
- package/dist/tweaks/markup-QM53TQJZ.js +2 -0
- package/dist/tweaks/markup-QM53TQJZ.js.map +6 -0
- package/dist/tweaks/monitor-722WIYGW.js +3 -0
- package/dist/tweaks/monitor-722WIYGW.js.map +6 -0
- package/dist/tweaks/motion-HMCZK6HJ.js +2 -0
- package/dist/tweaks/motion-HMCZK6HJ.js.map +6 -0
- package/dist/tweaks/plot-D3NQ6XNN.js +3 -0
- package/dist/tweaks/plot-D3NQ6XNN.js.map +6 -0
- package/dist/tweaks/point-XDXKMDXH.js +2 -0
- package/dist/tweaks/point-XDXKMDXH.js.map +6 -0
- package/dist/tweaks/presets-EUWNSCUW.js +2 -0
- package/dist/tweaks/presets-EUWNSCUW.js.map +6 -0
- package/dist/tweaks/shadow-AM7VSRNS.js +2 -0
- package/dist/tweaks/shadow-AM7VSRNS.js.map +6 -0
- package/dist/tweaks/shadow-root-RYUV3AGS.js +3 -0
- package/dist/tweaks/shadow-root-RYUV3AGS.js.map +6 -0
- package/dist/tweaks/sides-JWIURDA4.js +2 -0
- package/dist/tweaks/sides-JWIURDA4.js.map +6 -0
- package/dist/tweaks/spring-BF5GBTPM.js +2 -0
- package/dist/tweaks/spring-BF5GBTPM.js.map +6 -0
- package/dist/tweaks/tabs-PRVUUZXZ.js +2 -0
- package/dist/tweaks/tabs-PRVUUZXZ.js.map +6 -0
- package/dist/tweaks.css +1 -1
- package/dist/tweaks.js +7 -3
- package/dist/tweaks.js.map +6 -0
- package/dist/types/tweaks/easing.d.ts +8 -11
- package/dist/types/tweaks/enhance.d.ts +3 -2
- package/dist/types/tweaks/panel.d.ts +6 -4
- package/dist/types/tweaks/schema.d.ts +4 -8
- package/dist/types/tweaks/standalone.d.ts +3 -3
- package/dist/types/tweaks/types.d.ts +499 -116
- package/package.json +30 -5
- package/src/tweaks/auto.ts +16 -0
- package/src/tweaks/clip.ts +156 -0
- package/src/tweaks/controls/basic.ts +727 -0
- package/src/tweaks/controls/bezier.ts +204 -0
- package/src/tweaks/controls/color.ts +861 -0
- package/src/tweaks/controls/gradient.ts +419 -0
- package/src/tweaks/controls/image.ts +555 -0
- package/src/tweaks/controls/interval.ts +189 -0
- package/src/tweaks/controls/monitor.ts +211 -0
- package/src/tweaks/controls/motion.ts +316 -0
- package/src/tweaks/controls/plot.ts +440 -0
- package/src/tweaks/controls/point.ts +208 -0
- package/src/tweaks/controls/shadow.ts +156 -0
- package/src/tweaks/controls/sides.ts +196 -0
- package/src/tweaks/controls/spring.ts +308 -0
- package/src/tweaks/controls/tabs.ts +68 -0
- package/src/tweaks/core.ts +9 -0
- package/src/tweaks/easing.ts +118 -0
- package/src/tweaks/enhance.ts +35 -0
- package/src/tweaks/feedback.ts +218 -0
- package/src/tweaks/fold.ts +33 -0
- package/src/tweaks/heavy.ts +626 -0
- package/src/tweaks/icons.ts +26 -0
- package/src/tweaks/image-store.ts +189 -0
- package/src/tweaks/lazy.ts +63 -0
- package/src/tweaks/markup.ts +317 -0
- package/src/tweaks/panel.ts +1693 -0
- package/src/tweaks/presets.ts +85 -0
- package/src/tweaks/schema.ts +351 -0
- package/src/tweaks/shadow-root.ts +178 -0
- package/src/tweaks/shared.ts +1137 -0
- package/src/tweaks/single.ts +22 -0
- package/src/tweaks/standalone.ts +179 -0
- package/src/tweaks/types.ts +1027 -0
- package/src/tweaks/undo.ts +104 -0
- package/src/tweaks.css +965 -0
- package/src/wide-gamut.ts +658 -0
- package/dist/tweaks/bezier-2RABC6LD.js +0 -1
- package/dist/tweaks/chunk-4ATI34PB.js +0 -1
- package/dist/tweaks/chunk-7OSCMZ2J.js +0 -1
- package/dist/tweaks/chunk-W3JNIJ2F.js +0 -1
- package/dist/tweaks/clip-ECGCECEC.js +0 -1
- package/dist/tweaks/colour-2RE3FQ5E.js +0 -1
- package/dist/tweaks/fold-UGN7TXGX.js +0 -1
- package/dist/tweaks/gradient-DB6XTDHO.js +0 -1
- package/dist/tweaks/heavy-PKOMHB3C.js +0 -1
- package/dist/tweaks/image-7MCLBOZ3.js +0 -1
- package/dist/tweaks/interval-L2E4L3AZ.js +0 -1
- package/dist/tweaks/markup-ZJJ6CCWM.js +0 -1
- package/dist/tweaks/monitor-IEK742PG.js +0 -2
- package/dist/tweaks/motion-N5UNVR55.js +0 -1
- package/dist/tweaks/plot-VVTOY3RA.js +0 -2
- package/dist/tweaks/point-YMMHSLO7.js +0 -1
- package/dist/tweaks/shadow-IKYOV3VN.js +0 -1
- package/dist/tweaks/sides-FH227Z54.js +0 -1
- package/dist/tweaks/spring-3PKAFIH7.js +0 -1
- package/dist/tweaks/tabs-BKK5NIIY.js +0 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
/** An option for a select / radio grid: a bare string, or `{ value, label }
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/** An option for a select / radio grid: a bare string or number, or `{ value, label }`; the param is the value as
|
|
2
|
+
* written. */
|
|
3
|
+
export type Option = string | number | {
|
|
4
|
+
value: string | number;
|
|
4
5
|
label?: string;
|
|
5
6
|
color?: string;
|
|
6
7
|
};
|
|
7
|
-
/** A
|
|
8
|
+
/** A color stop in a gradient. */
|
|
8
9
|
export type GradientStop = {
|
|
9
10
|
color: string;
|
|
10
11
|
pos: number;
|
|
@@ -15,10 +16,11 @@ export type GradientStop = {
|
|
|
15
16
|
export type GradientInterpolation = "srgb" | "hsl" | "hwb" | "oklch" | "oklab" | "lch" | "lab" | "display-p3" | "rec2020";
|
|
16
17
|
/** What a sides control accepts: one number for all four; `[vertical, horizontal]`,
|
|
17
18
|
* `[top, horizontal, bottom]` or `[top, right, bottom, left]` (CSS order); a CSS shorthand
|
|
18
|
-
* string like `"
|
|
19
|
+
* string like `"1rem 2rem"` (its unit becomes the control's when `unit` isn't given; mixed
|
|
20
|
+
* units warn and read as px); or any subset of
|
|
19
21
|
* the four as an object. With `corners: true` the four are the corners from top-left round,
|
|
20
22
|
* the same CSS order, under the same keys, and they floor at 0 unless `min` says otherwise. */
|
|
21
|
-
export type SidesInput = number | number[] | string | {
|
|
23
|
+
export type SidesInput = number | readonly number[] | string | {
|
|
22
24
|
top?: number;
|
|
23
25
|
right?: number;
|
|
24
26
|
bottom?: number;
|
|
@@ -33,9 +35,9 @@ export interface SidesValue {
|
|
|
33
35
|
left: number;
|
|
34
36
|
css: string;
|
|
35
37
|
}
|
|
36
|
-
/** What a shadow control accepts — any subset of its fields (px, `color` any
|
|
38
|
+
/** What a shadow control accepts — any subset of its fields (px, `color` any color the kit
|
|
37
39
|
* parses); or, as `value`, a `box-shadow` string: optional `inset`, two to four px lengths,
|
|
38
|
-
* one
|
|
40
|
+
* one color, one layer. */
|
|
39
41
|
export interface ShadowInput {
|
|
40
42
|
inset?: boolean;
|
|
41
43
|
x?: number;
|
|
@@ -45,7 +47,7 @@ export interface ShadowInput {
|
|
|
45
47
|
color?: string;
|
|
46
48
|
}
|
|
47
49
|
/** A shadow control's value — the fields, plus the `box-shadow` CSS they resolve to (`css`),
|
|
48
|
-
* built from the numbers and the picker's own
|
|
50
|
+
* built from the numbers and the picker's own color notation, so `el.style.boxShadow = css`. */
|
|
49
51
|
export interface ShadowValue {
|
|
50
52
|
inset: boolean;
|
|
51
53
|
x: number;
|
|
@@ -60,7 +62,7 @@ export interface ShadowValue {
|
|
|
60
62
|
* the easing's, in ms. Spring keys select the spring mode unless `mode` says otherwise. */
|
|
61
63
|
export interface MotionInput {
|
|
62
64
|
mode?: "easing" | "spring";
|
|
63
|
-
curve?: [number, number, number, number] | string;
|
|
65
|
+
curve?: readonly [number, number, number, number] | string;
|
|
64
66
|
duration?: number;
|
|
65
67
|
stiffness?: number;
|
|
66
68
|
damping?: number;
|
|
@@ -87,7 +89,7 @@ export type MotionValue = {
|
|
|
87
89
|
duration: number;
|
|
88
90
|
easing: string;
|
|
89
91
|
};
|
|
90
|
-
/** How a gradient blends between
|
|
92
|
+
/** How a gradient blends between neighboring stops: `"linear"` (CSS's own straight blend,
|
|
91
93
|
* the default) or a CSS easing — `ease`, `ease-in`, `ease-out`, `ease-in-out`, or any
|
|
92
94
|
* `cubic-bezier(x1, y1, x2, y2)`. No browser eases a gradient natively, so `gradientCss()`
|
|
93
95
|
* samples the curve into stops; template through it rather than by hand. */
|
|
@@ -109,7 +111,7 @@ export interface ControlOptions {
|
|
|
109
111
|
label?: string;
|
|
110
112
|
/** Show/hide this control based on other values. */
|
|
111
113
|
render?: (get: Get) => boolean;
|
|
112
|
-
/**
|
|
114
|
+
/** Gray-out + lock this control (static, or derived from other values). */
|
|
113
115
|
disabled?: boolean | ((get: Get) => boolean);
|
|
114
116
|
/** A tooltip revealed by an ⓘ marker beside the label. */
|
|
115
117
|
hint?: string;
|
|
@@ -147,12 +149,12 @@ export type SchemaObject = {
|
|
|
147
149
|
value?: boolean;
|
|
148
150
|
} | {
|
|
149
151
|
type: "list";
|
|
150
|
-
options: Option[];
|
|
151
|
-
value?: string;
|
|
152
|
+
options: readonly Option[];
|
|
153
|
+
value?: string | number;
|
|
152
154
|
} | {
|
|
153
155
|
type: "radiogrid" | "segmented";
|
|
154
|
-
options: Option[];
|
|
155
|
-
value?: string;
|
|
156
|
+
options: readonly Option[];
|
|
157
|
+
value?: string | number;
|
|
156
158
|
cols?: number;
|
|
157
159
|
} | {
|
|
158
160
|
type: "color";
|
|
@@ -164,7 +166,7 @@ export type SchemaObject = {
|
|
|
164
166
|
placeholder?: string;
|
|
165
167
|
} | {
|
|
166
168
|
type: "interval";
|
|
167
|
-
value?: [number, number];
|
|
169
|
+
value?: readonly [number, number];
|
|
168
170
|
min?: number;
|
|
169
171
|
max?: number;
|
|
170
172
|
step?: number;
|
|
@@ -186,7 +188,7 @@ export type SchemaObject = {
|
|
|
186
188
|
};
|
|
187
189
|
} | {
|
|
188
190
|
type: "cubicbezier";
|
|
189
|
-
value?: [number, number, number, number];
|
|
191
|
+
value?: readonly [number, number, number, number];
|
|
190
192
|
} | ({
|
|
191
193
|
type: "shadow";
|
|
192
194
|
value?: ShadowInput | string;
|
|
@@ -207,7 +209,7 @@ export type SchemaObject = {
|
|
|
207
209
|
value?: MotionInput;
|
|
208
210
|
} & MotionInput) | {
|
|
209
211
|
type: "point";
|
|
210
|
-
components:
|
|
212
|
+
components: ReadonlyArray<{
|
|
211
213
|
key: string;
|
|
212
214
|
label?: string;
|
|
213
215
|
value?: number;
|
|
@@ -215,12 +217,25 @@ export type SchemaObject = {
|
|
|
215
217
|
max?: number;
|
|
216
218
|
step?: number;
|
|
217
219
|
}>;
|
|
220
|
+
value?: {
|
|
221
|
+
readonly [key: string]: number;
|
|
222
|
+
};
|
|
223
|
+
pad?: boolean;
|
|
224
|
+
invertY?: boolean;
|
|
225
|
+
} | {
|
|
226
|
+
type: "point";
|
|
227
|
+
value: {
|
|
228
|
+
readonly [key: string]: number;
|
|
229
|
+
};
|
|
230
|
+
components?: undefined;
|
|
218
231
|
pad?: boolean;
|
|
219
232
|
invertY?: boolean;
|
|
220
233
|
} | {
|
|
221
234
|
type: "gradient";
|
|
222
|
-
value?: GradientValue |
|
|
223
|
-
stops?:
|
|
235
|
+
value?: GradientValue | ReadonlyArray<GradientStop | readonly [string, number]>;
|
|
236
|
+
stops?: ReadonlyArray<GradientStop | readonly [string, number]>;
|
|
237
|
+
interpolation?: GradientInterpolation;
|
|
238
|
+
easing?: GradientEasing;
|
|
224
239
|
} | {
|
|
225
240
|
type: "plot";
|
|
226
241
|
expr?: string;
|
|
@@ -250,23 +265,30 @@ export type SchemaObject = {
|
|
|
250
265
|
type: "image";
|
|
251
266
|
value?: string;
|
|
252
267
|
/** Supplied images to pick from, as URLs or `{ value, label }`. The control becomes a row naming the chosen
|
|
253
|
-
* image that opens a pane of tiles with the upload below them, and the param is the chosen URL or the upload's
|
|
254
|
-
|
|
255
|
-
|
|
268
|
+
* image that opens a pane of tiles with the upload below them, and the param is the chosen URL or the upload's
|
|
269
|
+
* data URL. */
|
|
270
|
+
options?: ReadonlyArray<string | {
|
|
271
|
+
value: string;
|
|
272
|
+
label?: string;
|
|
273
|
+
}>;
|
|
274
|
+
/** Tiles per row in the gallery, 1 to 6 (by default one per option for two or three, 2 for one or four, then
|
|
275
|
+
* 3). */
|
|
256
276
|
cols?: number;
|
|
257
277
|
/** The largest upload taken, in bytes (10 MB unless set); a bigger file is refused with a note. */
|
|
258
278
|
maxSize?: number;
|
|
259
279
|
/** An upload longer than this on its long side, in px, is drawn down to it before it becomes the param, and one
|
|
260
|
-
* heavier than 1.5 MB is redrawn at its size, so a phone photo can't fill storage (2560 unless set; `false`
|
|
261
|
-
* uploads as they are). A JPEG comes out as a JPEG at
|
|
262
|
-
* `quality`; anything else as a PNG when that is 1.5 MB or less (a screenshot, not a photo), else as a JPEG, or
|
|
263
|
-
* when it has transparency. A GIF, an SVG, a light picture within `fit
|
|
280
|
+
* heavier than 1.5 MB is redrawn at its size, so a phone photo can't fill storage (2560 unless set; `false`
|
|
281
|
+
* takes uploads as they are). A JPEG comes out as a JPEG at
|
|
282
|
+
* `quality`; anything else as a PNG when that is 1.5 MB or less (a screenshot, not a photo), else as a JPEG, or
|
|
283
|
+
* a WebP when it has transparency. A GIF, an SVG, an animated WebP or APNG, a light picture within `fit`, or one
|
|
284
|
+
* the redraw wouldn't make smaller is kept as it is. */
|
|
264
285
|
fit?: number | false;
|
|
265
286
|
/** The JPEG or WebP quality of a shrunk upload, 0 to 1 (0.85 unless set). */
|
|
266
287
|
quality?: number;
|
|
267
288
|
/** `false` leaves the upload button out of a gallery: the options are the whole choice. */
|
|
268
289
|
upload?: boolean;
|
|
269
|
-
/** `false` leaves Clear out of a gallery, and Delete no longer empties it: for a choice that must keep a
|
|
290
|
+
/** `false` leaves Clear out of a gallery, and Delete no longer empties it: for a choice that must keep a
|
|
291
|
+
* value. */
|
|
270
292
|
clear?: boolean;
|
|
271
293
|
} | {
|
|
272
294
|
type: "button";
|
|
@@ -274,7 +296,7 @@ export type SchemaObject = {
|
|
|
274
296
|
label?: string;
|
|
275
297
|
} | {
|
|
276
298
|
type: "buttongroup";
|
|
277
|
-
buttons: Record<string, () => void> |
|
|
299
|
+
buttons: Record<string, () => void> | ReadonlyArray<{
|
|
278
300
|
label: string;
|
|
279
301
|
action: () => void;
|
|
280
302
|
}>;
|
|
@@ -282,63 +304,216 @@ export type SchemaObject = {
|
|
|
282
304
|
type: "separator";
|
|
283
305
|
} | {
|
|
284
306
|
type: "tabs";
|
|
285
|
-
pages:
|
|
307
|
+
pages: {
|
|
308
|
+
readonly [title: string]: Schema;
|
|
309
|
+
};
|
|
286
310
|
};
|
|
287
311
|
/** A schema value — a shorthand, a verbose `{ type }` object, or a nested folder. */
|
|
288
|
-
export type SchemaValue =
|
|
289
|
-
|
|
312
|
+
export type SchemaValue = SchemaValueOf<SchemaNest>;
|
|
313
|
+
type SchemaValueOf<F> = number | boolean | string | readonly [number] | readonly [number, number] | readonly [number, number, number] | readonly [number, number, number, number] | readonly [readonly [number, number], number, number] | readonly [readonly [number, number], number, number, number] | readonly number[] | readonly (number | readonly number[])[] | readonly Option[] | {
|
|
314
|
+
options: readonly Option[];
|
|
315
|
+
value?: string | number;
|
|
316
|
+
} | {
|
|
317
|
+
action: () => void;
|
|
318
|
+
label?: string;
|
|
319
|
+
} | (SchemaObject & ControlOptions) | F | SchemaFolder;
|
|
320
|
+
/** The object you hand to `tweaks(name, schema, opts?)`. No value is a function, so a verbose
|
|
321
|
+
* form's callbacks (`format`, `fn`) take their parameter types from that form alone. */
|
|
290
322
|
export interface Schema {
|
|
291
|
-
[key: string]: SchemaValue;
|
|
323
|
+
readonly [key: string]: SchemaValue;
|
|
324
|
+
}
|
|
325
|
+
type SchemaNest = Schema & {
|
|
326
|
+
readonly type?: SchemaObject["type"] | Exclude<SchemaValue, string>;
|
|
327
|
+
};
|
|
328
|
+
/** What tweaks() and mountControl() infer from: `Schema` with a folder's `type` left open, since a schema held in a
|
|
329
|
+
* variable widens a verbose form's `type` to `string`; their own check then rules on the names it can still see. */
|
|
330
|
+
export interface HeldSchema {
|
|
331
|
+
readonly [key: string]: HeldValue;
|
|
292
332
|
}
|
|
333
|
+
/** A single value as mountControl() infers it; see `HeldSchema`. */
|
|
334
|
+
export type HeldValue = SchemaValueOf<HeldSchema>;
|
|
335
|
+
/** A nested folder that carries its own `render` / `disabled` predicate or `hint`: chrome, not
|
|
336
|
+
* children. Its index admits the predicate, so it gives up a child named `type`: that sets it
|
|
337
|
+
* apart from a verbose form, whose callbacks would otherwise be typed against the predicate too. */
|
|
338
|
+
export interface SchemaFolder {
|
|
339
|
+
readonly type?: undefined;
|
|
340
|
+
readonly render?: (get: Get) => boolean;
|
|
341
|
+
readonly disabled?: boolean | ((get: Get) => boolean);
|
|
342
|
+
readonly hint?: string;
|
|
343
|
+
readonly [key: string]: SchemaValue | ((get: Get) => boolean) | undefined;
|
|
344
|
+
}
|
|
345
|
+
/** A spring control's value: the physics, plus the time pair and mode when tuned by feel. */
|
|
346
|
+
export interface SpringValue {
|
|
347
|
+
stiffness: number;
|
|
348
|
+
damping: number;
|
|
349
|
+
mass: number;
|
|
350
|
+
visualDuration?: number;
|
|
351
|
+
bounce?: number;
|
|
352
|
+
mode?: "time" | "physics";
|
|
353
|
+
}
|
|
354
|
+
type OptionValue<O> = O extends string | number ? O : O extends {
|
|
355
|
+
readonly value: infer V extends string | number;
|
|
356
|
+
} ? V : never;
|
|
357
|
+
type ChoiceValue<V> = (V extends {
|
|
358
|
+
readonly options: readonly (infer O)[];
|
|
359
|
+
} ? OptionValue<O> : never) | (V extends {
|
|
360
|
+
readonly value: infer D extends string | number;
|
|
361
|
+
} ? D : never);
|
|
362
|
+
type Alnum = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
|
363
|
+
type Dashed<S extends string, Acc extends string = "", Run = false> = S extends `${infer C}${infer R}` ? C extends Alnum ? Dashed<R, `${Acc}${C}`, false> : Run extends true ? Dashed<R, Acc, true> : Dashed<R, `${Acc}-`, true> : Acc;
|
|
364
|
+
type TrimDash<S extends string> = S extends `-${infer R}` ? TrimDash<R> : S extends `${infer R}-` ? TrimDash<R> : S;
|
|
365
|
+
/** A tab title's key under `params`: lower-case, each run of other characters a `-`, trimmed
|
|
366
|
+
* (`"Layout & Grid"` → `"layout-grid"`). Two titles that slug alike are told apart at runtime only. */
|
|
367
|
+
export type TabKey<T extends string> = string extends T ? string : TrimDash<Dashed<Lowercase<T>>> extends infer K extends string ? K extends "" ? "tab" : K : never;
|
|
368
|
+
type TabClash<P> = true extends {
|
|
369
|
+
[T in keyof P & string]: TabKey<T> extends {
|
|
370
|
+
[U in Exclude<keyof P & string, T>]: TabKey<U>;
|
|
371
|
+
}[Exclude<keyof P & string, T>] ? true : false;
|
|
372
|
+
}[keyof P & string] ? true : false;
|
|
373
|
+
type Valueless = "button" | "buttongroup" | "separator" | "fpsgraph" | "monitor";
|
|
374
|
+
type Hinge = {
|
|
375
|
+
list: "options" | "value";
|
|
376
|
+
radiogrid: "options" | "value";
|
|
377
|
+
segmented: "options" | "value";
|
|
378
|
+
point: "components" | "value";
|
|
379
|
+
tabs: "pages" | "value";
|
|
380
|
+
};
|
|
381
|
+
type TypedValue<V> = V extends {
|
|
382
|
+
readonly type: "slider" | "number";
|
|
383
|
+
} ? number : V extends {
|
|
384
|
+
readonly type: "checkbox";
|
|
385
|
+
} ? boolean : V extends {
|
|
386
|
+
readonly type: "list" | "radiogrid" | "segmented";
|
|
387
|
+
} ? V extends {
|
|
388
|
+
readonly options: readonly unknown[];
|
|
389
|
+
} ? ChoiceValue<V> : never : V extends {
|
|
390
|
+
readonly type: "color" | "text" | "image" | "plot";
|
|
391
|
+
} ? string : V extends {
|
|
392
|
+
readonly type: "interval";
|
|
393
|
+
} ? [number, number] : V extends {
|
|
394
|
+
readonly type: "cubicbezier";
|
|
395
|
+
} ? [number, number, number, number] : V extends {
|
|
396
|
+
readonly type: "spring";
|
|
397
|
+
} ? SpringValue : V extends {
|
|
398
|
+
readonly type: "motion";
|
|
399
|
+
} ? MotionValue : V extends {
|
|
400
|
+
readonly type: "shadow";
|
|
401
|
+
} ? ShadowValue : V extends {
|
|
402
|
+
readonly type: "sides";
|
|
403
|
+
} ? SidesValue : V extends {
|
|
404
|
+
readonly type: "gradient";
|
|
405
|
+
} ? GradientValue : V extends {
|
|
406
|
+
readonly type: "point";
|
|
407
|
+
} ? V extends {
|
|
408
|
+
readonly components: readonly [];
|
|
409
|
+
} ? never : V extends {
|
|
410
|
+
readonly components: readonly (infer C)[];
|
|
411
|
+
} ? {
|
|
412
|
+
[K in C extends {
|
|
413
|
+
readonly key: infer Key extends string;
|
|
414
|
+
} ? Key : never]: number;
|
|
415
|
+
} : V extends {
|
|
416
|
+
readonly value: infer P extends object;
|
|
417
|
+
} ? keyof P extends never ? never : {
|
|
418
|
+
-readonly [K in keyof P & string]: number;
|
|
419
|
+
} : never : V extends {
|
|
420
|
+
readonly type: "tabs";
|
|
421
|
+
} ? V extends {
|
|
422
|
+
readonly pages: infer P extends object;
|
|
423
|
+
} ? keyof P extends never ? never : TabClash<P> extends true ? Record<string, Params> : {
|
|
424
|
+
[T in keyof P & string as TabKey<T>]: ParamsOf<P[T]>;
|
|
425
|
+
} : never : unknown;
|
|
426
|
+
/** The value a schema entry puts on `params`. */
|
|
427
|
+
export type ValueOf<V> = V extends {
|
|
428
|
+
readonly type: infer T;
|
|
429
|
+
} ? string extends T ? any : T extends SchemaObject["type"] ? T extends keyof Hinge ? [Extract<Hinge[T], keyof V>] extends [never] ? ShorthandValue<V> : VerboseValue<V, T> : VerboseValue<V, T> : T extends string ? [Extract<"value" | "options" | "action", keyof V>] extends [never] ? ShorthandValue<V> : never : ShorthandValue<V> : ShorthandValue<V>;
|
|
430
|
+
type VerboseValue<V, T> = T extends Valueless ? never : TypedValue<V>;
|
|
431
|
+
type ShorthandValue<V> = V extends readonly [readonly [number, number], ...unknown[]] ? [number, number] : V extends readonly [number] | readonly [number, number] | readonly [number, number, number] | readonly [number, number, number, number] ? number : V extends readonly [number, ...number[]] ? V[number] : V extends readonly [] ? undefined : V extends readonly number[] ? number : V extends readonly (number | readonly number[])[] ? [number, number] : V extends number ? number : V extends boolean ? boolean : V extends string ? string : V extends readonly (infer O)[] ? OptionValue<O> : V extends {
|
|
432
|
+
readonly action: (...a: any[]) => any;
|
|
433
|
+
} ? never : V extends {
|
|
434
|
+
readonly options: readonly unknown[];
|
|
435
|
+
} ? ChoiceValue<V> : V extends object ? Entries<V, true> : unknown;
|
|
436
|
+
type Reserved = "__proto__" | "constructor" | "prototype";
|
|
437
|
+
type FolderChrome<V, K> = K extends "disabled" | "hint" ? true : K extends "render" ? V extends (...a: any[]) => any ? true : false : false;
|
|
438
|
+
type Entries<S, Nested extends boolean> = string extends keyof S ? Params : {
|
|
439
|
+
-readonly [K in keyof S & string as K extends Reserved ? never : ValueOf<S[K]> extends never ? never : Nested extends true ? FolderChrome<S[K], K> extends true ? never : K : K]: ValueOf<S[K]>;
|
|
440
|
+
};
|
|
441
|
+
/** The live values a schema builds, nested like the schema; a plain `Schema` gives the loose `Params`. */
|
|
442
|
+
export type ParamsOf<S> = Entries<S, false>;
|
|
443
|
+
type CheckValue<V> = V extends {
|
|
444
|
+
readonly type: infer T;
|
|
445
|
+
} ? string extends T ? V : T extends SchemaObject["type"] ? V extends {
|
|
446
|
+
readonly type: "tabs";
|
|
447
|
+
readonly pages: infer P;
|
|
448
|
+
} ? {
|
|
449
|
+
readonly pages: {
|
|
450
|
+
[K in keyof P]: KnownTypes<P[K]>;
|
|
451
|
+
};
|
|
452
|
+
} : T extends keyof Hinge ? [Extract<Hinge[T], keyof V>] extends [never] ? unknown : FitsForm<V, T> : FitsForm<V, T> : {
|
|
453
|
+
readonly type: NotAControlType;
|
|
454
|
+
} : V extends readonly unknown[] | ((...a: any[]) => any) ? unknown : V extends object ? KnownTypes<V> : unknown;
|
|
455
|
+
type FormOf<O, T> = O extends {
|
|
456
|
+
readonly type: infer N;
|
|
457
|
+
} ? (T extends N ? O & ControlOptions : never) : never;
|
|
458
|
+
type FitsForm<V, T> = V extends FormOf<SchemaObject, T> ? unknown : FormOf<SchemaObject, T>;
|
|
459
|
+
type NotAControlType = {
|
|
460
|
+
readonly "names no control: see the type list in AGENTS.md": true;
|
|
461
|
+
};
|
|
462
|
+
/** A schema whose every `type` names a control (`tweaks()` checks it; `{ type: "slidr" }` is an error). */
|
|
463
|
+
export type KnownTypes<S> = {
|
|
464
|
+
[K in keyof S]: CheckValue<S[K]>;
|
|
465
|
+
};
|
|
466
|
+
/** One schema value whose `type` names a control (`mountControl()` checks it). */
|
|
467
|
+
export type KnownType<V> = CheckValue<V>;
|
|
293
468
|
/** A theme — token overrides applied over the default monochrome look. Every key is
|
|
294
469
|
* optional, so a partial theme only moves what it names; unset tokens keep their default.
|
|
295
|
-
*
|
|
470
|
+
* Keys are the `--twk-*` custom properties themselves. Apply at build via
|
|
296
471
|
* `tweaks(name, schema, { theme })`, or live via `panel.setTheme(theme)`. The kit's whole
|
|
297
472
|
* appearance runs on these custom properties — this is the public theming surface. */
|
|
298
473
|
export interface Theme {
|
|
299
474
|
/** Brand accent — slider fills, focus rings, active highlights (default: neutral, no accent). */
|
|
300
|
-
accent?: string;
|
|
301
|
-
/** Text drawn on the accent (active segment / radio label)
|
|
302
|
-
|
|
475
|
+
"--twk-accent"?: string;
|
|
476
|
+
/** Text drawn on the accent (active segment / radio label); the primary text tone unless set, so set it with a light
|
|
477
|
+
* accent. */
|
|
478
|
+
"--twk-on-accent"?: string;
|
|
303
479
|
/** Panel background, reused for recessed wells. */
|
|
304
|
-
base?: string;
|
|
480
|
+
"--twk-base"?: string;
|
|
305
481
|
/** Popover / dropdown background. */
|
|
306
|
-
|
|
482
|
+
"--twk-dropdown-bg"?: string;
|
|
307
483
|
/** Control surface and its hover / active steps. */
|
|
308
|
-
surface?: string;
|
|
309
|
-
|
|
310
|
-
|
|
484
|
+
"--twk-surface"?: string;
|
|
485
|
+
"--twk-surface-hover"?: string;
|
|
486
|
+
"--twk-surface-active"?: string;
|
|
311
487
|
/** Hairline border and its hover step. */
|
|
312
|
-
border?: string;
|
|
313
|
-
|
|
488
|
+
"--twk-border"?: string;
|
|
489
|
+
"--twk-border-hover"?: string;
|
|
314
490
|
/** Text-selection highlight wash. */
|
|
315
|
-
selection?: string;
|
|
316
|
-
/** Text tones — panel title, section heading, primary value text, control label, and the muted / faint / focus
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
text?: string;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
danger?: string;
|
|
491
|
+
"--twk-selection"?: string;
|
|
492
|
+
/** Text tones — panel title, section heading, primary value text, control label, and the muted / faint / focus
|
|
493
|
+
* tones. */
|
|
494
|
+
"--twk-text-root"?: string;
|
|
495
|
+
"--twk-text-section"?: string;
|
|
496
|
+
"--twk-text-primary"?: string;
|
|
497
|
+
"--twk-text-label"?: string;
|
|
498
|
+
"--twk-text-secondary"?: string;
|
|
499
|
+
"--twk-text-tertiary"?: string;
|
|
500
|
+
"--twk-text-focus"?: string;
|
|
501
|
+
/** Copy-confirmation accent, and the invalid-state accent (the plot's bad-expression cue). */
|
|
502
|
+
"--twk-success"?: string;
|
|
503
|
+
"--twk-danger"?: string;
|
|
328
504
|
/** Popover shadow, the panel's container elevation, and its lifted (floating / dragging) variant. */
|
|
329
|
-
shadow?: string;
|
|
330
|
-
|
|
331
|
-
|
|
505
|
+
"--twk-shadow-dropdown"?: string;
|
|
506
|
+
"--twk-shadow-panel"?: string;
|
|
507
|
+
"--twk-shadow-panel-lifted"?: string;
|
|
332
508
|
/** Font stack, and the monospace stack (the plot's expression field). */
|
|
333
|
-
font?: string;
|
|
334
|
-
|
|
335
|
-
/** Control corner radius
|
|
336
|
-
radius?:
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
[token: string]: string | number | undefined;
|
|
509
|
+
"--twk-font-sans"?: string;
|
|
510
|
+
"--twk-font-mono"?: string;
|
|
511
|
+
/** Control corner radius, row height and the panel's width (256px by default), as CSS lengths. */
|
|
512
|
+
"--twk-radius"?: string;
|
|
513
|
+
"--twk-row-height"?: string;
|
|
514
|
+
"--twk-width"?: string;
|
|
515
|
+
/** Any other token. */
|
|
516
|
+
[token: `--twk-${string}`]: string | undefined;
|
|
342
517
|
}
|
|
343
518
|
/** Options for a programmatic write — `set()`, `setMany()`, `fromJSON()`. */
|
|
344
519
|
export interface SetOptions {
|
|
@@ -347,29 +522,194 @@ export interface SetOptions {
|
|
|
347
522
|
* controls, persistence and undo still follow it: it's an undo step like any other. */
|
|
348
523
|
silent?: boolean;
|
|
349
524
|
}
|
|
350
|
-
/**
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
/** Where a floating panel starts: `x` / `y` are its insets in px (16 by default) from the two
|
|
354
|
-
* edges of `corner` (top-left by default), and it keeps that corner as the window resizes.
|
|
355
|
-
* The far edge keeps a gap of `y` when the height caps; CSS `--tw-float-gap` overrides it. */
|
|
525
|
+
/** Where a floating panel starts: `x` / `y` are its insets in px (16 by default) from the
|
|
526
|
+
* viewport's top-left. The far edge keeps a gap of `y` when the height caps; CSS
|
|
527
|
+
* `--twk-float-gap` overrides it. */
|
|
356
528
|
export interface FloatingPlacement {
|
|
357
529
|
x?: number;
|
|
358
530
|
y?: number;
|
|
359
|
-
corner?: FloatingCorner;
|
|
360
531
|
}
|
|
532
|
+
/** The words a panel shows and says, by key, for the `strings` option (each line notes the English default). A key
|
|
533
|
+
* with a `{placeholder}` takes a string that uses it, or a function given the values, for a language whose plurals
|
|
534
|
+
* need more than one form. Units (px, ms, %), axis letters and color notations stay as they are. */
|
|
535
|
+
export type Word<V = never> = [V] extends [never] ? string : string | ((v: V) => string);
|
|
536
|
+
export interface Strings {
|
|
537
|
+
/** "Tweaks" — the panel's landmark name when its own is blank */ panel?: string;
|
|
538
|
+
/** "Presets" — the toolbar button */ presets?: string;
|
|
539
|
+
/** "Copy values (⇧: changed only)" — the toolbar button */ copy?: string;
|
|
540
|
+
/** "Reset" — the toolbar button */ reset?: string;
|
|
541
|
+
/** "Double-click or hold to reset" — a row label's tooltip */ resetHint?: string;
|
|
542
|
+
/** "Values copied" */ copied?: string;
|
|
543
|
+
/** "{n} changed values copied" — 1 changed value copied for one */ copiedChanged?: Word<{
|
|
544
|
+
n: number;
|
|
545
|
+
}>;
|
|
546
|
+
/** "Nothing has changed" */ nothingChanged?: string;
|
|
547
|
+
/** "Copy failed" */ copyFailed?: string;
|
|
548
|
+
/** "{label} copied" — ⌘C on a row */ rowCopied?: Word<{
|
|
549
|
+
label: string;
|
|
550
|
+
}>;
|
|
551
|
+
/** "Not saved" — a value storage refused */ notSaved?: string;
|
|
552
|
+
/** "{n} values pasted" — 1 value pasted for one */ pasted?: Word<{
|
|
553
|
+
n: number;
|
|
554
|
+
}>;
|
|
555
|
+
/** "Nothing to paste" */ nothingToPaste?: string;
|
|
556
|
+
/** "Couldn't read the clipboard" */ clipboardUnread?: string;
|
|
557
|
+
/** "Preset name…" — the field's placeholder */ presetName?: string;
|
|
558
|
+
/** "New preset name" — the field's label */ newPreset?: string;
|
|
559
|
+
/** "Save" */ save?: string;
|
|
560
|
+
/** "No presets yet" */ noPresets?: string;
|
|
561
|
+
/** "Loaded preset {name}" */ presetLoaded?: Word<{
|
|
562
|
+
name: string;
|
|
563
|
+
}>;
|
|
564
|
+
/** "Saved preset {name}" */ presetSaved?: Word<{
|
|
565
|
+
name: string;
|
|
566
|
+
}>;
|
|
567
|
+
/** "Preset {name} not saved: storage is full" */ presetFull?: Word<{
|
|
568
|
+
name: string;
|
|
569
|
+
}>;
|
|
570
|
+
/** "Delete preset {name}" — a row's delete button */ presetDelete?: Word<{
|
|
571
|
+
name: string;
|
|
572
|
+
}>;
|
|
573
|
+
/** "Off" — a checkbox */ off?: string;
|
|
574
|
+
/** "On" — a checkbox */ on?: string;
|
|
575
|
+
/** "Button" — a group button with no label */ button?: string;
|
|
576
|
+
/** "Tab" — a tab page whose title is blank */ tab?: string;
|
|
577
|
+
/** "Folder" — a markup folder with no data-label */ folder?: string;
|
|
578
|
+
/** "Drag to adjust" — a number field's grip */ dragToAdjust?: string;
|
|
579
|
+
/** "{label} minimum" */ intervalMin?: Word<{
|
|
580
|
+
label: string;
|
|
581
|
+
}>;
|
|
582
|
+
/** "{label} maximum" */ intervalMax?: Word<{
|
|
583
|
+
label: string;
|
|
584
|
+
}>;
|
|
585
|
+
/** "Monitor" — a monitor with no label */ monitor?: string;
|
|
586
|
+
/** "FPS" — an fpsgraph with no label */ fps?: string;
|
|
587
|
+
/** "Plot" — a plot with no label */ plot?: string;
|
|
588
|
+
/** "{label} — expression in x" — a plot's field */ expression?: Word<{
|
|
589
|
+
label: string;
|
|
590
|
+
}>;
|
|
591
|
+
/** "Point" — a point with no label */ point?: string;
|
|
592
|
+
/** "{label} pad: {x} and {y}" */ pointPad?: Word<{
|
|
593
|
+
label: string;
|
|
594
|
+
x: string | number;
|
|
595
|
+
y: string | number;
|
|
596
|
+
}>;
|
|
597
|
+
/** "Control point {n}" — a cubic bézier handle */ controlPoint?: Word<{
|
|
598
|
+
n: number;
|
|
599
|
+
}>;
|
|
600
|
+
/** "Control point {n}, x {x}, y {y}" */ controlPointAt?: Word<{
|
|
601
|
+
n: number;
|
|
602
|
+
x: string | number;
|
|
603
|
+
y: string | number;
|
|
604
|
+
}>;
|
|
605
|
+
/** "Color" — a color row with no label, and the shadow's color row */ color?: string;
|
|
606
|
+
/** "Color plane: chroma across, lightness up" */ colorPlane?: string;
|
|
607
|
+
/** "Color mode" */ colorMode?: string;
|
|
608
|
+
/** "Hex color" */ hex?: string;
|
|
609
|
+
/** "Hue" */ hue?: string;
|
|
610
|
+
/** "Alpha" */ alpha?: string;
|
|
611
|
+
/** "lightness {l}%, chroma {c}" — the color plane's description, said after a run of keys */ colorReadout?: Word<{
|
|
612
|
+
l: number;
|
|
613
|
+
c: number;
|
|
614
|
+
}>;
|
|
615
|
+
/** "wide" — the gamut badge past P3 (sRGB and P3 are names) */ wide?: string;
|
|
616
|
+
/** "Gradient" — a gradient with no label */ gradient?: string;
|
|
617
|
+
/** "{n} stops" — 1 stop for one */ stops?: Word<{
|
|
618
|
+
n: number;
|
|
619
|
+
}>;
|
|
620
|
+
/** "Add a stop after the selected one" — the add button's tooltip */ addStop?: string;
|
|
621
|
+
/** "Add a color stop after the selected one" — the add button's label */ addStopAria?: string;
|
|
622
|
+
/** "Color stop at {pos}%" */ stopAt?: Word<{
|
|
623
|
+
pos: number;
|
|
624
|
+
}>;
|
|
625
|
+
/** "Color stop at {pos}%, selected" */ stopAtSelected?: Word<{
|
|
626
|
+
pos: number;
|
|
627
|
+
}>;
|
|
628
|
+
/** "Easing between stops" — the easing menu's label */ stopEasing?: string;
|
|
629
|
+
/** "How the ramp blends between stops" — its tooltip */ stopEasingHint?: string;
|
|
630
|
+
/** "Linear" */ easeLinear?: string;
|
|
631
|
+
/** "Ease" */ ease?: string;
|
|
632
|
+
/** "Ease in" */ easeIn?: string;
|
|
633
|
+
/** "Ease out" */ easeOut?: string;
|
|
634
|
+
/** "Ease in-out" */ easeInOut?: string;
|
|
635
|
+
/** "Custom" — an easing the menu doesn't list */ custom?: string;
|
|
636
|
+
/** "Upload image" — the gallery's upload button */ upload?: string;
|
|
637
|
+
/** "{label}: upload an image" */ uploadImage?: Word<{
|
|
638
|
+
label: string;
|
|
639
|
+
}>;
|
|
640
|
+
/** "{label}: choose an image" */ chooseImage?: Word<{
|
|
641
|
+
label: string;
|
|
642
|
+
}>;
|
|
643
|
+
/** "Clear" */ clear?: string;
|
|
644
|
+
/** "{label}: clear the image" */ clearImage?: Word<{
|
|
645
|
+
label: string;
|
|
646
|
+
}>;
|
|
647
|
+
/** "Replace" */ replace?: string;
|
|
648
|
+
/** "Drop or choose" */ dropOrChoose?: string;
|
|
649
|
+
/** "None" — no image chosen */ none?: string;
|
|
650
|
+
/** "Image {n}" — a gallery option with no label */ imageN?: Word<{
|
|
651
|
+
n: number;
|
|
652
|
+
}>;
|
|
653
|
+
/** "Uploaded image" */ uploadedImage?: string;
|
|
654
|
+
/** "Custom image" — a value off the gallery's list */ customImage?: string;
|
|
655
|
+
/** "Not an image" */ notAnImage?: string;
|
|
656
|
+
/** "Couldn't read the file" */ unreadable?: string;
|
|
657
|
+
/** "{size}, max {max}" — a file over maxSize */ tooLarge?: Word<{
|
|
658
|
+
size: string;
|
|
659
|
+
max: string;
|
|
660
|
+
}>;
|
|
661
|
+
/** "Motion" — a motion row with no label */ motion?: string;
|
|
662
|
+
/** "Motion type" */ motionType?: string;
|
|
663
|
+
/** "Type" — the motion pane's row label beside that switch */ type?: string;
|
|
664
|
+
/** "Easing" */ easing?: string;
|
|
665
|
+
/** "Spring" */ spring?: string;
|
|
666
|
+
/** "Duration" */ duration?: string;
|
|
667
|
+
/** "Replay the motion" */ replay?: string;
|
|
668
|
+
/** "Easing · {ms} ms" */ easingSummary?: Word<{
|
|
669
|
+
ms: number;
|
|
670
|
+
}>;
|
|
671
|
+
/** "Spring · {ms} ms" */ springSummary?: Word<{
|
|
672
|
+
ms: number;
|
|
673
|
+
}>;
|
|
674
|
+
/** "Spring mode" */ springMode?: string;
|
|
675
|
+
/** "Mode" */ mode?: string;
|
|
676
|
+
/** "Time" */ time?: string;
|
|
677
|
+
/** "Physics" */ physics?: string;
|
|
678
|
+
/** "Stiffness" */ stiffness?: string;
|
|
679
|
+
/** "Damping" */ damping?: string;
|
|
680
|
+
/** "Mass" */ mass?: string;
|
|
681
|
+
/** "Bounce" */ bounce?: string;
|
|
682
|
+
/** "Shadow" — a shadow with no label */ shadow?: string;
|
|
683
|
+
/** "Shadow position" */ shadowPosition?: string;
|
|
684
|
+
/** "in" — an inset shadow's summary, before its numbers */ inset?: string;
|
|
685
|
+
/** "Position" */ position?: string;
|
|
686
|
+
/** "Outer" */ outer?: string;
|
|
687
|
+
/** "Inner" */ inner?: string;
|
|
688
|
+
/** "Blur" */ blur?: string;
|
|
689
|
+
/** "Spread" */ spread?: string;
|
|
690
|
+
/** "Top" */ top?: string;
|
|
691
|
+
/** "Right" */ right?: string;
|
|
692
|
+
/** "Bottom" */ bottom?: string;
|
|
693
|
+
/** "Left" */ left?: string;
|
|
694
|
+
/** "Top-left" */ topLeft?: string;
|
|
695
|
+
/** "Top-right" */ topRight?: string;
|
|
696
|
+
/** "Bottom-right" */ bottomRight?: string;
|
|
697
|
+
/** "Bottom-left" */ bottomLeft?: string;
|
|
698
|
+
/** "Independent sides" */ splitSides?: string;
|
|
699
|
+
/** "Independent corners" */ splitCorners?: string;
|
|
700
|
+
}
|
|
701
|
+
/** Panel options (third arg to `tweaks()`). */
|
|
361
702
|
export interface TweaksOptions {
|
|
362
|
-
/** Theme overrides
|
|
703
|
+
/** Theme overrides as `--twk-*` tokens; `null` / omitted = default monochrome. */
|
|
363
704
|
theme?: Theme | null;
|
|
364
|
-
/** Persist values to localStorage + enable presets — a key, or `true` to key by name.
|
|
705
|
+
/** Persist values to localStorage + enable presets — a key, or `true` to key by name. Uploaded images go to
|
|
706
|
+
* IndexedDB under the same key, and come back by `ready`. */
|
|
365
707
|
persist?: string | boolean;
|
|
366
708
|
/** With `persist`, `false` keeps saving values but leaves the Presets menu out of the
|
|
367
709
|
* toolbar (the presets API still works). On by default. */
|
|
368
710
|
presets?: boolean;
|
|
369
|
-
/** Add a filter/search field to the toolbar. */
|
|
370
|
-
filter?: boolean;
|
|
371
711
|
/** Start the panel already floated (fixed-positioned): `true` → 16px from the top-left, or a
|
|
372
|
-
* placement `{ x, y
|
|
712
|
+
* placement `{ x, y }`. Its height caps at the window and it scrolls, header and all. */
|
|
373
713
|
floating?: boolean | FloatingPlacement;
|
|
374
714
|
/** Drag the header to reposition the panel — on by default. An inline panel lifts into a
|
|
375
715
|
* floating layer on first drag; set `false` to pin it in place. */
|
|
@@ -380,15 +720,32 @@ export interface TweaksOptions {
|
|
|
380
720
|
undo?: boolean;
|
|
381
721
|
/** Custom reset handler (e.g. restore real app defaults + rebuild). */
|
|
382
722
|
onReset?: () => void;
|
|
383
|
-
/**
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
723
|
+
/** Mount the panel in a shadow root, so page CSS can't restyle it (`#app button { … }`, a `.prose` wrapper). `el` is
|
|
724
|
+
* then its host, a `<twk-host>`: theme it with `--twk-*` on the host itself (`.twk-host { … }`), or with `theme`.
|
|
725
|
+
* The styles come from the page's `tweakit/css` when that's loaded, else from `href` (by default `tweaks.css` beside
|
|
726
|
+
* the module). */
|
|
727
|
+
shadow?: boolean | {
|
|
728
|
+
href?: string;
|
|
729
|
+
};
|
|
730
|
+
/** The words the panel and its controls show and say, over the English defaults: translate them, or rename the chrome
|
|
731
|
+
* (`{ presets: "Looks" }`). A key left out keeps its default. */
|
|
732
|
+
strings?: Strings;
|
|
387
733
|
}
|
|
388
|
-
/**
|
|
389
|
-
export
|
|
390
|
-
|
|
391
|
-
}
|
|
734
|
+
/** Options for `enhance()`: the words its markup controls show and say. */
|
|
735
|
+
export interface EnhanceOptions {
|
|
736
|
+
strings?: Strings;
|
|
737
|
+
}
|
|
738
|
+
/** The live values bag, loosely typed — `params[key]` for each control. A schema literal passed
|
|
739
|
+
* to `tweaks()` gets its exact shape instead (`ParamsOf`). */
|
|
740
|
+
export type Params = Record<string, any>;
|
|
741
|
+
/** The keys `set()` and `setMany()` take: a typed panel's controls by dotted path (`size`, `card.size`), so a typo is a
|
|
742
|
+
* compile error; any string for a panel built from a plain `Schema`. An object-valued control's own fields read as
|
|
743
|
+
* paths too (`drop.blur`): the types can't tell its value from a folder, so they allow what the runtime refuses. Past
|
|
744
|
+
* ten folders deep the rest of a path is any string, since spelling every one out overran tsc's depth limit. */
|
|
745
|
+
export type PanelPath<P> = PathsOf<P, []>;
|
|
746
|
+
type PathsOf<P, D extends unknown[]> = string extends keyof P ? string : D["length"] extends 10 ? string : {
|
|
747
|
+
[K in keyof P & string]: K | (P[K] extends readonly unknown[] ? never : P[K] extends object ? `${K}.${PathsOf<P[K], [...D, 0]>}` : never);
|
|
748
|
+
}[keyof P & string];
|
|
392
749
|
/** The whole-panel state `toJSON()` returns / `fromJSON()` accepts: every control value
|
|
393
750
|
* (nested to mirror folders/tabs) plus UI state — which folders are collapsed and which
|
|
394
751
|
* tab is active. JSON-safe; persist it however you like. Both `values` and `ui` are
|
|
@@ -410,25 +767,32 @@ export interface PanelState {
|
|
|
410
767
|
tabs?: Record<string, string | null>;
|
|
411
768
|
};
|
|
412
769
|
}
|
|
413
|
-
/** The panel object `tweaks()` returns.
|
|
414
|
-
|
|
415
|
-
|
|
770
|
+
/** The panel object `tweaks()` returns; `P` is its values' shape. A bare `Panel` takes any panel, typed or not (its
|
|
771
|
+
* default is `any`, not `Params`: `set()`'s typed paths would make a typed panel unassignable to `Panel<Params>`). */
|
|
772
|
+
export interface Panel<P = any> {
|
|
773
|
+
/** The panel root element — append it to the DOM. With `shadow`, the host the panel lives in. */
|
|
416
774
|
el: HTMLElement;
|
|
417
775
|
/** Live values, updated in place as controls change. */
|
|
418
|
-
params:
|
|
419
|
-
/** Resolves once any lazily-loaded controls have hydrated
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
776
|
+
params: P;
|
|
777
|
+
/** Resolves once any lazily-loaded controls have hydrated and, for a panel that persists an image control, its
|
|
778
|
+
* stored images have loaded (resolves immediately when there's neither). */
|
|
779
|
+
ready: Promise<Panel<P>>;
|
|
780
|
+
/** Subscribe to changes; `last` is the changed control's dotted path (`size`, `card.size`), as `set()` and
|
|
781
|
+
* `changes()`
|
|
782
|
+
* name it, or undefined when many changed at once (a restore, `reset()`, `fromJSON`, `setMany`). */
|
|
783
|
+
on(fn: (params: P, last?: string) => void): () => void;
|
|
423
784
|
/** Set a control's value programmatically. Nested controls take a dotted path
|
|
424
|
-
* ("folder.child", "tabs.page.child"); a bare key also reaches a nested control
|
|
425
|
-
* when it's unambiguous
|
|
426
|
-
|
|
785
|
+
* ("folder.child", "tabs.page.child"); at runtime a bare key also reaches a nested control
|
|
786
|
+
* when it's unambiguous, though a typed panel's types take the full path. A key matching
|
|
787
|
+
* no control, or a folder or tab, is refused with a console warning. */
|
|
788
|
+
set(key: PanelPath<P>, value: unknown, options?: SetOptions): void;
|
|
427
789
|
/** Set several controls at once — a flat map of keys (dotted for nested controls), e.g.
|
|
428
790
|
* `setMany({ "shadow.radius": 28, blur: 48 })`. Resolves each key like `set()`, then fires
|
|
429
791
|
* listeners + persists ONCE for the whole batch, where N separate `set()` calls would fire
|
|
430
792
|
* N times. Unknown / ambiguous / reserved keys warn and are skipped. */
|
|
431
|
-
setMany(values:
|
|
793
|
+
setMany(values: {
|
|
794
|
+
readonly [K in PanelPath<P>]?: unknown;
|
|
795
|
+
}, options?: SetOptions): void;
|
|
432
796
|
/** Reset every control to its default (or run `opts.onReset`). */
|
|
433
797
|
reset(): void;
|
|
434
798
|
/** Serialize the whole panel — every control value plus UI state (collapsed folders,
|
|
@@ -454,7 +818,8 @@ export interface Panel {
|
|
|
454
818
|
* with stays its storage key (`persist: true`), so saved values and presets keep their
|
|
455
819
|
* home across a rename. */
|
|
456
820
|
setName(name: string): void;
|
|
457
|
-
/** Save the current values as a named preset (needs `opts.persist`). False when there is nowhere to save it, or
|
|
821
|
+
/** Save the current values as a named preset (needs `opts.persist`). False when there is nowhere to save it, or
|
|
822
|
+
* storage refused it. */
|
|
458
823
|
savePreset(name: string): boolean;
|
|
459
824
|
/** Load a named preset. */
|
|
460
825
|
loadPreset(name: string): boolean;
|
|
@@ -465,7 +830,8 @@ export interface Panel {
|
|
|
465
830
|
/** Undo / redo (need `opts.undo`). */
|
|
466
831
|
undo(): void;
|
|
467
832
|
redo(): void;
|
|
468
|
-
/** Tear the panel down — remove it from the DOM, release every global listener, close any open popover; the API goes
|
|
833
|
+
/** Tear the panel down — remove it from the DOM, release every global listener, close any open popover; the API goes
|
|
834
|
+
* inert. */
|
|
469
835
|
destroy(): void;
|
|
470
836
|
}
|
|
471
837
|
/** A built control's handle (internal-ish, but used across modules). */
|
|
@@ -479,6 +845,9 @@ export interface Control {
|
|
|
479
845
|
/** Show a short message on the control for a moment. Only the image control supplies it; the panel calls it when
|
|
480
846
|
* storage refuses the control's value. */
|
|
481
847
|
note?(message: string): void;
|
|
848
|
+
/** The hard range set() fits a number into, as the control resolved it (derived bounds too); a point's per component.
|
|
849
|
+
* Only the numeric controls supply it; the panel's clamp notice reads it. */
|
|
850
|
+
range?: [number, number] | Record<string, [number, number]>;
|
|
482
851
|
}
|
|
483
852
|
/** Options for `mountControl(host, value, opts?)` — one control, no panel. */
|
|
484
853
|
export interface MountOptions {
|
|
@@ -489,12 +858,19 @@ export interface MountOptions {
|
|
|
489
858
|
label?: string;
|
|
490
859
|
/** Fired with the new value whenever the control changes — by hand or through `set()`. */
|
|
491
860
|
onChange?: (value: unknown, key: string) => void;
|
|
861
|
+
/** Mount it in a shadow root, as `tweaks()`'s `shadow` does a panel; `el` is then the host. */
|
|
862
|
+
shadow?: boolean | {
|
|
863
|
+
href?: string;
|
|
864
|
+
};
|
|
865
|
+
/** Its words, as `tweaks()`'s `strings`. */
|
|
866
|
+
strings?: Strings;
|
|
492
867
|
}
|
|
493
868
|
/** The handle `mountControl()` returns. */
|
|
494
869
|
export interface MountedControl {
|
|
495
|
-
/** The wrapper (`.
|
|
870
|
+
/** The wrapper (`.twk-control`) the control lives in, already appended to the host and
|
|
496
871
|
* stable from creation — it carries the kit's tokens, so the control renders outside a
|
|
497
|
-
* panel. On the code-split build a lazily-loaded control fills it in once `ready` resolves.
|
|
872
|
+
* panel. On the code-split build a lazily-loaded control fills it in once `ready` resolves. With `shadow`, the host
|
|
873
|
+
* the wrapper lives in. */
|
|
498
874
|
el: HTMLElement;
|
|
499
875
|
/** Apply a value — the control updates AND `onChange` fires (when the value actually
|
|
500
876
|
* changed). Before `ready`, the value is queued and applied once the control builds. */
|
|
@@ -507,28 +883,34 @@ export interface MountedControl {
|
|
|
507
883
|
/** Resolves with this handle once the control has built — immediately on the single-file build. */
|
|
508
884
|
ready: Promise<MountedControl>;
|
|
509
885
|
}
|
|
510
|
-
/** The notations the
|
|
511
|
-
* the mode: `hex` gives `#
|
|
886
|
+
/** The notations the color picker edits in (its mode dropdown). The emitted value follows
|
|
887
|
+
* the mode: `hex` gives `#RRGGBB`, the wide-gamut modes give a CSS color like `oklch(…)`. */
|
|
512
888
|
export type ColorMode = "hex" | "srgb" | "css" | "hsl" | "hwb" | "oklch" | "oklab" | "lch" | "lab" | "p3" | "rec2020";
|
|
513
889
|
/** Options for `createColorPicker(opts?)` — the picker body alone, no trigger row. */
|
|
514
890
|
export interface ColorPickerOptions {
|
|
515
|
-
/** The
|
|
891
|
+
/** The color it opens on — any CSS color string (default `#7c5cff`). */
|
|
516
892
|
value?: string;
|
|
517
893
|
/** The notation it opens in (default `oklch`). */
|
|
518
894
|
mode?: ColorMode;
|
|
519
|
-
/** Fired with the new
|
|
895
|
+
/** Fired with the new color string whenever it changes — by hand or through `set()`. */
|
|
520
896
|
onChange?: (value: string) => void;
|
|
897
|
+
/** Build it in a shadow root, as `tweaks()`'s `shadow` does a panel; `el` is then the host. */
|
|
898
|
+
shadow?: boolean | {
|
|
899
|
+
href?: string;
|
|
900
|
+
};
|
|
901
|
+
/** Its words, as `tweaks()`'s `strings`. */
|
|
902
|
+
strings?: Strings;
|
|
521
903
|
}
|
|
522
904
|
/** The handle `createColorPicker()` returns. Bring your own trigger: append `el` to a popover
|
|
523
905
|
* or a panel of your own; the kit owns the editor surface and the gamut math. */
|
|
524
906
|
export interface ColorPicker {
|
|
525
|
-
/** The picker's root (`.
|
|
907
|
+
/** The picker's root (`.twk-color-picker`), stable from creation; it carries the kit's tokens,
|
|
526
908
|
* so the picker renders anywhere. On the code-split build the editor fills it in once the
|
|
527
|
-
*
|
|
909
|
+
* color chunk has loaded. With `shadow`, the host the root lives in. */
|
|
528
910
|
el: HTMLElement;
|
|
529
|
-
/** Apply a
|
|
911
|
+
/** Apply a color — the picker updates AND `onChange` fires (when the color actually changed). */
|
|
530
912
|
set(value: string): void;
|
|
531
|
-
/** The current
|
|
913
|
+
/** The current color, serialized in the picker's mode. */
|
|
532
914
|
get(): string;
|
|
533
915
|
/** The current notation. */
|
|
534
916
|
mode(): ColorMode;
|
|
@@ -542,7 +924,7 @@ export interface ColorPicker {
|
|
|
542
924
|
/** Resolves with this handle once the editor has built — immediately on the single-file build. */
|
|
543
925
|
ready: Promise<ColorPicker>;
|
|
544
926
|
}
|
|
545
|
-
/** `detail` of the `
|
|
927
|
+
/** `detail` of the `twk:change` event a `[data-twk]` host dispatches (bubbling) whenever its
|
|
546
928
|
* control changes — `key` is the host's `data-key` (or its label), `value` the new value,
|
|
547
929
|
* also written to the host's `data-value`. */
|
|
548
930
|
export interface TwChangeDetail {
|
|
@@ -551,6 +933,7 @@ export interface TwChangeDetail {
|
|
|
551
933
|
}
|
|
552
934
|
declare global {
|
|
553
935
|
interface HTMLElementEventMap {
|
|
554
|
-
"
|
|
936
|
+
"twk:change": CustomEvent<TwChangeDetail>;
|
|
555
937
|
}
|
|
556
938
|
}
|
|
939
|
+
export {};
|