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.
Files changed (132) hide show
  1. package/AGENTS.md +5 -5
  2. package/README.md +3 -3
  3. package/dist/tweaks/auto.js +1 -1
  4. package/dist/tweaks/auto.js.map +1 -1
  5. package/dist/tweaks/bezier-S2BOXQBM.js +2 -0
  6. package/dist/tweaks/bezier-S2BOXQBM.js.map +6 -0
  7. package/dist/tweaks/chunk-3MZAAMC2.js +2 -0
  8. package/dist/tweaks/chunk-3MZAAMC2.js.map +6 -0
  9. package/dist/tweaks/chunk-ETB35KFV.js +2 -0
  10. package/dist/tweaks/chunk-ETB35KFV.js.map +6 -0
  11. package/dist/tweaks/chunk-YB5GKWZI.js +2 -0
  12. package/dist/tweaks/chunk-YB5GKWZI.js.map +6 -0
  13. package/dist/tweaks/clip-A3VI53BK.js +2 -0
  14. package/dist/tweaks/clip-A3VI53BK.js.map +6 -0
  15. package/dist/tweaks/color-UGYMAF3W.js +2 -0
  16. package/dist/tweaks/core.js +1 -1
  17. package/dist/tweaks/core.js.map +3 -3
  18. package/dist/tweaks/fold-GY47HIQA.js +2 -0
  19. package/dist/tweaks/fold-GY47HIQA.js.map +6 -0
  20. package/dist/tweaks/gradient-BTX25WSH.js +2 -0
  21. package/dist/tweaks/gradient-BTX25WSH.js.map +6 -0
  22. package/dist/tweaks/{heavy-CLD7UAVI.js → heavy-NPFIGHRM.js} +2 -2
  23. package/dist/tweaks/image-HTQQ4WX6.js +4 -0
  24. package/dist/tweaks/image-HTQQ4WX6.js.map +6 -0
  25. package/dist/tweaks/{image-store-WRKY2NPU.js → image-store-QD5SMZXG.js} +1 -1
  26. package/dist/tweaks/image-store-QD5SMZXG.js.map +6 -0
  27. package/dist/tweaks/interval-BHPI4LRN.js +2 -0
  28. package/dist/tweaks/interval-BHPI4LRN.js.map +6 -0
  29. package/dist/tweaks/markup-QM53TQJZ.js +2 -0
  30. package/dist/tweaks/markup-QM53TQJZ.js.map +6 -0
  31. package/dist/tweaks/monitor-722WIYGW.js +3 -0
  32. package/dist/tweaks/monitor-722WIYGW.js.map +6 -0
  33. package/dist/tweaks/motion-HMCZK6HJ.js +2 -0
  34. package/dist/tweaks/motion-HMCZK6HJ.js.map +6 -0
  35. package/dist/tweaks/plot-D3NQ6XNN.js +3 -0
  36. package/dist/tweaks/plot-D3NQ6XNN.js.map +6 -0
  37. package/dist/tweaks/point-XDXKMDXH.js +2 -0
  38. package/dist/tweaks/point-XDXKMDXH.js.map +6 -0
  39. package/dist/tweaks/presets-EUWNSCUW.js +2 -0
  40. package/dist/tweaks/presets-EUWNSCUW.js.map +6 -0
  41. package/dist/tweaks/shadow-AM7VSRNS.js +2 -0
  42. package/dist/tweaks/shadow-AM7VSRNS.js.map +6 -0
  43. package/dist/tweaks/shadow-root-RYUV3AGS.js +3 -0
  44. package/dist/tweaks/shadow-root-RYUV3AGS.js.map +6 -0
  45. package/dist/tweaks/sides-JWIURDA4.js +2 -0
  46. package/dist/tweaks/sides-JWIURDA4.js.map +6 -0
  47. package/dist/tweaks/spring-BF5GBTPM.js +2 -0
  48. package/dist/tweaks/spring-BF5GBTPM.js.map +6 -0
  49. package/dist/tweaks/tabs-PRVUUZXZ.js +2 -0
  50. package/dist/tweaks/tabs-PRVUUZXZ.js.map +6 -0
  51. package/dist/tweaks.css +1 -1
  52. package/dist/tweaks.js +5 -4
  53. package/dist/tweaks.js.map +3 -3
  54. package/dist/types/tweaks/enhance.d.ts +3 -2
  55. package/dist/types/tweaks/types.d.ts +278 -72
  56. package/package.json +5 -2
  57. package/src/tweaks/auto.ts +9 -5
  58. package/src/tweaks/clip.ts +84 -22
  59. package/src/tweaks/controls/basic.ts +463 -139
  60. package/src/tweaks/controls/bezier.ts +152 -47
  61. package/src/tweaks/controls/color.ts +610 -161
  62. package/src/tweaks/controls/gradient.ts +256 -77
  63. package/src/tweaks/controls/image.ts +388 -132
  64. package/src/tweaks/controls/interval.ts +126 -41
  65. package/src/tweaks/controls/monitor.ts +142 -36
  66. package/src/tweaks/controls/motion.ts +231 -56
  67. package/src/tweaks/controls/plot.ts +321 -101
  68. package/src/tweaks/controls/point.ts +134 -45
  69. package/src/tweaks/controls/shadow.ts +103 -30
  70. package/src/tweaks/controls/sides.ts +150 -40
  71. package/src/tweaks/controls/spring.ts +220 -60
  72. package/src/tweaks/controls/tabs.ts +39 -18
  73. package/src/tweaks/core.ts +1 -1
  74. package/src/tweaks/easing.ts +45 -20
  75. package/src/tweaks/enhance.ts +21 -6
  76. package/src/tweaks/feedback.ts +133 -34
  77. package/src/tweaks/fold.ts +22 -10
  78. package/src/tweaks/heavy.ts +461 -137
  79. package/src/tweaks/icons.ts +12 -5
  80. package/src/tweaks/image-store.ts +131 -43
  81. package/src/tweaks/lazy.ts +36 -19
  82. package/src/tweaks/markup.ts +255 -92
  83. package/src/tweaks/panel.ts +1262 -438
  84. package/src/tweaks/presets.ts +85 -0
  85. package/src/tweaks/schema.ts +213 -51
  86. package/src/tweaks/shadow-root.ts +178 -0
  87. package/src/tweaks/shared.ts +802 -190
  88. package/src/tweaks/standalone.ts +120 -30
  89. package/src/tweaks/types.ts +644 -143
  90. package/src/tweaks/undo.ts +104 -0
  91. package/src/tweaks.css +556 -550
  92. package/src/wide-gamut.ts +473 -125
  93. package/dist/tweaks/bezier-JFEZMCLP.js +0 -2
  94. package/dist/tweaks/bezier-JFEZMCLP.js.map +0 -6
  95. package/dist/tweaks/chunk-6UO4DHBN.js +0 -2
  96. package/dist/tweaks/chunk-6UO4DHBN.js.map +0 -6
  97. package/dist/tweaks/chunk-DIQMLBAS.js +0 -2
  98. package/dist/tweaks/chunk-DIQMLBAS.js.map +0 -6
  99. package/dist/tweaks/chunk-XE6OUJBG.js +0 -2
  100. package/dist/tweaks/chunk-XE6OUJBG.js.map +0 -6
  101. package/dist/tweaks/clip-WW7JYO4X.js +0 -2
  102. package/dist/tweaks/clip-WW7JYO4X.js.map +0 -6
  103. package/dist/tweaks/color-YMMFURP6.js +0 -2
  104. package/dist/tweaks/fold-U6YRZLCJ.js +0 -2
  105. package/dist/tweaks/fold-U6YRZLCJ.js.map +0 -6
  106. package/dist/tweaks/gradient-JER7PW4D.js +0 -2
  107. package/dist/tweaks/gradient-JER7PW4D.js.map +0 -6
  108. package/dist/tweaks/image-H6QSLGO3.js +0 -4
  109. package/dist/tweaks/image-H6QSLGO3.js.map +0 -6
  110. package/dist/tweaks/image-store-WRKY2NPU.js.map +0 -6
  111. package/dist/tweaks/interval-TEUHQGZN.js +0 -2
  112. package/dist/tweaks/interval-TEUHQGZN.js.map +0 -6
  113. package/dist/tweaks/markup-NIBJD6E2.js +0 -2
  114. package/dist/tweaks/markup-NIBJD6E2.js.map +0 -6
  115. package/dist/tweaks/monitor-3BY7YCGX.js +0 -3
  116. package/dist/tweaks/monitor-3BY7YCGX.js.map +0 -6
  117. package/dist/tweaks/motion-FQPUBKKM.js +0 -2
  118. package/dist/tweaks/motion-FQPUBKKM.js.map +0 -6
  119. package/dist/tweaks/plot-2HBNBZSG.js +0 -3
  120. package/dist/tweaks/plot-2HBNBZSG.js.map +0 -6
  121. package/dist/tweaks/point-VV6XV3I6.js +0 -2
  122. package/dist/tweaks/point-VV6XV3I6.js.map +0 -6
  123. package/dist/tweaks/shadow-5E4E55I2.js +0 -2
  124. package/dist/tweaks/shadow-5E4E55I2.js.map +0 -6
  125. package/dist/tweaks/sides-P3STEVYY.js +0 -2
  126. package/dist/tweaks/sides-P3STEVYY.js.map +0 -6
  127. package/dist/tweaks/spring-HUVSXNVN.js +0 -2
  128. package/dist/tweaks/spring-HUVSXNVN.js.map +0 -6
  129. package/dist/tweaks/tabs-76FW7LQ7.js +0 -2
  130. package/dist/tweaks/tabs-76FW7LQ7.js.map +0 -6
  131. /package/dist/tweaks/{color-YMMFURP6.js.map → color-UGYMAF3W.js.map} +0 -0
  132. /package/dist/tweaks/{heavy-CLD7UAVI.js.map → heavy-NPFIGHRM.js.map} +0 -0
