tweakit 0.6.0 → 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 +5 -5
- package/README.md +3 -3
- package/dist/tweaks/auto.js +1 -1
- package/dist/tweaks/auto.js.map +1 -1
- 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/core.js +1 -1
- package/dist/tweaks/core.js.map +3 -3
- 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-CLD7UAVI.js → heavy-NPFIGHRM.js} +2 -2
- package/dist/tweaks/image-HTQQ4WX6.js +4 -0
- package/dist/tweaks/image-HTQQ4WX6.js.map +6 -0
- package/dist/tweaks/{image-store-WRKY2NPU.js → image-store-QD5SMZXG.js} +1 -1
- 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 +5 -4
- package/dist/tweaks.js.map +3 -3
- package/dist/types/tweaks/enhance.d.ts +3 -2
- package/dist/types/tweaks/types.d.ts +278 -72
- package/package.json +5 -2
- package/src/tweaks/auto.ts +9 -5
- package/src/tweaks/clip.ts +84 -22
- package/src/tweaks/controls/basic.ts +463 -139
- package/src/tweaks/controls/bezier.ts +152 -47
- package/src/tweaks/controls/color.ts +610 -161
- package/src/tweaks/controls/gradient.ts +256 -77
- package/src/tweaks/controls/image.ts +388 -132
- package/src/tweaks/controls/interval.ts +126 -41
- package/src/tweaks/controls/monitor.ts +142 -36
- package/src/tweaks/controls/motion.ts +231 -56
- package/src/tweaks/controls/plot.ts +321 -101
- package/src/tweaks/controls/point.ts +134 -45
- package/src/tweaks/controls/shadow.ts +103 -30
- package/src/tweaks/controls/sides.ts +150 -40
- package/src/tweaks/controls/spring.ts +220 -60
- package/src/tweaks/controls/tabs.ts +39 -18
- package/src/tweaks/core.ts +1 -1
- package/src/tweaks/easing.ts +45 -20
- package/src/tweaks/enhance.ts +21 -6
- package/src/tweaks/feedback.ts +133 -34
- package/src/tweaks/fold.ts +22 -10
- package/src/tweaks/heavy.ts +461 -137
- package/src/tweaks/icons.ts +12 -5
- package/src/tweaks/image-store.ts +131 -43
- package/src/tweaks/lazy.ts +36 -19
- package/src/tweaks/markup.ts +255 -92
- package/src/tweaks/panel.ts +1262 -438
- package/src/tweaks/presets.ts +85 -0
- package/src/tweaks/schema.ts +213 -51
- package/src/tweaks/shadow-root.ts +178 -0
- package/src/tweaks/shared.ts +802 -190
- package/src/tweaks/standalone.ts +120 -30
- package/src/tweaks/types.ts +644 -143
- package/src/tweaks/undo.ts +104 -0
- package/src/tweaks.css +556 -550
- package/src/wide-gamut.ts +473 -125
- package/dist/tweaks/bezier-JFEZMCLP.js +0 -2
- package/dist/tweaks/bezier-JFEZMCLP.js.map +0 -6
- package/dist/tweaks/chunk-6UO4DHBN.js +0 -2
- package/dist/tweaks/chunk-6UO4DHBN.js.map +0 -6
- package/dist/tweaks/chunk-DIQMLBAS.js +0 -2
- package/dist/tweaks/chunk-DIQMLBAS.js.map +0 -6
- package/dist/tweaks/chunk-XE6OUJBG.js +0 -2
- package/dist/tweaks/chunk-XE6OUJBG.js.map +0 -6
- package/dist/tweaks/clip-WW7JYO4X.js +0 -2
- package/dist/tweaks/clip-WW7JYO4X.js.map +0 -6
- package/dist/tweaks/color-YMMFURP6.js +0 -2
- package/dist/tweaks/fold-U6YRZLCJ.js +0 -2
- package/dist/tweaks/fold-U6YRZLCJ.js.map +0 -6
- package/dist/tweaks/gradient-JER7PW4D.js +0 -2
- package/dist/tweaks/gradient-JER7PW4D.js.map +0 -6
- package/dist/tweaks/image-H6QSLGO3.js +0 -4
- package/dist/tweaks/image-H6QSLGO3.js.map +0 -6
- package/dist/tweaks/image-store-WRKY2NPU.js.map +0 -6
- package/dist/tweaks/interval-TEUHQGZN.js +0 -2
- package/dist/tweaks/interval-TEUHQGZN.js.map +0 -6
- package/dist/tweaks/markup-NIBJD6E2.js +0 -2
- package/dist/tweaks/markup-NIBJD6E2.js.map +0 -6
- package/dist/tweaks/monitor-3BY7YCGX.js +0 -3
- package/dist/tweaks/monitor-3BY7YCGX.js.map +0 -6
- package/dist/tweaks/motion-FQPUBKKM.js +0 -2
- package/dist/tweaks/motion-FQPUBKKM.js.map +0 -6
- package/dist/tweaks/plot-2HBNBZSG.js +0 -3
- package/dist/tweaks/plot-2HBNBZSG.js.map +0 -6
- package/dist/tweaks/point-VV6XV3I6.js +0 -2
- package/dist/tweaks/point-VV6XV3I6.js.map +0 -6
- package/dist/tweaks/shadow-5E4E55I2.js +0 -2
- package/dist/tweaks/shadow-5E4E55I2.js.map +0 -6
- package/dist/tweaks/sides-P3STEVYY.js +0 -2
- package/dist/tweaks/sides-P3STEVYY.js.map +0 -6
- package/dist/tweaks/spring-HUVSXNVN.js +0 -2
- package/dist/tweaks/spring-HUVSXNVN.js.map +0 -6
- package/dist/tweaks/tabs-76FW7LQ7.js +0 -2
- package/dist/tweaks/tabs-76FW7LQ7.js.map +0 -6
- /package/dist/tweaks/{color-YMMFURP6.js.map → color-UGYMAF3W.js.map} +0 -0
- /package/dist/tweaks/{heavy-CLD7UAVI.js.map → heavy-NPFIGHRM.js.map} +0 -0
package/src/tweaks/clip.ts
CHANGED
|
@@ -6,26 +6,44 @@ import type { Entry, Snapshot } from "./panel.js";
|
|
|
6
6
|
import type { PanelState } from "./types.js";
|
|
7
7
|
import type { Meta } from "./schema.js";
|
|
8
8
|
import type { announce } from "./feedback.js";
|
|
9
|
-
import { isObj, isColorStr, optValue, hasOwn, isReservedKey } from "./shared.js";
|
|
9
|
+
import { isObj, isColorStr, optValue, hasOwn, isReservedKey, words } from "./shared.js";
|
|
10
|
+
import type { Tr } from "./shared.js";
|
|
10
11
|
|
|
11
|
-
/** Where a pasted value can land: a control's path, its current value, its meta and its row (a disabled one takes
|
|
12
|
-
|
|
12
|
+
/** Where a pasted value can land: a control's path, its current value, its meta and its row (a disabled one takes
|
|
13
|
+
* nothing). */
|
|
14
|
+
export interface Slot {
|
|
15
|
+
path: string[];
|
|
16
|
+
get: () => unknown;
|
|
17
|
+
m: Meta;
|
|
18
|
+
el?: Element;
|
|
19
|
+
}
|
|
13
20
|
/** How a paste reports: the words a screen reader hears, and whether its panel is still there. */
|
|
14
|
-
export interface Report {
|
|
21
|
+
export interface Report {
|
|
22
|
+
announce: typeof announce;
|
|
23
|
+
alive: () => boolean;
|
|
24
|
+
tr?: Tr;
|
|
25
|
+
}
|
|
15
26
|
/** What a panel lends the paste. */
|
|
16
|
-
export interface ClipCtx extends Report {
|
|
27
|
+
export interface ClipCtx extends Report {
|
|
28
|
+
panel: HTMLElement;
|
|
29
|
+
entries: Entry[];
|
|
30
|
+
fromJSON: (state: PanelState) => void;
|
|
31
|
+
}
|
|
17
32
|
/** What a scoped ⌘C / ⌘V acts on: one row, or every row under a folder's path. */
|
|
18
33
|
export type Scope = { kind: "row"; entry: Entry; label: string } | { kind: "folder"; path: string[]; label: string };
|
|
19
34
|
|
|
20
|
-
const atPath = (obj: unknown, path: string[]): unknown =>
|
|
35
|
+
const atPath = (obj: unknown, path: string[]): unknown =>
|
|
36
|
+
path.reduce<any>((o, k) => (isObj(o) && hasOwn(o, k) ? o[k] : undefined), obj);
|
|
21
37
|
|
|
22
38
|
/** ⇧-copy's output is a flat map by dotted path; nested, "folder.child" lands where set() would put it. A reserved
|
|
23
39
|
* segment is dropped, so a pasted "__proto__.x" can't write onto Object.prototype. */
|
|
24
40
|
const unflatten = (flat: Record<string, unknown>): Snapshot => {
|
|
25
41
|
const out: Snapshot = {};
|
|
26
42
|
for (const k of Object.keys(flat)) {
|
|
27
|
-
const path = k.split(".");
|
|
28
|
-
|
|
43
|
+
const path = k.split(".");
|
|
44
|
+
if (path.some(isReservedKey)) continue;
|
|
45
|
+
let o = out;
|
|
46
|
+
for (const s of path.slice(0, -1)) o = isObj(o[s]) ? o[s] : (o[s] = {});
|
|
29
47
|
o[path[path.length - 1]] = flat[k];
|
|
30
48
|
}
|
|
31
49
|
return out;
|
|
@@ -36,14 +54,28 @@ const unflatten = (flat: Record<string, unknown>): Snapshot => {
|
|
|
36
54
|
// shaped like the first, a color the browser can parse wherever a color stands (a color or gradient control), and
|
|
37
55
|
// for a list control one of its options. Anything else would be coerced or ignored by set() — null to 0, a stray string
|
|
38
56
|
// to black, a short tuple dropped — so it's skipped, and counted as not pasted; so is anything for a disabled row.
|
|
39
|
-
const isCssColor = (v: unknown) =>
|
|
57
|
+
const isCssColor = (v: unknown) =>
|
|
58
|
+
isColorStr(v) && (typeof CSS === "undefined" || !CSS.supports || CSS.supports("color", v as string));
|
|
40
59
|
const sameShape = (cur: unknown, v: unknown, col: boolean): boolean => {
|
|
41
|
-
if (Array.isArray(cur))
|
|
60
|
+
if (Array.isArray(cur))
|
|
61
|
+
return (
|
|
62
|
+
Array.isArray(v) &&
|
|
63
|
+
(cur.every((x) => !isObj(x))
|
|
64
|
+
? v.length === cur.length && v.every((x, i) => sameShape(cur[i], x, col))
|
|
65
|
+
: v.length >= 2 && v.length <= 64 && v.every((x) => sameShape(cur[0], x, col)))
|
|
66
|
+
);
|
|
42
67
|
if (isObj(cur)) return isObj(v) && !Array.isArray(v) && Object.keys(cur).every((k) => sameShape(cur[k], v[k], col));
|
|
43
|
-
return
|
|
68
|
+
return (
|
|
69
|
+
typeof v === typeof cur &&
|
|
70
|
+
(typeof v !== "number" || Number.isFinite(v)) &&
|
|
71
|
+
(!col || !isColorStr(cur) || isCssColor(v))
|
|
72
|
+
);
|
|
44
73
|
};
|
|
45
|
-
const fits = (s: Slot, v: unknown) =>
|
|
46
|
-
|
|
74
|
+
const fits = (s: Slot, v: unknown) =>
|
|
75
|
+
v != null &&
|
|
76
|
+
!s.el?.closest(".is-disabled") &&
|
|
77
|
+
sameShape(s.get(), v, s.m.type === "color" || s.m.type === "gradient") &&
|
|
78
|
+
(!s.m.options || s.m.options.some((o) => JSON.stringify(optValue(o)) === JSON.stringify(v)));
|
|
47
79
|
|
|
48
80
|
/** The values in `values` that fit their slots, each slot read at its path below `base`. */
|
|
49
81
|
export const pick = <S extends Slot>(slots: S[], values: unknown, base: string[] = []): Array<[S, unknown]> =>
|
|
@@ -52,7 +84,11 @@ export const pick = <S extends Slot>(slots: S[], values: unknown, base: string[]
|
|
|
52
84
|
// Only what fits is written, through fromJSON: a bag key or a value set() would coerce stays as it is.
|
|
53
85
|
const write = (ctx: ClipCtx, hits: Array<[Entry, unknown]>) => {
|
|
54
86
|
const out: Snapshot = {};
|
|
55
|
-
for (const [e, v] of hits) {
|
|
87
|
+
for (const [e, v] of hits) {
|
|
88
|
+
let o = out;
|
|
89
|
+
for (const k of e.path.slice(0, -1)) o = o[k] ||= {};
|
|
90
|
+
o[e.path[e.path.length - 1]] = v;
|
|
91
|
+
}
|
|
56
92
|
ctx.fromJSON({ values: out });
|
|
57
93
|
};
|
|
58
94
|
|
|
@@ -64,13 +100,21 @@ export function pasteScope(ctx: ClipCtx, s: Scope, text: string): number | false
|
|
|
64
100
|
const data = JSON.parse(text);
|
|
65
101
|
let hits: Array<[Entry, unknown]>;
|
|
66
102
|
if (s.kind === "row") {
|
|
67
|
-
const e = s.entry,
|
|
103
|
+
const e = s.entry,
|
|
104
|
+
v =
|
|
105
|
+
isObj(data) && !Array.isArray(data) && Object.keys(data).length === 1 && hasOwn(data, e.key)
|
|
106
|
+
? data[e.key]
|
|
107
|
+
: data;
|
|
68
108
|
hits = fits(e, v) ? [[e, v]] : [];
|
|
69
109
|
} else {
|
|
70
110
|
if (!isObj(data) || Array.isArray(data)) return false;
|
|
71
111
|
const rows = ctx.entries.filter((e) => e.path.length > s.path.length && s.path.every((k, i) => e.path[i] === k));
|
|
72
112
|
hits = [];
|
|
73
|
-
for (const d of [data, unflatten(data)])
|
|
113
|
+
for (const d of [data, unflatten(data)])
|
|
114
|
+
for (const v of [atPath(d, s.path), d]) {
|
|
115
|
+
const h = isObj(v) ? pick(rows, v, s.path) : [];
|
|
116
|
+
if (h.length > hits.length) hits = h;
|
|
117
|
+
}
|
|
74
118
|
}
|
|
75
119
|
if (!hits.length) return false;
|
|
76
120
|
write(ctx, hits);
|
|
@@ -80,15 +124,33 @@ export function pasteScope(ctx: ClipCtx, s: Scope, text: string): number | false
|
|
|
80
124
|
const flashes = new WeakMap<Element, ReturnType<typeof setTimeout>>();
|
|
81
125
|
/** Every paste ends here. `text` is what the browser's paste event carried (null: none); `apply` returns how many
|
|
82
126
|
* values it took, or false for nothing usable (a throw counts the same). A screen reader hears which, and a picked
|
|
83
|
-
* `band` flashes it; the result is whether anything landed, or undefined when the panel went while this chunk
|
|
127
|
+
* `band` flashes it; the result is whether anything landed, or undefined when the panel went while this chunk
|
|
128
|
+
* loaded. */
|
|
84
129
|
export function pasteInto(text: string | null, apply: (text: string) => number | false, r: Report, band?: Element) {
|
|
85
130
|
if (!r.alive()) return;
|
|
86
131
|
let n: number | false = false;
|
|
87
|
-
if (text !== null)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
132
|
+
if (text !== null)
|
|
133
|
+
try {
|
|
134
|
+
n = apply(text);
|
|
135
|
+
} catch {}
|
|
136
|
+
const tr = r.tr || words();
|
|
137
|
+
r.announce(
|
|
138
|
+
!n
|
|
139
|
+
? text === null
|
|
140
|
+
? tr("clipboardUnread", "Couldn't read the clipboard")
|
|
141
|
+
: tr("nothingToPaste", "Nothing to paste")
|
|
142
|
+
: tr("pasted", (v) => `${v.n} value${v.n === 1 ? "" : "s"} pasted`, { n }),
|
|
143
|
+
);
|
|
144
|
+
if (band) {
|
|
145
|
+
// a quick second paste on the same row restarts the flash: the first one's timer mustn't cut it short
|
|
146
|
+
const c = n ? "is-pasted" : "is-refused";
|
|
147
|
+
band.classList.remove("is-pasted", "is-refused");
|
|
148
|
+
band.classList.add(c);
|
|
149
|
+
clearTimeout(flashes.get(band));
|
|
150
|
+
flashes.set(
|
|
151
|
+
band,
|
|
152
|
+
setTimeout(() => band.classList.remove(c), 450),
|
|
153
|
+
);
|
|
92
154
|
}
|
|
93
155
|
return !!n;
|
|
94
156
|
}
|