@@ -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 nothing). */
12
- export interface Slot { path: string[]; get: () => unknown; m: Meta; el?: Element }
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 { announce: typeof announce; alive: () => boolean }
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 { panel: HTMLElement; entries: Entry[]; fromJSON: (state: PanelState) => void }
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 => path.reduce<any>((o, k) => (isObj(o) && hasOwn(o, k) ? o[k] : undefined), obj);
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("."); if (path.some(isReservedKey)) continue;
28
- let o = out; for (const s of path.slice(0, -1)) o = isObj(o[s]) ? o[s] : (o[s] = {});
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) => isColorStr(v) && (typeof CSS === "undefined" || !CSS.supports || CSS.supports("color", v as string));
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)) return Array.isArray(v) && (cur.every((x) => !isObj(x)) ? v.length === cur.length && v.every((x, i) => sameShape(cur[i], x, col)) : v.length >= 2 && v.length <= 64 && v.every((x) => sameShape(cur[0], x, col)));
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 typeof v === typeof cur && (typeof v !== "number" || Number.isFinite(v)) && (!col || !isColorStr(cur) || isCssColor(v));
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) => v != null && !s.el?.closest(".is-disabled") && sameShape(s.get(), v, s.m.type === "color" || s.m.type === "gradient")
46
- && (!s.m.options || s.m.options.some((o) => JSON.stringify(optValue(o)) === JSON.stringify(v)));
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) { let o = out; for (const k of e.path.slice(0, -1)) o = o[k] ||= {}; o[e.path[e.path.length - 1]] = v; }
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, v = isObj(data) && !Array.isArray(data) && Object.keys(data).length === 1 && hasOwn(data, e.key) ? data[e.key] : data;
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)]) for (const v of [atPath(d, s.path), d]) { const h = isObj(v) ? pick(rows, v, s.path) : []; if (h.length > hits.length) hits = h; }
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 loaded. */
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) try { n = apply(text); } catch {}
88
- r.announce(!n ? (text === null ? "Couldn't read the clipboard" : "Nothing to paste") : `${n} value${n === 1 ? "" : "s"} pasted`);
89
- if (band) { // a quick second paste on the same row restarts the flash: the first one's timer mustn't cut it short
90
- const c = n ? "is-pasted" : "is-refused"; band.classList.remove("is-pasted", "is-refused"); band.classList.add(c);
91
- clearTimeout(flashes.get(band)); flashes.set(band, setTimeout(() => band.classList.remove(c), 450));
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
  }