twiddle-panel 0.8.2 → 0.9.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.
- package/README.md +6 -6
- package/dist/twiddle.js +173 -111
- package/dist/twiddle.mjs +1245 -1063
- package/package.json +1 -1
package/dist/twiddle.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const
|
|
2
|
-
let M =
|
|
3
|
-
function
|
|
1
|
+
const Ct = /* @__PURE__ */ new Set();
|
|
2
|
+
let M = ya(), oe = [], Ee = [], Ze = null, gn = 0;
|
|
3
|
+
function ya() {
|
|
4
4
|
const e = new URLSearchParams(location.search).get("twd");
|
|
5
5
|
if (!e) return {};
|
|
6
6
|
try {
|
|
@@ -9,140 +9,163 @@ function sa() {
|
|
|
9
9
|
return {};
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function ka() {
|
|
13
13
|
const e = new URL(location.href);
|
|
14
14
|
Object.keys(M).length ? e.searchParams.set("twd", JSON.stringify(M)) : e.searchParams.delete("twd"), history.replaceState(history.state, "", e);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
16
|
+
function Rt() {
|
|
17
|
+
ka(), Ct.forEach((e) => e(M));
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function re(e, t) {
|
|
20
20
|
if (JSON.stringify(e) === JSON.stringify(M)) return;
|
|
21
21
|
const n = Date.now();
|
|
22
|
-
t && t ===
|
|
22
|
+
t && t === Ze && n - gn < 400 || (oe.push(M), oe.length > 100 && oe.shift()), Ee = [], Ze = t, gn = n, M = e, Rt();
|
|
23
23
|
}
|
|
24
|
-
function
|
|
24
|
+
function Te(e, t) {
|
|
25
25
|
const n = { ...e, ...t };
|
|
26
26
|
return Object.keys(n).forEach((a) => {
|
|
27
27
|
n[a] == null && delete n[a];
|
|
28
28
|
}), Object.keys(n).length ? n : null;
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
function I(e, t, n) {
|
|
31
31
|
const a = { ...e };
|
|
32
32
|
return n == null ? delete a[t] : a[t] = n, a;
|
|
33
33
|
}
|
|
34
|
-
function
|
|
34
|
+
function Mt() {
|
|
35
35
|
return JSON.parse(JSON.stringify(M));
|
|
36
36
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
37
|
+
function Sa(e, t) {
|
|
38
|
+
re(I(M, e, Te(M[e], t)), `${e}:${Object.keys(t)}`);
|
|
39
|
+
}
|
|
40
|
+
function $a(e, t = `multi:${Object.keys(e)}`) {
|
|
41
|
+
const n = Object.entries(e).reduce((a, [o, r]) => I(a, o, Te(a[o], r)), M);
|
|
42
|
+
re(n, t);
|
|
39
43
|
}
|
|
40
|
-
function
|
|
41
|
-
const
|
|
42
|
-
|
|
44
|
+
function In(e, t, n) {
|
|
45
|
+
const a = I(M.scopes?.[e], t, Te(M.scopes?.[e]?.[t], n)), o = I(M.scopes, e, Object.keys(a).length ? a : null);
|
|
46
|
+
re(I(M, "scopes", Object.keys(o).length ? o : null), `${e}|${t}:${Object.keys(n)}`);
|
|
43
47
|
}
|
|
44
|
-
function
|
|
45
|
-
const a =
|
|
46
|
-
|
|
48
|
+
function Ea(e, t, n = `multi:${Object.keys(t)}`) {
|
|
49
|
+
const a = Object.entries(t).reduce((r, [s, i]) => I(r, s, Te(r?.[s], i)), M.scopes?.[e]), o = I(M.scopes, e, Object.keys(a).length ? a : null);
|
|
50
|
+
re(I(M, "scopes", Object.keys(o).length ? o : null), `${e}|${n}`);
|
|
47
51
|
}
|
|
48
|
-
function
|
|
49
|
-
if (t) return
|
|
52
|
+
function Ca(e, t) {
|
|
53
|
+
if (t) return In(e, t, Object.fromEntries(
|
|
50
54
|
Object.keys(M.scopes?.[e]?.[t] || {}).map((a) => [a, null])
|
|
51
55
|
));
|
|
52
|
-
const n =
|
|
53
|
-
|
|
56
|
+
const n = I(M.scopes, e, null);
|
|
57
|
+
re(I(M, "scopes", Object.keys(n).length ? n : null));
|
|
54
58
|
}
|
|
55
|
-
function
|
|
59
|
+
function Ma(e, t) {
|
|
56
60
|
const n = M.scopes?.[e];
|
|
57
61
|
if (!n || e === t) return;
|
|
58
62
|
let a = { ...M.scopes[t] };
|
|
59
|
-
Object.entries(n).forEach(([
|
|
60
|
-
a =
|
|
63
|
+
Object.entries(n).forEach(([r, s]) => {
|
|
64
|
+
a = I(a, r, Te(a[r], s));
|
|
61
65
|
});
|
|
62
|
-
const o =
|
|
63
|
-
|
|
66
|
+
const o = I(I(M.scopes, e, null), t, a);
|
|
67
|
+
re(I(M, "scopes", o));
|
|
64
68
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
69
|
+
function Dn() {
|
|
70
|
+
re({});
|
|
67
71
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
72
|
+
function Bn() {
|
|
73
|
+
oe.length && (Ee.push(M), M = oe.pop(), Ze = null, Rt());
|
|
70
74
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
75
|
+
function Un() {
|
|
76
|
+
Ee.length && (oe.push(M), M = Ee.pop(), Ze = null, Rt());
|
|
73
77
|
}
|
|
74
|
-
const
|
|
75
|
-
function
|
|
76
|
-
return
|
|
78
|
+
const Ta = () => oe.length > 0, La = () => Ee.length > 0;
|
|
79
|
+
function Vn(e) {
|
|
80
|
+
return Ct.add(e), () => Ct.delete(e);
|
|
77
81
|
}
|
|
78
|
-
const
|
|
82
|
+
const Aa = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
79
83
|
__proto__: null,
|
|
80
|
-
canRedo:
|
|
81
|
-
canUndo:
|
|
82
|
-
getState:
|
|
83
|
-
moveScope:
|
|
84
|
-
onChange:
|
|
85
|
-
redo:
|
|
86
|
-
removeScope:
|
|
87
|
-
resetState:
|
|
88
|
-
setScope:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
84
|
+
canRedo: La,
|
|
85
|
+
canUndo: Ta,
|
|
86
|
+
getState: Mt,
|
|
87
|
+
moveScope: Ma,
|
|
88
|
+
onChange: Vn,
|
|
89
|
+
redo: Un,
|
|
90
|
+
removeScope: Ca,
|
|
91
|
+
resetState: Dn,
|
|
92
|
+
setScope: In,
|
|
93
|
+
setScopeSections: Ea,
|
|
94
|
+
setSection: Sa,
|
|
95
|
+
setSections: $a,
|
|
96
|
+
undo: Bn
|
|
97
|
+
}, Symbol.toStringTag, { value: "Module" })), qn = ':root{--twd-font-heading: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-text-xs: .694rem;--twd-text-sm: .833rem;--twd-text-base: 1rem;--twd-text-lg: 1.2rem;--twd-text-xl: 1.44rem;--twd-text-2xl: 1.728rem;--twd-text-3xl: 2.074rem;--twd-leading-tight: 1.2;--twd-leading-normal: 1.5;--twd-weight-heading: 600;--twd-weight-strong: 600;--twd-bg: #fafafa;--twd-surface: #ffffff;--twd-surface-raised: #ffffff;--twd-text: #18181b;--twd-text-muted: #71717a;--twd-border: #e4e4e7;--twd-primary: #4f46e5;--twd-primary-hover: #4338ca;--twd-on-primary: #ffffff;--twd-focus: #6366f1;--twd-success: #16a34a;--twd-warning: #d97706;--twd-danger: #dc2626;--twd-radius-control: 8px;--twd-radius-container: 12px;--twd-space-1: 4px;--twd-space-2: 8px;--twd-space-3: 12px;--twd-space-4: 16px;--twd-space-5: 24px;--twd-space-6: 32px;--twd-space-7: 48px;--twd-space-8: 64px;--twd-control-height: 40px;--twd-control-pad-x: 16px;--twd-page-pad: var(--twd-space-5);--twd-section-gap: var(--twd-space-6);--twd-card-pad: var(--twd-space-5);--twd-card-gap: var(--twd-space-2);--twd-control-gap: var(--twd-space-3);--twd-border-width: 1px;--twd-border-width-container: var(--twd-border-width);--twd-shadow-sm: 0 1px 2px rgb(0 0 0 / .06);--twd-shadow-md: 0 4px 12px rgb(0 0 0 / .08);--twd-shadow-lg: 0 12px 32px rgb(0 0 0 / .12);--twd-button-bg: var(--twd-primary);--twd-button-fg: var(--twd-on-primary);--twd-button-border: transparent;--twd-button-hover: var(--twd-primary-hover);--twd-card-bg: var(--twd-surface);--twd-card-border: var(--twd-border);--twd-card-shadow: var(--twd-shadow-sm)}', Pa = [...qn.matchAll(/(--twd-[\w-]+):\s*([^;]*var\(--twd-[^;]*);/g)].map((e) => [e[1], e[2].trim()]);
|
|
98
|
+
function Oa(e) {
|
|
99
|
+
const t = document.documentElement, n = document.createElement("div");
|
|
100
|
+
n.hidden = !0, t.appendChild(n);
|
|
101
|
+
const a = getComputedStyle(t), o = getComputedStyle(n), r = [];
|
|
102
|
+
return Pa.forEach(([s, i]) => {
|
|
103
|
+
const l = e[s];
|
|
104
|
+
if (l != null) {
|
|
105
|
+
String(l).includes("var(") && r.push([s, l]);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
n.style.setProperty("--twd-probe", i), o.getPropertyValue("--twd-probe").trim() === a.getPropertyValue(s).trim() && r.push([s, i]);
|
|
109
|
+
}), n.remove(), r;
|
|
110
|
+
}
|
|
111
|
+
const Hn = [];
|
|
112
|
+
let Tt = [], zt = {}, R = null, fe = !1, _n = {};
|
|
113
|
+
function ja(e) {
|
|
114
|
+
typeof e == "function" && Hn.push(e);
|
|
115
|
+
}
|
|
116
|
+
const mn = (e) => Object.assign({}, ...Hn.map((t) => t(e) || {})), Lt = (e) => /^(\.[\w-]+|\[data-twd="[^"]+"\])$/.test(e), Fa = (e) => {
|
|
98
117
|
try {
|
|
99
118
|
return document.querySelector(e), !0;
|
|
100
119
|
} catch {
|
|
101
120
|
return !1;
|
|
102
121
|
}
|
|
103
122
|
};
|
|
104
|
-
function
|
|
123
|
+
function Na() {
|
|
105
124
|
return (!R || !R.isConnected) && (R = document.createElement("style"), R.id = "twiddle-scoped", document.head.appendChild(R)), R;
|
|
106
125
|
}
|
|
107
|
-
function
|
|
126
|
+
function Kn() {
|
|
108
127
|
const e = document.documentElement;
|
|
109
|
-
|
|
128
|
+
Tt.forEach((t) => e.style.removeProperty(t)), Tt = [], zt = {};
|
|
110
129
|
}
|
|
111
|
-
function
|
|
112
|
-
if (
|
|
130
|
+
function At(e) {
|
|
131
|
+
if (_n = e, fe) return;
|
|
113
132
|
const t = document.documentElement;
|
|
114
|
-
|
|
115
|
-
const n =
|
|
116
|
-
Object.entries(n).forEach(([s,
|
|
117
|
-
const
|
|
118
|
-
${
|
|
133
|
+
Kn();
|
|
134
|
+
const n = mn(e), a = e.scopes ? Oa(n) : [], o = Object.entries(e.scopes || {}).filter(([s]) => Fa(s)).sort(([s], [i]) => Lt(i) - Lt(s)).map(([s, i]) => [s, mn(i)]);
|
|
135
|
+
Object.entries(n).forEach(([s, i]) => t.style.setProperty(s, i)), Tt = Object.keys(n), zt = n;
|
|
136
|
+
const r = o.filter(([, s]) => Object.keys(s).length).map(([s, i]) => `${s} {
|
|
137
|
+
${[
|
|
138
|
+
// Derived tokens first, without !important, so the page's own rules for them still win.
|
|
139
|
+
...a.filter(([l]) => !(l in i)).map(([l, d]) => ` ${l}: ${d};`),
|
|
140
|
+
...Object.entries(i).map(([l, d]) => ` ${l}: ${d} !important;`)
|
|
141
|
+
].join(`
|
|
119
142
|
`)}
|
|
120
143
|
}`).join(`
|
|
121
144
|
`);
|
|
122
|
-
(
|
|
145
|
+
(r || R) && (Na().textContent = r);
|
|
123
146
|
}
|
|
124
|
-
function
|
|
125
|
-
const t = document.documentElement, n =
|
|
147
|
+
function Wn(e) {
|
|
148
|
+
const t = document.documentElement, n = fe, a = { ...zt };
|
|
126
149
|
Object.keys(a).forEach((o) => t.style.removeProperty(o)), R && (R.disabled = !0);
|
|
127
150
|
try {
|
|
128
151
|
return e();
|
|
129
152
|
} finally {
|
|
130
|
-
n || (Object.entries(a).forEach(([o,
|
|
153
|
+
n || (Object.entries(a).forEach(([o, r]) => t.style.setProperty(o, r)), R && (R.disabled = !1));
|
|
131
154
|
}
|
|
132
155
|
}
|
|
133
|
-
function
|
|
134
|
-
return
|
|
156
|
+
function Ra(e, t = document.documentElement) {
|
|
157
|
+
return Wn(() => {
|
|
135
158
|
const n = getComputedStyle(t);
|
|
136
159
|
return Object.fromEntries(e.map((a) => [a, n.getPropertyValue(a).trim()]));
|
|
137
160
|
});
|
|
138
161
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
162
|
+
function za() {
|
|
163
|
+
fe || (Kn(), R && (R.disabled = !0), fe = !0);
|
|
141
164
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
165
|
+
function Ia() {
|
|
166
|
+
fe && (fe = !1, R && (R.disabled = !1), At(_n));
|
|
144
167
|
}
|
|
145
|
-
const
|
|
168
|
+
const Da = `
|
|
146
169
|
:host { all: initial; } /* block fonts/colours inherited from the page */
|
|
147
170
|
* { box-sizing: border-box; }
|
|
148
171
|
|
|
@@ -154,6 +177,9 @@ const va = `
|
|
|
154
177
|
--p-hover: #f4f4f5;
|
|
155
178
|
--p-track: #f4f4f5;
|
|
156
179
|
--p-selected: #ffffff;
|
|
180
|
+
--p-line-strong: #d4d4d8; /* tile and segmented outlines */
|
|
181
|
+
--p-brand: #4f46e5; /* selected borders (Twiddle accent) */
|
|
182
|
+
--p-brand-fg: #4f46e5; /* selected icons and labels */
|
|
157
183
|
--p-accent: #18181b;
|
|
158
184
|
--p-on-accent: #ffffff;
|
|
159
185
|
--p-shadow: 0 12px 40px rgb(0 0 0 / 0.14), 0 2px 6px rgb(0 0 0 / 0.06);
|
|
@@ -184,6 +210,11 @@ const va = `
|
|
|
184
210
|
--p-hover: #27272a;
|
|
185
211
|
--p-track: #27272a;
|
|
186
212
|
--p-selected: #3f3f46;
|
|
213
|
+
--p-line-strong: #3f3f46;
|
|
214
|
+
/* Brand on dark (Ravi, 2026-10-16): the accent #4f46e5 is too dark to read on
|
|
215
|
+
#1c1c1f, so borders use #6366f1 and icons/labels the lighter #818cf8. */
|
|
216
|
+
--p-brand: #6366f1;
|
|
217
|
+
--p-brand-fg: #818cf8;
|
|
187
218
|
--p-accent: #f4f4f5;
|
|
188
219
|
--p-on-accent: #18181b;
|
|
189
220
|
--p-shadow: 0 12px 40px rgb(0 0 0 / 0.5), 0 2px 6px rgb(0 0 0 / 0.3);
|
|
@@ -202,14 +233,14 @@ const va = `
|
|
|
202
233
|
cursor: pointer;
|
|
203
234
|
padding: 0;
|
|
204
235
|
}
|
|
205
|
-
/*
|
|
236
|
+
/* Minimised: centred on the tab bar. 10px = panel border 1 + tab bar
|
|
206
237
|
padding 6 + half a tab (~49px) − half the launcher (22). */
|
|
207
238
|
.launcher { transition: margin-bottom .2s cubic-bezier(0.2, 0.8, 0.2, 1); }
|
|
208
|
-
.wrap.is-
|
|
239
|
+
.wrap.is-min .launcher { margin-bottom: 10px; } /* open: the actions row is at the bottom, already centred (~40px tall) */
|
|
209
240
|
/* Left corners: the panel opens to the right of the launcher. Top corners: tops aligned. */
|
|
210
241
|
.wrap.at-left { flex-direction: row-reverse; }
|
|
211
242
|
.wrap.at-top { align-items: flex-start; }
|
|
212
|
-
.wrap.at-top.is-
|
|
243
|
+
.wrap.at-top.is-min .launcher { margin-bottom: 0; }
|
|
213
244
|
.launcher { touch-action: none; }
|
|
214
245
|
.wrap.dragging .launcher { cursor: grabbing; }
|
|
215
246
|
.wrap.dragging .panel { pointer-events: none; }
|
|
@@ -245,15 +276,10 @@ const va = `
|
|
|
245
276
|
}
|
|
246
277
|
.panel[hidden] { display: none; }
|
|
247
278
|
|
|
248
|
-
.head {
|
|
249
|
-
display: flex; align-items: center; gap: 4px;
|
|
250
|
-
padding: 10px 10px 6px 14px; /* the actions row below carries the divider */
|
|
251
|
-
}
|
|
252
|
-
.brand { font-weight: 600; font-size: 14px; letter-spacing: -0.01em; flex: 1; }
|
|
253
279
|
.icon-btn {
|
|
254
280
|
width: 28px; height: 28px; padding: 0;
|
|
255
281
|
border: 0; border-radius: 6px;
|
|
256
|
-
background: transparent; color: var(--p-
|
|
282
|
+
background: transparent; color: var(--p-fg);
|
|
257
283
|
display: grid; place-items: center;
|
|
258
284
|
cursor: pointer;
|
|
259
285
|
}
|
|
@@ -297,6 +323,7 @@ const va = `
|
|
|
297
323
|
display: flex; gap: 2px; padding: 2px;
|
|
298
324
|
border-radius: 6px;
|
|
299
325
|
background: var(--p-track);
|
|
326
|
+
border: 1px solid var(--p-line-strong); /* clearer group edge (Ravi, 2026-10-16) */
|
|
300
327
|
}
|
|
301
328
|
.seg [role="radio"] {
|
|
302
329
|
flex: 1 1 0; min-width: 0;
|
|
@@ -313,7 +340,7 @@ const va = `
|
|
|
313
340
|
.seg [role="radio"]:hover { background: var(--p-hover); color: var(--p-fg); }
|
|
314
341
|
.seg [aria-checked="true"] {
|
|
315
342
|
background: var(--p-bg); color: var(--p-fg);
|
|
316
|
-
box-shadow:
|
|
343
|
+
box-shadow: inset 0 0 0 1px var(--p-brand);
|
|
317
344
|
}
|
|
318
345
|
.seg [aria-checked="true"]:hover { background: var(--p-hover); }
|
|
319
346
|
.seg [role="radio"]:focus-visible { outline: 2px solid var(--p-accent); outline-offset: 0; position: relative; }
|
|
@@ -438,7 +465,7 @@ const va = `
|
|
|
438
465
|
box-shadow: inset 0 0 0 2px transparent;
|
|
439
466
|
}
|
|
440
467
|
.swatch:hover .swatch-ring { box-shadow: inset 0 0 0 2px var(--p-line); }
|
|
441
|
-
.swatch[aria-checked="true"] .swatch-ring { box-shadow: inset 0 0 0 2px var(--p-
|
|
468
|
+
.swatch[aria-checked="true"] .swatch-ring { box-shadow: inset 0 0 0 2px var(--p-brand); }
|
|
442
469
|
.swatch:focus-visible .swatch-ring { box-shadow: none; outline: 2px dashed var(--p-accent); outline-offset: -2px; }
|
|
443
470
|
.swatch-dot { width: 22px; height: 22px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.08); }
|
|
444
471
|
.swatch-label { font-size: 12px; color: var(--p-muted); }
|
|
@@ -446,16 +473,60 @@ const va = `
|
|
|
446
473
|
|
|
447
474
|
/* Second header row: the global actions that used to be the footer */
|
|
448
475
|
.actions {
|
|
449
|
-
|
|
450
|
-
|
|
476
|
+
padding: 8px 8px 8px 8px;
|
|
477
|
+
border-top: 1px solid var(--p-line);
|
|
451
478
|
display: flex; align-items: center; gap: 2px;
|
|
452
479
|
color: var(--p-muted); font-size: 12px;
|
|
453
480
|
}
|
|
454
481
|
.spacer { flex: 1; }
|
|
482
|
+
.sep { width: 1px; height: 16px; margin: 0 3px; background: var(--p-line-strong); flex: none; }
|
|
483
|
+
/* Changes: an icon with the count badge on its corner */
|
|
484
|
+
/* Changes: just the count in a circle (Ravi, 2026-10-16); opens the side panel */
|
|
485
|
+
.changes-btn {
|
|
486
|
+
flex: none; height: 28px; padding: 0 3px; border: 0; border-radius: 6px;
|
|
487
|
+
background: transparent; display: grid; place-items: center; cursor: pointer;
|
|
488
|
+
}
|
|
489
|
+
.changes-btn .badge { min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; font-size: 11px; background: var(--p-hover); color: var(--p-muted); }
|
|
490
|
+
.changes-btn.has-changes .badge { background: var(--p-brand); color: #fff; }
|
|
491
|
+
.changes-btn:hover .badge { box-shadow: 0 0 0 2px var(--p-line-strong); }
|
|
492
|
+
.changes-btn[aria-pressed="true"] .badge { box-shadow: 0 0 0 2px var(--p-brand-fg); }
|
|
493
|
+
.changes-btn:focus-visible { outline: 2px solid var(--p-accent); outline-offset: 0; }
|
|
494
|
+
|
|
495
|
+
/* Changes side panel: beside the main panel, on the side away from the edge */
|
|
496
|
+
.side {
|
|
497
|
+
width: 300px; max-width: calc(100vw - var(--off-x) - 440px);
|
|
498
|
+
max-height: calc(100vh - var(--off-y) - 16px);
|
|
499
|
+
max-height: calc(100dvh - var(--off-y) - 16px);
|
|
500
|
+
background: var(--p-bg); border: 1px solid var(--p-line); border-radius: 14px;
|
|
501
|
+
box-shadow: var(--p-shadow);
|
|
502
|
+
display: flex; flex-direction: column; overflow: hidden;
|
|
503
|
+
}
|
|
504
|
+
.side[hidden] { display: none; }
|
|
505
|
+
.side-head {
|
|
506
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
507
|
+
padding: 10px 8px 8px 14px; border-bottom: 1px solid var(--p-line);
|
|
508
|
+
font-size: 13px; font-weight: 600;
|
|
509
|
+
}
|
|
510
|
+
.side .body { flex: 0 1 auto; min-height: 0; } /* as tall as the list; scrolls when the window is shorter */
|
|
511
|
+
.wrap.at-top .side { align-self: flex-start; }
|
|
512
|
+
/* Phones: the one footer row gets compact (no dividers, "Copy", 26px icons) */
|
|
513
|
+
@media (max-width: 440px) {
|
|
514
|
+
.actions .sep, .apply-btn .wide { display: none; }
|
|
515
|
+
.actions .icon-btn { width: 26px; }
|
|
516
|
+
/* icons only; the names stay as tooltips and for screen readers */
|
|
517
|
+
.tab span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
518
|
+
.tab { padding: 9px 0; }
|
|
519
|
+
}
|
|
520
|
+
/* Not enough room beside it (phones): over the main panel instead */
|
|
521
|
+
@media (max-width: 780px) {
|
|
522
|
+
.side { position: absolute; bottom: 0; right: 54px; width: min(360px, calc(100vw - var(--off-x) - 70px)); max-width: none; z-index: 4; }
|
|
523
|
+
.wrap.at-left .side { right: auto; left: 54px; }
|
|
524
|
+
.wrap.at-top .side { bottom: auto; top: 0; }
|
|
525
|
+
}
|
|
455
526
|
.icon-btn:disabled { opacity: .35; cursor: default; background: transparent; }
|
|
456
527
|
.actions .icon-btn, .actions .apply { flex: none; } /* Undo / Redo / Copy prompt keep their size; Changes gives way if space runs out */
|
|
457
528
|
.actions .text-btn { min-width: 0; overflow: hidden; }
|
|
458
|
-
.icon-btn[aria-pressed="true"] { background: var(--p-
|
|
529
|
+
.icon-btn[aria-pressed="true"], .icon-btn[aria-pressed="true"]:hover { background: var(--p-hover); color: var(--p-brand-fg); }
|
|
459
530
|
kbd {
|
|
460
531
|
font: 11px ui-monospace, "SF Mono", Menlo, monospace;
|
|
461
532
|
border: 1px solid var(--p-line); border-radius: 4px;
|
|
@@ -489,9 +560,9 @@ const va = `
|
|
|
489
560
|
color: var(--p-muted); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer;
|
|
490
561
|
}
|
|
491
562
|
.text-btn:hover { background: var(--p-hover); color: var(--p-fg); }
|
|
492
|
-
.text-btn[aria-pressed="true"] { background: var(--p-hover); color: var(--p-fg); }
|
|
563
|
+
.text-btn[aria-pressed="true"] { background: var(--p-hover); color: var(--p-brand-fg); }
|
|
493
564
|
.badge {
|
|
494
|
-
min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
|
|
565
|
+
min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; line-height: 1;
|
|
495
566
|
display: inline-grid; place-items: center;
|
|
496
567
|
background: var(--p-accent); color: var(--p-on-accent);
|
|
497
568
|
font-size: 11px; font-weight: 600;
|
|
@@ -503,20 +574,22 @@ const va = `
|
|
|
503
574
|
.apply-btn {
|
|
504
575
|
height: 28px; padding: 0 12px;
|
|
505
576
|
border: 0; border-radius: 6px;
|
|
506
|
-
|
|
507
|
-
|
|
577
|
+
/* Soft brand button (Ravi, 2026-10-16): was solid white */
|
|
578
|
+
background: color-mix(in srgb, var(--p-brand) 22%, transparent); color: var(--p-brand-fg);
|
|
579
|
+
font: inherit; font-size: 12px; font-weight: 500; cursor: pointer;
|
|
508
580
|
}
|
|
509
581
|
.apply { flex: none; }
|
|
582
|
+
.apply-btn:hover, .apply-menu-btn:hover { background: color-mix(in srgb, var(--p-brand) 32%, transparent); }
|
|
510
583
|
.apply .apply-btn { border-radius: 6px 0 0 6px; display: inline-flex; align-items: center; gap: 6px; padding: 0 10px; white-space: nowrap; }
|
|
511
584
|
.apply-menu-btn {
|
|
512
585
|
width: 22px; height: 28px; padding: 0;
|
|
513
586
|
display: grid; place-items: center;
|
|
514
|
-
border: 0; border-left: 1px solid color-mix(in srgb, var(--p-
|
|
587
|
+
border: 0; border-left: 1px solid color-mix(in srgb, var(--p-brand) 45%, transparent);
|
|
515
588
|
border-radius: 0 6px 6px 0;
|
|
516
|
-
background: var(--p-
|
|
589
|
+
background: color-mix(in srgb, var(--p-brand) 22%, transparent); color: var(--p-brand-fg); cursor: pointer;
|
|
517
590
|
}
|
|
518
591
|
.menu {
|
|
519
|
-
position: absolute; right: 0;
|
|
592
|
+
position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 2; /* opens upwards from the bottom actions */
|
|
520
593
|
min-width: 180px; padding: 4px; display: grid; gap: 2px;
|
|
521
594
|
background: var(--p-bg); border: 1px solid var(--p-line); border-radius: 8px;
|
|
522
595
|
box-shadow: var(--p-shadow);
|
|
@@ -571,6 +644,7 @@ const va = `
|
|
|
571
644
|
}
|
|
572
645
|
.scope-name .muted { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
573
646
|
.scope-target:empty, .warnings:empty { display: none; }
|
|
647
|
+
.scope-note { font-size: 12px; line-height: 1.45; color: var(--p-muted); }
|
|
574
648
|
.muted { color: var(--p-muted); }
|
|
575
649
|
.empty { padding: 14px; color: var(--p-muted); font-size: 12px; line-height: 1.5; }
|
|
576
650
|
|
|
@@ -776,23 +850,26 @@ const va = `
|
|
|
776
850
|
/* ---------- Tabbed layout (Figma: Panel — tabbed layout (proposal), 254:323) ---------- */
|
|
777
851
|
.tabbar {
|
|
778
852
|
display: flex; gap: 2px; padding: 6px;
|
|
779
|
-
border-
|
|
853
|
+
border-bottom: 1px solid var(--p-line); /* tabs on top (Ravi, 2026-10-16) */
|
|
780
854
|
}
|
|
781
855
|
.tab {
|
|
782
856
|
flex: 1 1 0; min-width: 0;
|
|
783
857
|
display: grid; justify-items: center; gap: 3px;
|
|
784
|
-
padding: 7px
|
|
785
|
-
background: transparent; color: var(--p-
|
|
858
|
+
padding: 7px 0 6px; border: 0; border-radius: 8px; /* no side padding: six labels + the Minimise chevron fit 360px */
|
|
859
|
+
background: transparent; color: var(--p-fg);
|
|
786
860
|
font: inherit; font-size: 11px; cursor: pointer;
|
|
787
861
|
}
|
|
788
862
|
.tab:hover { background: var(--p-hover); color: var(--p-fg); }
|
|
789
|
-
.tab[aria-pressed="true"] { background: var(--p-hover); color: var(--p-fg); font-weight: 600; }
|
|
863
|
+
.tab[aria-pressed="true"] { background: var(--p-hover); color: var(--p-brand-fg); font-weight: 600; }
|
|
790
864
|
.tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
791
865
|
|
|
792
866
|
/* Minimised (proposal, not yet in Figma): only the tab bar (and the Compare
|
|
793
867
|
strip while it shows); clicking a tab opens the panel on that tab. */
|
|
794
868
|
.panel.minimised > :not(.tabbar):not(.original-pill) { display: none; }
|
|
795
|
-
.panel.minimised .tabbar { border-
|
|
869
|
+
.panel.minimised .tabbar { border-bottom: 0; }
|
|
870
|
+
.tab-min { flex: none; align-self: center; width: 24px; }
|
|
871
|
+
.tab-min svg { transition: transform .2s cubic-bezier(0.2, 0.8, 0.2, 1); }
|
|
872
|
+
.panel.minimised .tab-min svg { transform: rotate(180deg); }
|
|
796
873
|
.page-fields { gap: 16px; }
|
|
797
874
|
.page-heading { display: flex; align-items: baseline; justify-content: space-between; font-size: 13px; font-weight: 500; color: var(--p-fg); margin-bottom: -6px; }
|
|
798
875
|
.page-heading .muted { font-size: 12px; font-weight: 400; }
|
|
@@ -801,12 +878,13 @@ const va = `
|
|
|
801
878
|
.tiles { display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); gap: 8px; }
|
|
802
879
|
.tile {
|
|
803
880
|
display: grid; justify-items: center; align-content: start; gap: 6px; min-width: 0;
|
|
804
|
-
padding: 8px; border: 1px solid var(--p-line); border-radius: 10px;
|
|
881
|
+
padding: 8px; border: 1px solid var(--p-line-strong); border-radius: 10px;
|
|
805
882
|
background: var(--p-bg); color: var(--p-muted);
|
|
806
883
|
font: inherit; font-size: 11px; text-align: center; cursor: pointer;
|
|
807
884
|
}
|
|
808
885
|
.tile:hover { border-color: var(--p-muted); }
|
|
809
|
-
.tile[aria-checked="true"] { border-color: var(--p-
|
|
886
|
+
.tile[aria-checked="true"] { border-color: var(--p-brand); box-shadow: inset 0 0 0 1px var(--p-brand); color: var(--p-fg); font-weight: 600; }
|
|
887
|
+
.tile.near:not([aria-checked="true"]) { border-style: dashed; border-color: var(--p-brand); } /* between two presets (Figma 275:921) */
|
|
810
888
|
.tile:focus-visible { outline: 2px solid var(--p-accent); outline-offset: 2px; }
|
|
811
889
|
.tile.wide { grid-column: 1 / -1; }
|
|
812
890
|
.tile-text { display: grid; gap: 1px; min-width: 0; max-width: 100%; }
|
|
@@ -831,26 +909,35 @@ const va = `
|
|
|
831
909
|
.sketch i { height: 9px; border-radius: 2px; background: var(--p-line); }
|
|
832
910
|
.sketch i:first-child { background: var(--p-muted); }
|
|
833
911
|
.shape { display: block; }
|
|
834
|
-
|
|
835
|
-
.shape-button
|
|
836
|
-
.shape-button.size-
|
|
837
|
-
.shape-button.size-
|
|
912
|
+
/* Live previews: --pv-* come from the page (panel.js syncPreviews); fallbacks = defaults. */
|
|
913
|
+
.shape-button { width: 52px; height: 24px; background: var(--pv-primary, #4f46e5); }
|
|
914
|
+
.shape-button.size-small { width: 38px; height: 16px; border-radius: min(var(--pv-radius-control, 8px), 8px); }
|
|
915
|
+
.shape-button.size-medium { width: 52px; height: 22px; border-radius: min(var(--pv-radius-control, 8px), 11px); }
|
|
916
|
+
.shape-button.size-large { width: 66px; height: 28px; border-radius: min(var(--pv-radius-control, 8px), 14px); }
|
|
838
917
|
.shape-card { width: 52px; height: 38px; background: var(--p-bg); border: 1px solid var(--p-muted); }
|
|
839
|
-
.shape-card
|
|
840
|
-
.shape-card.border-
|
|
841
|
-
.shape-card.border-thick { border-width: 2px;
|
|
842
|
-
/*
|
|
843
|
-
.shape-button[class*="style-"] { width: 52px; height: 22px; border-radius:
|
|
844
|
-
.shape-button[class*="style-"]::after { content: ""; width: 22px; height: 3px; border-radius: 2px; background: #
|
|
845
|
-
.shape-button.style-solid::after { background: #fff; }
|
|
846
|
-
.shape-button.style-soft { background:
|
|
847
|
-
.shape-button.style-outline { background: transparent; border-color: #
|
|
918
|
+
.shape-card[class*="border-"] { border-radius: min(calc(var(--pv-radius-container, 12px) / 2), 10px); }
|
|
919
|
+
.shape-card.border-none { border-color: transparent; background: var(--p-hover); }
|
|
920
|
+
.shape-card.border-thick { border-width: 2px; }
|
|
921
|
+
/* Button style tiles (Figma 281:1158) */
|
|
922
|
+
.shape-button[class*="style-"] { width: 52px; height: 22px; border-radius: min(var(--pv-radius-control, 8px), 11px); display: grid; place-items: center; border: 1.5px solid transparent; }
|
|
923
|
+
.shape-button[class*="style-"]::after { content: ""; width: 22px; height: 3px; border-radius: 2px; background: color-mix(in srgb, var(--pv-primary, #4f46e5) 55%, white); }
|
|
924
|
+
.shape-button.style-solid::after { background: var(--pv-on-primary, #fff); }
|
|
925
|
+
.shape-button.style-soft { background: color-mix(in srgb, var(--pv-primary, #4f46e5) 32%, transparent); }
|
|
926
|
+
.shape-button.style-outline { background: transparent; border-color: color-mix(in srgb, var(--pv-primary, #4f46e5) 70%, white); }
|
|
848
927
|
.shape-button.style-ghost { background: transparent; }
|
|
849
|
-
|
|
850
|
-
.
|
|
851
|
-
.
|
|
852
|
-
|
|
853
|
-
|
|
928
|
+
/* Card style tiles: a mini card with content lines on a page backdrop (Figma 281:1421) */
|
|
929
|
+
.mini { display: grid; place-items: center; width: 60px; height: 42px; border-radius: 6px; background: var(--p-hover); }
|
|
930
|
+
.mini-card {
|
|
931
|
+
width: 44px; height: 28px; padding: 0 7px; box-sizing: border-box;
|
|
932
|
+
display: grid; align-content: center; gap: 4px;
|
|
933
|
+
border: 1px solid transparent; border-radius: min(calc(var(--pv-radius-container, 12px) / 2), 10px);
|
|
934
|
+
}
|
|
935
|
+
.mini-card i { display: block; width: 22px; height: 3px; border-radius: 2px; background: #a1a1aa; }
|
|
936
|
+
.mini-card i + i { width: 30px; background: #71717a; }
|
|
937
|
+
.mini-card.style-filled { background: #3f3f46; border-color: #52525b; }
|
|
938
|
+
.mini-card.style-outlined { background: transparent; border-color: #a1a1aa; }
|
|
939
|
+
.mini-card.style-elevated { background: #52525b; box-shadow: 0 4px 8px rgb(0 0 0 / 0.7); }
|
|
940
|
+
.mini-card.style-tinted { background: color-mix(in srgb, var(--pv-primary, #6366f1) 30%, transparent); border-color: color-mix(in srgb, var(--pv-primary, #6366f1) 70%, white); }
|
|
854
941
|
.shape-card[class*="shadow-"] { width: 44px; height: 32px; border-radius: 6px; border-color: var(--p-line); margin: 4px 0 6px; }
|
|
855
942
|
.shape-card.shadow-bold { border-color: var(--p-fg); }
|
|
856
943
|
|
|
@@ -901,106 +988,109 @@ const va = `
|
|
|
901
988
|
.summary-swatch { width: 14px; height: 14px; border-radius: 4px; flex: none; }
|
|
902
989
|
.summary-swatch[hidden] { display: none; }
|
|
903
990
|
.summary-row .chev { display: grid; color: var(--p-muted); }
|
|
904
|
-
`,
|
|
905
|
-
dial:
|
|
906
|
-
link:
|
|
907
|
-
reset:
|
|
908
|
-
close:
|
|
909
|
-
chevron:
|
|
991
|
+
`, A = (e, t, n) => `<svg width="${e}" height="${e}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${t}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${n}</svg>`, P = {
|
|
992
|
+
dial: A(20, 1.8, '<circle cx="12" cy="12" r="8"/><path d="M12 12l4-4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2"/>'),
|
|
993
|
+
link: A(15, 2, '<path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1 1"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1-1"/>'),
|
|
994
|
+
reset: A(15, 2, '<path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 3v6h6"/>'),
|
|
995
|
+
close: A(15, 2, '<path d="M18 6L6 18M6 6l12 12"/>'),
|
|
996
|
+
chevron: A(12, 2.4, '<path d="M9 6l6 6-6 6"/>'),
|
|
910
997
|
// Proposals: not yet in the Figma Icon/* set.
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
998
|
+
// Figma: Icon/Cursor (39:313), the header's Pick button.
|
|
999
|
+
pick: A(15, 2, '<path d="M17.202 10.5576C17.6738 10.3035 17.9098 10.1765 17.9694 10.0236C18.0212 9.89097 18.0066 9.74158 17.9301 9.6215C17.8419 9.48312 17.5858 9.40424 17.0737 9.24647L7.05204 6.15943C6.63366 6.03055 6.42447 5.96611 6.28318 6.01796C6.16011 6.06312 6.06312 6.16011 6.01796 6.28317C5.96611 6.42446 6.03055 6.63365 6.15943 7.05204L9.24645 17.0737C9.40422 17.5858 9.4831 17.8419 9.62147 17.9301C9.74156 18.0066 9.89095 18.0212 10.0236 17.9694C10.1764 17.9098 10.3035 17.6738 10.5576 17.202L12.7816 13.0717C12.8193 13.0016 12.8382 12.9666 12.8625 12.9359C12.884 12.9086 12.9086 12.884 12.9359 12.8624C12.9666 12.8382 13.0016 12.8193 13.0717 12.7816L17.202 10.5576Z"/>'),
|
|
1000
|
+
compare: A(15, 2, '<circle cx="12" cy="12" r="9"/><path d="M12 3v18" /><path d="M12 3a9 9 0 0 0 0 18z" fill="currentColor" stroke="none"/>'),
|
|
1001
|
+
undo: A(15, 2, '<path d="M9 14L4 9l5-5"/><path d="M4 9h11a5 5 0 0 1 0 10h-3"/>'),
|
|
1002
|
+
redo: A(15, 2, '<path d="M15 14l5-5-5-5"/><path d="M20 9H9a5 5 0 0 0 0 10h3"/>'),
|
|
1003
|
+
back: A(15, 2, '<path d="M15 18l-6-6 6-6"/>'),
|
|
1004
|
+
down: A(12, 2.4, '<path d="M6 9l6 6 6-6"/>'),
|
|
1005
|
+
minimise: A(15, 2, '<path d="M6 9l6 6 6-6"/>'),
|
|
1006
|
+
// chevron at the end of the tab bar; flips to ˄ when minimised (Ravi, 2026-10-16)
|
|
1007
|
+
changes: A(15, 2, '<path d="M9 6h11M9 12h11M9 18h11"/><path d="M4.5 6h.01M4.5 12h.01M4.5 18h.01"/>'),
|
|
1008
|
+
copy: A(13, 2, '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/>'),
|
|
1009
|
+
warn: A(13, 2, '<path d="M12 3l9.5 17h-19z"/><path d="M12 10v4M12 17.5v.01"/>'),
|
|
920
1010
|
// Launcher: the Panel product mark (Twiddle brand; Figma Logo page › Panel mark; also site/favicon.svg).
|
|
921
1011
|
// A switch knocked out of a disc; knob + two-dot trail. Classes let the Toggle animation move them.
|
|
922
1012
|
panelMark: '<svg class="mark" width="44" height="44" viewBox="0 0 120 120" aria-hidden="true"><path class="mark-disc" fill-rule="evenodd" clip-rule="evenodd" d="M60 120C93.1371 120 120 93.1371 120 60C120 26.8629 93.1371 0 60 0C26.8629 0 0 26.8629 0 60C0 93.1371 26.8629 120 60 120ZM86 76C86 90.3594 74.3594 102 60 102C45.6406 102 34 90.3594 34 76V44C34 29.6406 45.6406 18 60 18C74.3594 18 86 29.6406 86 44V76ZM60 22C72.1503 22 82 31.8497 82 44V76C82 88.1503 72.1503 98 60 98C47.8497 98 38 88.1503 38 76V44C38 31.8497 47.8497 22 60 22Z"/><circle class="mark-t2" cx="60" cy="80.46" r="4.28"/><circle class="mark-t1" cx="60" cy="66.04" r="7.14"/><circle class="mark-knob" cx="60" cy="44" r="11.9"/></svg>',
|
|
923
1013
|
// Tab bar (Figma: Panel — tabbed layout (proposal), Icon/Tab * components)
|
|
924
|
-
tabOverview:
|
|
925
|
-
tabColour:
|
|
926
|
-
tabType:
|
|
927
|
-
tabSpacing:
|
|
928
|
-
tabShape:
|
|
929
|
-
tabShadows:
|
|
930
|
-
sliders:
|
|
1014
|
+
tabOverview: A(18, 1.8, '<circle cx="12" cy="12" r="8"/><path d="M12 12l4-4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2"/>'),
|
|
1015
|
+
tabColour: A(18, 2, '<path d="M12 3c-3.5 4.5-6 7.8-6 11a6 6 0 0 0 12 0c0-3.2-2.5-6.5-6-11z"/>'),
|
|
1016
|
+
tabType: A(18, 2, '<path d="M5 7V5h14v2"/><path d="M12 5v14"/><path d="M9 19h6"/>'),
|
|
1017
|
+
tabSpacing: A(18, 2, '<path d="M4 5v14M20 5v14"/><path d="M8 12h8"/><path d="M10 9l-3 3 3 3M14 9l3 3-3 3"/>'),
|
|
1018
|
+
tabShape: A(18, 2, '<path d="M20 4h-9a7 7 0 0 0-7 7v9"/><path d="M20 10h-5a5 5 0 0 0-5 5v5"/>'),
|
|
1019
|
+
tabShadows: A(18, 2, '<rect x="4" y="4" width="12" height="12" rx="2"/><path d="M20 8v10a2 2 0 0 1-2 2H8"/>'),
|
|
1020
|
+
sliders: A(15, 2, '<path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/>')
|
|
931
1021
|
// Figma: Icon/Sliders (proposal)
|
|
932
|
-
},
|
|
933
|
-
function
|
|
1022
|
+
}, Gn = "twiddle:";
|
|
1023
|
+
function ne(e) {
|
|
934
1024
|
try {
|
|
935
|
-
return localStorage.getItem(
|
|
1025
|
+
return localStorage.getItem(Gn + e);
|
|
936
1026
|
} catch {
|
|
937
1027
|
return null;
|
|
938
1028
|
}
|
|
939
1029
|
}
|
|
940
|
-
function
|
|
1030
|
+
function ce(e, t) {
|
|
941
1031
|
try {
|
|
942
|
-
localStorage.setItem(
|
|
1032
|
+
localStorage.setItem(Gn + e, t);
|
|
943
1033
|
} catch {
|
|
944
1034
|
}
|
|
945
1035
|
}
|
|
946
|
-
const
|
|
947
|
-
function
|
|
1036
|
+
const pe = 44, de = 8, Ba = 4, Yn = { corner: "br", x: 16, y: 16 };
|
|
1037
|
+
function Ua() {
|
|
948
1038
|
try {
|
|
949
|
-
const e = JSON.parse(
|
|
1039
|
+
const e = JSON.parse(ne("position"));
|
|
950
1040
|
if (e && /^[tb][lr]$/.test(e.corner) && isFinite(e.x) && isFinite(e.y)) return e;
|
|
951
1041
|
} catch {
|
|
952
1042
|
}
|
|
953
|
-
return { ...
|
|
1043
|
+
return { ...Yn };
|
|
954
1044
|
}
|
|
955
|
-
function
|
|
956
|
-
const t = Math.max(
|
|
957
|
-
return { corner: e.corner, x: Math.round(Math.min(Math.max(e.x,
|
|
1045
|
+
function bn(e) {
|
|
1046
|
+
const t = Math.max(de, innerWidth - pe - de), n = Math.max(de, innerHeight - pe - de);
|
|
1047
|
+
return { corner: e.corner, x: Math.round(Math.min(Math.max(e.x, de), t)), y: Math.round(Math.min(Math.max(e.y, de), n)) };
|
|
958
1048
|
}
|
|
959
|
-
function
|
|
960
|
-
let n =
|
|
1049
|
+
function Va({ wrapEl: e, launcherEl: t }) {
|
|
1050
|
+
let n = Ua();
|
|
961
1051
|
function a() {
|
|
962
|
-
const d =
|
|
1052
|
+
const d = bn(n), p = d.corner[1] === "l", f = d.corner[0] === "t";
|
|
963
1053
|
Object.assign(e.style, {
|
|
964
1054
|
left: p ? `${d.x}px` : "auto",
|
|
965
1055
|
right: p ? "auto" : `${d.x}px`,
|
|
966
|
-
top:
|
|
967
|
-
bottom:
|
|
968
|
-
}), e.style.setProperty("--off-x", `${d.x}px`), e.style.setProperty("--off-y", `${d.y}px`), e.classList.toggle("at-left", p), e.classList.toggle("at-top",
|
|
1056
|
+
top: f ? `${d.y}px` : "auto",
|
|
1057
|
+
bottom: f ? "auto" : `${d.y}px`
|
|
1058
|
+
}), e.style.setProperty("--off-x", `${d.x}px`), e.style.setProperty("--off-y", `${d.y}px`), e.classList.toggle("at-left", p), e.classList.toggle("at-top", f);
|
|
969
1059
|
}
|
|
970
1060
|
function o(d, p = !0) {
|
|
971
|
-
n =
|
|
1061
|
+
n = bn(d), a(), p && ce("position", JSON.stringify(n));
|
|
972
1062
|
}
|
|
973
|
-
function
|
|
974
|
-
const
|
|
1063
|
+
function r(d, p) {
|
|
1064
|
+
const f = d + pe / 2 < innerWidth / 2, v = p + pe / 2 < innerHeight / 2, u = !v && e.classList.contains("is-open") ? 10 : 0;
|
|
975
1065
|
return {
|
|
976
|
-
corner: (v ? "t" : "b") + (
|
|
977
|
-
x:
|
|
978
|
-
y: v ? p : innerHeight - p -
|
|
1066
|
+
corner: (v ? "t" : "b") + (f ? "l" : "r"),
|
|
1067
|
+
x: f ? d : innerWidth - d - pe,
|
|
1068
|
+
y: v ? p : innerHeight - p - pe - u
|
|
979
1069
|
};
|
|
980
1070
|
}
|
|
981
|
-
let
|
|
1071
|
+
let s = null, i = !1;
|
|
982
1072
|
t.addEventListener("pointerdown", (d) => {
|
|
983
1073
|
if (d.button !== 0) return;
|
|
984
1074
|
const p = t.getBoundingClientRect();
|
|
985
|
-
|
|
1075
|
+
s = { id: d.pointerId, sx: d.clientX, sy: d.clientY, dx: d.clientX - p.left, dy: d.clientY - p.top, moving: !1 };
|
|
986
1076
|
}), t.addEventListener("pointermove", (d) => {
|
|
987
|
-
if (!(!
|
|
988
|
-
if (!
|
|
989
|
-
if (Math.hypot(d.clientX -
|
|
990
|
-
|
|
1077
|
+
if (!(!s || d.pointerId !== s.id)) {
|
|
1078
|
+
if (!s.moving) {
|
|
1079
|
+
if (Math.hypot(d.clientX - s.sx, d.clientY - s.sy) < Ba) return;
|
|
1080
|
+
s.moving = !0, t.setPointerCapture(d.pointerId), e.classList.add("dragging");
|
|
991
1081
|
}
|
|
992
|
-
o(
|
|
1082
|
+
o(r(d.clientX - s.dx, d.clientY - s.dy), !1);
|
|
993
1083
|
}
|
|
994
1084
|
});
|
|
995
1085
|
const l = (d) => {
|
|
996
|
-
!
|
|
1086
|
+
!s || d.pointerId !== s.id || (s.moving && (i = !0, e.classList.remove("dragging"), o(n)), s = null);
|
|
997
1087
|
};
|
|
998
1088
|
return t.addEventListener("pointerup", l), t.addEventListener("pointercancel", l), t.addEventListener("keydown", (d) => {
|
|
999
1089
|
const p = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[d.key];
|
|
1000
1090
|
if (!p || d.altKey || d.metaKey || d.ctrlKey) return;
|
|
1001
1091
|
d.preventDefault();
|
|
1002
|
-
const
|
|
1003
|
-
o({ corner: n.corner, x: n.x + p[0] *
|
|
1092
|
+
const f = d.shiftKey ? 10 : 1, v = n.corner[1] === "l" ? 1 : -1, u = n.corner[0] === "t" ? 1 : -1;
|
|
1093
|
+
o({ corner: n.corner, x: n.x + p[0] * f * v, y: n.y + p[1] * f * u });
|
|
1004
1094
|
}), window.addEventListener("resize", a), a(), {
|
|
1005
1095
|
/** Call from the launcher's click handler: true = ignore this click (it ended a drag). */
|
|
1006
1096
|
consumeClick() {
|
|
@@ -1008,11 +1098,11 @@ function $a({ wrapEl: e, launcherEl: t }) {
|
|
|
1008
1098
|
return i = !1, d;
|
|
1009
1099
|
},
|
|
1010
1100
|
reset() {
|
|
1011
|
-
o({ ...
|
|
1101
|
+
o({ ...Yn });
|
|
1012
1102
|
}
|
|
1013
1103
|
};
|
|
1014
1104
|
}
|
|
1015
|
-
function
|
|
1105
|
+
function ge({ label: e, options: t, onSelect: n }) {
|
|
1016
1106
|
const a = document.createElement("div");
|
|
1017
1107
|
a.className = "seg", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
|
|
1018
1108
|
const o = t.map((l) => {
|
|
@@ -1020,35 +1110,35 @@ function ce({ label: e, options: t, onSelect: n }) {
|
|
|
1020
1110
|
return d.type = "button", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.textContent = l.label, l.title && (d.title = l.title), d.addEventListener("click", () => i(l.value)), d;
|
|
1021
1111
|
});
|
|
1022
1112
|
a.append(...o);
|
|
1023
|
-
let
|
|
1024
|
-
function
|
|
1025
|
-
|
|
1113
|
+
let r = null;
|
|
1114
|
+
function s(l) {
|
|
1115
|
+
r = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
|
|
1026
1116
|
const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
|
|
1027
|
-
o.forEach((p,
|
|
1028
|
-
p.tabIndex =
|
|
1117
|
+
o.forEach((p, f) => {
|
|
1118
|
+
p.tabIndex = f === d ? 0 : -1;
|
|
1029
1119
|
});
|
|
1030
1120
|
}
|
|
1031
1121
|
function i(l, d) {
|
|
1032
|
-
l !==
|
|
1122
|
+
l !== r && (s(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
|
|
1033
1123
|
}
|
|
1034
1124
|
return a.addEventListener("keydown", (l) => {
|
|
1035
1125
|
const d = o.indexOf(l.target);
|
|
1036
1126
|
if (d < 0) return;
|
|
1037
1127
|
const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
|
|
1038
|
-
let
|
|
1039
|
-
if (p)
|
|
1040
|
-
else if (l.key === "Home")
|
|
1041
|
-
else if (l.key === "End")
|
|
1128
|
+
let f;
|
|
1129
|
+
if (p) f = (d + p + o.length) % o.length;
|
|
1130
|
+
else if (l.key === "Home") f = 0;
|
|
1131
|
+
else if (l.key === "End") f = o.length - 1;
|
|
1042
1132
|
else return;
|
|
1043
|
-
l.preventDefault(), i(o[
|
|
1044
|
-
}),
|
|
1133
|
+
l.preventDefault(), i(o[f].dataset.value, !0);
|
|
1134
|
+
}), s(null), { el: a, setValue: s };
|
|
1045
1135
|
}
|
|
1046
|
-
const
|
|
1047
|
-
function*
|
|
1136
|
+
const Xn = /var\(\s*(--twd-[\w-]+)/g, qa = /::?[\w-]+(\([^)]*\))?/g;
|
|
1137
|
+
function* Qe(e) {
|
|
1048
1138
|
for (const t of e)
|
|
1049
|
-
t.selectorText != null && (yield t), t.cssRules && (yield*
|
|
1139
|
+
t.selectorText != null && (yield t), t.cssRules && (yield* Qe(t.cssRules));
|
|
1050
1140
|
}
|
|
1051
|
-
function
|
|
1141
|
+
function Ha() {
|
|
1052
1142
|
const e = [];
|
|
1053
1143
|
for (const t of document.styleSheets) {
|
|
1054
1144
|
if (t.ownerNode?.id === "twiddle-scoped") continue;
|
|
@@ -1058,92 +1148,92 @@ function Ca() {
|
|
|
1058
1148
|
} catch {
|
|
1059
1149
|
continue;
|
|
1060
1150
|
}
|
|
1061
|
-
for (const a of
|
|
1062
|
-
const o = [...a.style.cssText.matchAll(
|
|
1151
|
+
for (const a of Qe(n)) {
|
|
1152
|
+
const o = [...a.style.cssText.matchAll(Xn)].map((r) => r[1]);
|
|
1063
1153
|
o.length && e.push({ selectors: a.selectorText.split(","), vars: o });
|
|
1064
1154
|
}
|
|
1065
1155
|
}
|
|
1066
1156
|
return e;
|
|
1067
1157
|
}
|
|
1068
|
-
function
|
|
1069
|
-
const n = t.replace(
|
|
1158
|
+
function _a(e, t) {
|
|
1159
|
+
const n = t.replace(qa, "").trim() || "*";
|
|
1070
1160
|
try {
|
|
1071
1161
|
return e.matches(n);
|
|
1072
1162
|
} catch {
|
|
1073
1163
|
return !1;
|
|
1074
1164
|
}
|
|
1075
1165
|
}
|
|
1076
|
-
function
|
|
1166
|
+
function Zn(e) {
|
|
1077
1167
|
const t = /* @__PURE__ */ new Set(), n = [];
|
|
1078
|
-
return
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
}), [...(e.getAttribute("style") || "").matchAll(
|
|
1168
|
+
return Ha().forEach(({ selectors: a, vars: o }) => {
|
|
1169
|
+
const r = a.find((s) => _a(e, s));
|
|
1170
|
+
r && (o.forEach((s) => t.add(s)), n.push(r.trim()));
|
|
1171
|
+
}), [...(e.getAttribute("style") || "").matchAll(Xn)].forEach((a) => t.add(a[1])), { names: t, selectors: n };
|
|
1082
1172
|
}
|
|
1083
|
-
const
|
|
1084
|
-
function
|
|
1173
|
+
const Pt = (e) => window.CSS && CSS.escape ? CSS.escape(e) : e;
|
|
1174
|
+
function Ka(e) {
|
|
1085
1175
|
const t = [];
|
|
1086
1176
|
let n = e;
|
|
1087
1177
|
for (; n && n.nodeType === 1 && n !== document.body && n !== document.documentElement; ) {
|
|
1088
|
-
if (n.id && document.querySelectorAll(`#${
|
|
1089
|
-
return t.unshift(`#${
|
|
1090
|
-
const a = n.localName, o = [...n.parentElement.children].filter((
|
|
1178
|
+
if (n.id && document.querySelectorAll(`#${Pt(n.id)}`).length === 1)
|
|
1179
|
+
return t.unshift(`#${Pt(n.id)}`), t.join(" > ");
|
|
1180
|
+
const a = n.localName, o = [...n.parentElement.children].filter((r) => r.localName === a);
|
|
1091
1181
|
t.unshift(o.length > 1 ? `${a}:nth-of-type(${o.indexOf(n) + 1})` : a), n = n.parentElement;
|
|
1092
1182
|
}
|
|
1093
1183
|
return ["body", ...t].join(" > ");
|
|
1094
1184
|
}
|
|
1095
|
-
function
|
|
1185
|
+
function Wa(e, t = Zn(e).selectors) {
|
|
1096
1186
|
const n = e.getAttribute("data-twd");
|
|
1097
1187
|
if (n) return `[data-twd="${n.replace(/"/g, "")}"]`;
|
|
1098
|
-
const a = [...e.classList],
|
|
1099
|
-
return
|
|
1188
|
+
const a = [...e.classList], r = a.find((s) => t.some((i) => new RegExp(`\\.${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(i))) || a[0];
|
|
1189
|
+
return r ? `.${Pt(r)}` : null;
|
|
1100
1190
|
}
|
|
1101
|
-
function
|
|
1102
|
-
const t =
|
|
1191
|
+
function Ga(e) {
|
|
1192
|
+
const t = Zn(e), n = Wa(e, t.selectors), a = (e.getAttribute("aria-label") || e.placeholder || e.textContent || "").trim().replace(/\s+/g, " ");
|
|
1103
1193
|
return {
|
|
1104
1194
|
el: e,
|
|
1105
1195
|
tag: e.localName,
|
|
1106
1196
|
classes: [...e.classList],
|
|
1107
1197
|
text: a.length > 40 ? `${a.slice(0, 40)}…` : a,
|
|
1108
1198
|
tokens: t.names,
|
|
1109
|
-
elementSelector:
|
|
1199
|
+
elementSelector: Ka(e),
|
|
1110
1200
|
classSelector: n,
|
|
1111
1201
|
classCount: n ? document.querySelectorAll(n).length : 0
|
|
1112
1202
|
};
|
|
1113
1203
|
}
|
|
1114
|
-
const
|
|
1115
|
-
function
|
|
1116
|
-
const a =
|
|
1117
|
-
let
|
|
1204
|
+
const Ot = (e) => e.localName + [...e.classList].map((t) => `.${t}`).join(""), Ya = { radius: "px", intensity: "%", base: "px" }, Jn = (e) => e.charAt(0).toUpperCase() + e.slice(1), Xa = (e, t) => typeof t == "number" || /^\d+$/.test(t) ? `${t}${Ya[e] || ""}` : Jn(String(t));
|
|
1205
|
+
function xn(e, t, n) {
|
|
1206
|
+
const a = Wn(() => e.tokens({ [e.id]: t }) || {}), o = Object.keys(a);
|
|
1207
|
+
let r = {};
|
|
1118
1208
|
if (o.length) {
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1209
|
+
const s = n.selector ? document.querySelector(n.selector) : void 0;
|
|
1210
|
+
r = Ra(o, s || void 0);
|
|
1121
1211
|
}
|
|
1122
1212
|
return {
|
|
1123
1213
|
...n,
|
|
1124
1214
|
sectionId: e.id,
|
|
1125
1215
|
title: e.title,
|
|
1126
|
-
settings: Object.entries(t).map(([
|
|
1127
|
-
tokens: o.map((
|
|
1216
|
+
settings: Object.entries(t).map(([s, i]) => ({ key: s, label: Jn(s), value: Xa(s, i), ...e.describe?.(s, i) || {} })),
|
|
1217
|
+
tokens: o.map((s) => ({ name: s, from: r[s] || "", to: a[s] }))
|
|
1128
1218
|
};
|
|
1129
1219
|
}
|
|
1130
|
-
function
|
|
1220
|
+
function wn(e, t) {
|
|
1131
1221
|
const n = [];
|
|
1132
1222
|
return t.forEach((a) => {
|
|
1133
|
-
e[a.id] && n.push(
|
|
1223
|
+
e[a.id] && n.push(xn(a, e[a.id], { kind: "global", selector: null }));
|
|
1134
1224
|
}), Object.entries(e.scopes || {}).forEach(([a, o]) => {
|
|
1135
|
-
const
|
|
1136
|
-
t.forEach((
|
|
1137
|
-
o[
|
|
1225
|
+
const r = Lt(a) ? "class" : "element";
|
|
1226
|
+
t.forEach((s) => {
|
|
1227
|
+
o[s.id] && n.push(xn(s, o[s.id], { kind: r, selector: a }));
|
|
1138
1228
|
});
|
|
1139
1229
|
}), n;
|
|
1140
1230
|
}
|
|
1141
|
-
function
|
|
1142
|
-
const t = (o) => Object.values(o || {}).reduce((
|
|
1143
|
-
return t(a) + Object.values(n || {}).reduce((o,
|
|
1231
|
+
function Za(e) {
|
|
1232
|
+
const t = (o) => Object.values(o || {}).reduce((r, s) => r + Object.keys(s || {}).length, 0), { scopes: n, ...a } = e;
|
|
1233
|
+
return t(a) + Object.values(n || {}).reduce((o, r) => o + t(r), 0);
|
|
1144
1234
|
}
|
|
1145
|
-
const
|
|
1146
|
-
function
|
|
1235
|
+
const ue = (e) => `\`${e}\``, ct = (e) => e.map((t) => ` - ${ue(t.name)}: ${t.from ? `${ue(t.from)} → ` : ""}${ue(t.to)}`), pt = (e) => `${e.title} › ${e.settings.map((t) => `${t.label}: ${t.value}`).join(", ")}`;
|
|
1236
|
+
function Ja(e) {
|
|
1147
1237
|
let t = null;
|
|
1148
1238
|
try {
|
|
1149
1239
|
t = document.querySelector(e);
|
|
@@ -1151,58 +1241,58 @@ function Na(e) {
|
|
|
1151
1241
|
}
|
|
1152
1242
|
if (!t) return "not found on the current page";
|
|
1153
1243
|
const n = (t.getAttribute("aria-label") || t.placeholder || t.textContent || "").trim().replace(/\s+/g, " ").slice(0, 60);
|
|
1154
|
-
return `${
|
|
1244
|
+
return `${ue(Ot(t))}${n ? ` with text “${n}”` : ""}`;
|
|
1155
1245
|
}
|
|
1156
|
-
function
|
|
1246
|
+
function vn(e, t) {
|
|
1157
1247
|
const n = /* @__PURE__ */ new Map();
|
|
1158
1248
|
return e.forEach((a) => {
|
|
1159
1249
|
const o = a[t];
|
|
1160
1250
|
n.has(o) || n.set(o, []), n.get(o).push(a);
|
|
1161
1251
|
}), [...n];
|
|
1162
1252
|
}
|
|
1163
|
-
function
|
|
1253
|
+
function Qa(e, { link: t = location.href, warnings: n = [] } = {}) {
|
|
1164
1254
|
if (!e.length) return "";
|
|
1165
|
-
const a = e.filter((i) => i.kind === "global"), o = e.filter((i) => i.kind === "class"),
|
|
1166
|
-
if (
|
|
1255
|
+
const a = e.filter((i) => i.kind === "global"), o = e.filter((i) => i.kind === "class"), r = e.filter((i) => i.kind === "element"), s = [];
|
|
1256
|
+
if (s.push(
|
|
1167
1257
|
"# Apply design tweaks from Panel",
|
|
1168
1258
|
"",
|
|
1169
1259
|
"I explored design changes to this prototype with Panel (by Twiddle), a tweak panel that overrides the prototype's `--twd-*` CSS custom properties (design tokens) at runtime. Make these changes permanent in the source code.",
|
|
1170
1260
|
"",
|
|
1171
1261
|
`Preview of the tweaked version: ${t}`
|
|
1172
|
-
), a.length && (
|
|
1262
|
+
), a.length && (s.push(
|
|
1173
1263
|
"",
|
|
1174
1264
|
"## 1. Global token changes",
|
|
1175
1265
|
"",
|
|
1176
1266
|
"Update the default value of each token where it is defined (usually a `:root` rule or a `tokens.css` / `twiddle-tokens.css` file). Change the definition, not the places that use it.",
|
|
1177
1267
|
""
|
|
1178
|
-
), a.forEach((i) =>
|
|
1268
|
+
), a.forEach((i) => s.push(`- **${pt(i)}**`, ...ct(i.tokens)))), o.length && (s.push(
|
|
1179
1269
|
"",
|
|
1180
1270
|
`## ${a.length ? 2 : 1}. Component-wide changes`,
|
|
1181
1271
|
"",
|
|
1182
1272
|
"These apply to every element matching the selector. In the CSS rule for that component, redeclare the token, e.g. `.input { --twd-radius-control: 999px; }`. Don't replace the `var(--twd-…)` usages with fixed values.",
|
|
1183
1273
|
""
|
|
1184
|
-
),
|
|
1185
|
-
|
|
1186
|
-
})),
|
|
1274
|
+
), vn(o, "selector").forEach(([i, l]) => {
|
|
1275
|
+
s.push(`- ${ue(i)} (${document.querySelectorAll(i).length} on this page)`), l.forEach((d) => s.push(` - **${pt(d)}**`, ...ct(d.tokens).map((p) => ` ${p}`)));
|
|
1276
|
+
})), r.length) {
|
|
1187
1277
|
const i = 1 + (a.length ? 1 : 0) + (o.length ? 1 : 0);
|
|
1188
|
-
|
|
1278
|
+
s.push(
|
|
1189
1279
|
"",
|
|
1190
1280
|
`## ${i}. Single-element changes`,
|
|
1191
1281
|
"",
|
|
1192
1282
|
"These apply to one element only. Add a modifier class to that element (e.g. `input--pill`) and redeclare the token in a rule for that class. The selectors below are runtime paths to help you find the element; don't copy them into the stylesheet.",
|
|
1193
1283
|
""
|
|
1194
|
-
),
|
|
1195
|
-
|
|
1284
|
+
), vn(r, "selector").forEach(([l, d]) => {
|
|
1285
|
+
s.push(`- ${Ja(l)}, found at ${ue(l)}`), d.forEach((p) => s.push(` - **${pt(p)}**`, ...ct(p.tokens).map((f) => ` ${f}`)));
|
|
1196
1286
|
});
|
|
1197
1287
|
}
|
|
1198
|
-
return n.length &&
|
|
1288
|
+
return n.length && s.push(
|
|
1199
1289
|
"",
|
|
1200
1290
|
"## Contrast to check",
|
|
1201
1291
|
"",
|
|
1202
1292
|
"Panel flagged these colour pairs as below WCAG AA with the new values:",
|
|
1203
1293
|
"",
|
|
1204
1294
|
...n.map((i) => `- ${i.text}`)
|
|
1205
|
-
),
|
|
1295
|
+
), s.push(
|
|
1206
1296
|
"",
|
|
1207
1297
|
"## Constraints",
|
|
1208
1298
|
"",
|
|
@@ -1210,71 +1300,71 @@ function ja(e, { link: t = location.href, warnings: n = [] } = {}) {
|
|
|
1210
1300
|
"- Change only what is listed above.",
|
|
1211
1301
|
"- If a value uses `color-mix()` and the project targets older browsers, ask before converting it.",
|
|
1212
1302
|
"- When done, list the files you changed."
|
|
1213
|
-
),
|
|
1303
|
+
), s.join(`
|
|
1214
1304
|
`);
|
|
1215
1305
|
}
|
|
1216
|
-
const
|
|
1306
|
+
const Ye = 4.5, eo = 3, to = [
|
|
1217
1307
|
["--twd-text", "--twd-bg", "Text on background"],
|
|
1218
1308
|
["--twd-text", "--twd-surface", "Text on surface"],
|
|
1219
1309
|
["--twd-text-muted", "--twd-surface", "Muted text on surface"],
|
|
1220
1310
|
["--twd-on-primary", "--twd-primary", "Text on brand"]
|
|
1221
1311
|
];
|
|
1222
|
-
let
|
|
1223
|
-
function
|
|
1224
|
-
if (!e || (
|
|
1225
|
-
|
|
1226
|
-
const [t, n, a, o] =
|
|
1312
|
+
let Q;
|
|
1313
|
+
function It(e) {
|
|
1314
|
+
if (!e || (Q ||= Object.assign(document.createElement("canvas"), { width: 1, height: 1 }).getContext("2d", { willReadFrequently: !0 }), !Q)) return null;
|
|
1315
|
+
Q.clearRect(0, 0, 1, 1), Q.fillStyle = "#000", Q.fillStyle = e, Q.fillRect(0, 0, 1, 1);
|
|
1316
|
+
const [t, n, a, o] = Q.getImageData(0, 0, 1, 1).data;
|
|
1227
1317
|
return [t, n, a, o / 255];
|
|
1228
1318
|
}
|
|
1229
|
-
function
|
|
1319
|
+
function yn(e) {
|
|
1230
1320
|
const t = document.createElement("i");
|
|
1231
1321
|
t.style.cssText = `position:absolute;visibility:hidden;color:var(${e})`, document.documentElement.appendChild(t);
|
|
1232
1322
|
const n = getComputedStyle(t).color;
|
|
1233
|
-
return t.remove(),
|
|
1323
|
+
return t.remove(), It(n);
|
|
1234
1324
|
}
|
|
1235
|
-
const
|
|
1325
|
+
const kn = ([e, t, n]) => {
|
|
1236
1326
|
const a = (o) => (o /= 255, o <= 0.03928 ? o / 12.92 : ((o + 0.055) / 1.055) ** 2.4);
|
|
1237
1327
|
return 0.2126 * a(e) + 0.7152 * a(t) + 0.0722 * a(n);
|
|
1238
|
-
},
|
|
1239
|
-
function
|
|
1240
|
-
const n =
|
|
1328
|
+
}, Qn = (e, t) => e.slice(0, 3).map((n, a) => n * e[3] + t[a] * (1 - e[3])).concat(1);
|
|
1329
|
+
function ea(e, t) {
|
|
1330
|
+
const n = kn(e[3] < 1 ? Qn(e, t) : e), a = kn(t);
|
|
1241
1331
|
return (Math.max(n, a) + 0.05) / (Math.min(n, a) + 0.05);
|
|
1242
1332
|
}
|
|
1243
|
-
const
|
|
1244
|
-
function
|
|
1333
|
+
const ta = (e) => `${e.toFixed(1)}:1`;
|
|
1334
|
+
function ut() {
|
|
1245
1335
|
const e = [];
|
|
1246
|
-
return
|
|
1247
|
-
const o =
|
|
1248
|
-
if (!o || !
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1336
|
+
return to.forEach(([t, n, a]) => {
|
|
1337
|
+
const o = yn(t), r = yn(n);
|
|
1338
|
+
if (!o || !r || r[3] === 0) return;
|
|
1339
|
+
const s = ea(o, r);
|
|
1340
|
+
s < Ye && e.push({ label: a, ratio: s, needed: Ye, text: `${a}: ${ta(s)} · AA needs ${Ye}` });
|
|
1251
1341
|
}), e;
|
|
1252
1342
|
}
|
|
1253
|
-
function
|
|
1343
|
+
function no(e) {
|
|
1254
1344
|
const t = [];
|
|
1255
1345
|
for (let n = e; n && n.nodeType === 1; n = n.parentElement) {
|
|
1256
|
-
const a =
|
|
1346
|
+
const a = It(getComputedStyle(n).backgroundColor);
|
|
1257
1347
|
if (a && a[3] > 0 && (t.push(a), a[3] >= 1))
|
|
1258
1348
|
break;
|
|
1259
1349
|
}
|
|
1260
|
-
return t.reduceRight((n, a) =>
|
|
1350
|
+
return t.reduceRight((n, a) => Qn(a, n), [255, 255, 255, 1]);
|
|
1261
1351
|
}
|
|
1262
|
-
function
|
|
1352
|
+
function ao(e) {
|
|
1263
1353
|
if (!e.textContent.trim() && !e.placeholder) return null;
|
|
1264
|
-
const t = getComputedStyle(e), n =
|
|
1354
|
+
const t = getComputedStyle(e), n = It(t.color);
|
|
1265
1355
|
if (!n) return null;
|
|
1266
|
-
const a = parseFloat(t.fontSize),
|
|
1267
|
-
return
|
|
1356
|
+
const a = parseFloat(t.fontSize), r = a >= 24 || a >= 18.66 && parseInt(t.fontWeight, 10) >= 700 ? eo : Ye, s = ea(n, no(e));
|
|
1357
|
+
return s < r ? { label: "This element", ratio: s, needed: r, text: `This element’s text: ${ta(s)} · AA needs ${r}` } : null;
|
|
1268
1358
|
}
|
|
1269
|
-
const
|
|
1270
|
-
function
|
|
1359
|
+
const Sn = "twiddle-panel", oo = "0.9.1", $n = "https://panel.twiddle.in", ro = "--twd-radius-control", so = /var\(\s*--twd-/;
|
|
1360
|
+
function io(e) {
|
|
1271
1361
|
return e.style.cssText.split(";").some((t) => {
|
|
1272
1362
|
const n = t.slice(0, t.indexOf(":")).trim();
|
|
1273
|
-
return n && !n.startsWith("--") &&
|
|
1363
|
+
return n && !n.startsWith("--") && so.test(t);
|
|
1274
1364
|
});
|
|
1275
1365
|
}
|
|
1276
|
-
function
|
|
1277
|
-
if (!getComputedStyle(document.documentElement).getPropertyValue(
|
|
1366
|
+
function lo() {
|
|
1367
|
+
if (!getComputedStyle(document.documentElement).getPropertyValue(ro).trim()) return "no-tokens";
|
|
1278
1368
|
let e = !1;
|
|
1279
1369
|
for (const t of document.styleSheets) {
|
|
1280
1370
|
if (t.ownerNode?.id === "twiddle-scoped") continue;
|
|
@@ -1285,11 +1375,11 @@ function Va() {
|
|
|
1285
1375
|
e = !0;
|
|
1286
1376
|
continue;
|
|
1287
1377
|
}
|
|
1288
|
-
for (const a of
|
|
1378
|
+
for (const a of Qe(n)) if (io(a)) return "ok";
|
|
1289
1379
|
}
|
|
1290
1380
|
return document.querySelector('[style*="var(--twd-"]') ? "ok" : e ? "unknown" : "unused";
|
|
1291
1381
|
}
|
|
1292
|
-
function
|
|
1382
|
+
function co() {
|
|
1293
1383
|
const e = /* @__PURE__ */ new Set(), t = (n) => {
|
|
1294
1384
|
for (const a of n.matchAll(/var\(\s*(--twd-[\w-]+)/g)) e.add(a[1]);
|
|
1295
1385
|
};
|
|
@@ -1301,19 +1391,19 @@ function _a() {
|
|
|
1301
1391
|
} catch {
|
|
1302
1392
|
continue;
|
|
1303
1393
|
}
|
|
1304
|
-
for (const o of
|
|
1305
|
-
o.style.cssText.split(";").forEach((
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1394
|
+
for (const o of Qe(a))
|
|
1395
|
+
o.style.cssText.split(";").forEach((r) => {
|
|
1396
|
+
const s = r.slice(0, r.indexOf(":")).trim();
|
|
1397
|
+
s && !s.startsWith("--") && t(r);
|
|
1308
1398
|
});
|
|
1309
1399
|
}
|
|
1310
1400
|
return document.querySelectorAll('[style*="var(--twd-"]').forEach((n) => t(n.getAttribute("style"))), e;
|
|
1311
1401
|
}
|
|
1312
|
-
const
|
|
1313
|
-
{ label: "With a build tool, in your entry file", code: `import '${
|
|
1402
|
+
const En = [
|
|
1403
|
+
{ label: "With a build tool, in your entry file", code: `import '${Sn}/tokens.css';` },
|
|
1314
1404
|
// Pinned to this build's version: unversioned jsDelivr links can lag a release by days, and unpkg once failed for a fresh one.
|
|
1315
|
-
{ label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${
|
|
1316
|
-
],
|
|
1405
|
+
{ label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${Sn}@${oo}/dist/twiddle-tokens.css">` }
|
|
1406
|
+
], po = `Convert this prototype's styles to use the Panel design tokens (the --twd-* CSS custom properties from twiddle-panel), so the panel can tweak them live.
|
|
1317
1407
|
|
|
1318
1408
|
1. Keep the current look. In a :root rule loaded after twiddle-tokens.css, set each token below to the value this project already uses (for example, if buttons use #0f766e, set --twd-primary: #0f766e). Only set the tokens the project actually needs.
|
|
1319
1409
|
2. Replace fixed values in component styles with var(--twd-…). Map each value to the closest token by role:
|
|
@@ -1331,7 +1421,7 @@ const fn = [
|
|
|
1331
1421
|
3. Leave values that don't match a role as they are (layout widths, one-off illustrations, icon sizes). Don't invent new --twd-* tokens.
|
|
1332
1422
|
4. Don't change the layout or how the page looks; after the change it should look the same until someone uses the panel.
|
|
1333
1423
|
5. When done, list the files you changed and any values you left fixed, with the reason.`;
|
|
1334
|
-
function
|
|
1424
|
+
function D(e) {
|
|
1335
1425
|
const t = document.createElement("div");
|
|
1336
1426
|
t.className = "field";
|
|
1337
1427
|
const n = document.createElement("div");
|
|
@@ -1341,246 +1431,268 @@ function U(e) {
|
|
|
1341
1431
|
const o = document.createElement("span");
|
|
1342
1432
|
return o.className = "field-value", o.hidden = !0, n.append(a, o), t.append(n), {
|
|
1343
1433
|
el: t,
|
|
1344
|
-
setValue(
|
|
1345
|
-
o.textContent =
|
|
1434
|
+
setValue(r) {
|
|
1435
|
+
o.textContent = r || "", o.hidden = !r;
|
|
1346
1436
|
},
|
|
1347
|
-
append(...
|
|
1348
|
-
t.append(...
|
|
1437
|
+
append(...r) {
|
|
1438
|
+
t.append(...r);
|
|
1349
1439
|
}
|
|
1350
1440
|
};
|
|
1351
1441
|
}
|
|
1352
|
-
function
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
const
|
|
1356
|
-
const
|
|
1357
|
-
return
|
|
1442
|
+
function uo({ label: e, ends: t, cols: n = 9, rows: a = 5, onInput: o }) {
|
|
1443
|
+
const r = document.createElement("div");
|
|
1444
|
+
r.className = "pad", r.tabIndex = 0, r.setAttribute("role", "group"), r.setAttribute("aria-roledescription", "2D slider");
|
|
1445
|
+
const s = (b, h) => {
|
|
1446
|
+
const w = document.createElement("span");
|
|
1447
|
+
return w.className = `pad-edge ${b}`, w.textContent = h, w.setAttribute("aria-hidden", "true"), w;
|
|
1358
1448
|
};
|
|
1359
|
-
|
|
1449
|
+
r.append(s("top", t.top), s("bottom", t.bottom), s("left", t.left), s("right", t.right));
|
|
1360
1450
|
const i = document.createElement("div");
|
|
1361
1451
|
i.className = "pad-area";
|
|
1362
1452
|
for (let b = 0; b < a; b++)
|
|
1363
1453
|
for (let h = 0; h < n; h++) {
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1454
|
+
const w = document.createElement("i");
|
|
1455
|
+
w.className = "pad-dot", w.style.left = `${h / (n - 1) * 100}%`, w.style.top = `${b / (a - 1) * 100}%`, i.append(w);
|
|
1366
1456
|
}
|
|
1367
1457
|
const l = document.createElement("span");
|
|
1368
|
-
l.className = "pad-knob", i.append(l),
|
|
1458
|
+
l.className = "pad-knob", i.append(l), r.append(i);
|
|
1369
1459
|
let d = 0.5, p = 0.5;
|
|
1370
|
-
const
|
|
1460
|
+
const f = (b, h) => Math.round(Math.min(1, Math.max(0, b)) * (h - 1)) / (h - 1);
|
|
1371
1461
|
function v() {
|
|
1372
1462
|
l.style.left = `${d * 100}%`, l.style.top = `${p * 100}%`;
|
|
1373
1463
|
}
|
|
1374
|
-
function
|
|
1375
|
-
b =
|
|
1464
|
+
function u(b, h, w) {
|
|
1465
|
+
b = w === "x" ? d : f(b, n), h = w === "y" ? p : f(h, a), !(b === d && h === p) && (d = b, p = h, v(), o(d, p));
|
|
1376
1466
|
}
|
|
1377
1467
|
const m = (b) => {
|
|
1378
1468
|
const h = i.getBoundingClientRect();
|
|
1379
|
-
|
|
1469
|
+
u((b.clientX - h.left) / h.width, (b.clientY - h.top) / h.height);
|
|
1380
1470
|
};
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
}),
|
|
1384
|
-
|
|
1471
|
+
r.addEventListener("pointerdown", (b) => {
|
|
1472
|
+
r.setPointerCapture(b.pointerId), r.classList.add("dragging"), m(b);
|
|
1473
|
+
}), r.addEventListener("pointermove", (b) => {
|
|
1474
|
+
r.hasPointerCapture(b.pointerId) && m(b);
|
|
1385
1475
|
});
|
|
1386
1476
|
const x = (b) => {
|
|
1387
|
-
|
|
1477
|
+
r.hasPointerCapture(b.pointerId) && r.releasePointerCapture(b.pointerId), r.classList.remove("dragging");
|
|
1388
1478
|
};
|
|
1389
|
-
return
|
|
1479
|
+
return r.addEventListener("pointerup", x), r.addEventListener("pointercancel", x), r.addEventListener("keydown", (b) => {
|
|
1390
1480
|
const h = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[b.key];
|
|
1391
|
-
h && (b.preventDefault(), h[0] ? f(
|
|
1481
|
+
h && (b.preventDefault(), h[0] ? u(f(d, n) + h[0] / (n - 1), p, "y") : u(d, f(p, a) + h[1] / (a - 1), "x"));
|
|
1392
1482
|
}), {
|
|
1393
|
-
el:
|
|
1483
|
+
el: r,
|
|
1394
1484
|
/** Show a position without telling anyone (used to sync). Off-grid values are shown as they are. */
|
|
1395
|
-
setValue(b, h,
|
|
1396
|
-
d = Math.min(1, Math.max(0, b)), p = Math.min(1, Math.max(0, h)), v(),
|
|
1485
|
+
setValue(b, h, w) {
|
|
1486
|
+
d = Math.min(1, Math.max(0, b)), p = Math.min(1, Math.max(0, h)), v(), r.setAttribute("aria-label", w ? `${e}: ${w}` : e);
|
|
1397
1487
|
}
|
|
1398
1488
|
};
|
|
1399
1489
|
}
|
|
1400
|
-
function
|
|
1401
|
-
const
|
|
1402
|
-
|
|
1490
|
+
function na({ label: e, min: t, max: n, step: a = 1, format: o = String, onInput: r }) {
|
|
1491
|
+
const s = document.createElement("div");
|
|
1492
|
+
s.className = "slider";
|
|
1403
1493
|
const i = document.createElement("input");
|
|
1404
1494
|
i.type = "range", i.min = String(t), i.max = String(n), i.step = String(a), i.setAttribute("aria-label", e);
|
|
1405
1495
|
const l = document.createElement("span");
|
|
1406
|
-
l.className = "slider-value", l.setAttribute("aria-hidden", "true"),
|
|
1496
|
+
l.className = "slider-value", l.setAttribute("aria-hidden", "true"), s.append(i, l);
|
|
1407
1497
|
const d = (p) => {
|
|
1408
|
-
const
|
|
1409
|
-
i.style.setProperty("--fill", `${
|
|
1498
|
+
const f = (Number(i.value) - t) / (n - t) * 100;
|
|
1499
|
+
i.style.setProperty("--fill", `${f}%`), l.textContent = p ?? o(Number(i.value)), i.setAttribute("aria-valuetext", l.textContent);
|
|
1410
1500
|
};
|
|
1411
1501
|
return i.addEventListener("input", () => {
|
|
1412
|
-
d(),
|
|
1502
|
+
d(), r(Number(i.value));
|
|
1413
1503
|
}), {
|
|
1414
|
-
el:
|
|
1504
|
+
el: s,
|
|
1415
1505
|
/** Show a value without reporting it (sync after reset / URL load). */
|
|
1416
|
-
setValue(p,
|
|
1417
|
-
i.value = String(p), d(
|
|
1506
|
+
setValue(p, f) {
|
|
1507
|
+
i.value = String(p), d(f);
|
|
1418
1508
|
}
|
|
1419
1509
|
};
|
|
1420
1510
|
}
|
|
1421
|
-
function
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
const
|
|
1425
|
-
const
|
|
1426
|
-
return
|
|
1511
|
+
function ho({ label: e, options: t, columns: n = 3, className: a = "", onSelect: o }) {
|
|
1512
|
+
const r = document.createElement("div");
|
|
1513
|
+
r.className = `tiles ${a}`.trim(), r.style.setProperty("--cols", n), r.setAttribute("role", "radiogroup"), r.setAttribute("aria-label", e);
|
|
1514
|
+
const s = t.map((p) => {
|
|
1515
|
+
const f = document.createElement("button");
|
|
1516
|
+
return f.type = "button", f.className = "tile", p.wide && f.classList.add("wide"), f.setAttribute("role", "radio"), f.dataset.value = p.value, p.title && (f.title = p.title), f.innerHTML = `${p.preview ? `<span class="tile-preview" aria-hidden="true">${p.preview}</span>` : ""}<span class="tile-text"><span class="tile-label"></span>${p.sub ? '<span class="tile-sub"></span>' : ""}</span>`, f.querySelector(".tile-label").textContent = p.label, p.sub && (f.querySelector(".tile-sub").textContent = p.sub), f.addEventListener("click", () => l(p.value)), f;
|
|
1427
1517
|
});
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
v.tabIndex = f === u ? 0 : -1;
|
|
1518
|
+
r.append(...s);
|
|
1519
|
+
function i(p) {
|
|
1520
|
+
s.forEach((v) => v.setAttribute("aria-checked", String(v.dataset.value === p)));
|
|
1521
|
+
const f = Math.max(0, s.findIndex((v) => v.dataset.value === p));
|
|
1522
|
+
s.forEach((v, u) => {
|
|
1523
|
+
v.tabIndex = u === f ? 0 : -1;
|
|
1435
1524
|
});
|
|
1436
1525
|
}
|
|
1437
|
-
function
|
|
1438
|
-
|
|
1526
|
+
function l(p, f) {
|
|
1527
|
+
i(p), o(p), f && s.find((v) => v.dataset.value === p).focus();
|
|
1528
|
+
}
|
|
1529
|
+
function d(p = []) {
|
|
1530
|
+
s.forEach((f) => f.classList.toggle("near", p.includes(f.dataset.value)));
|
|
1439
1531
|
}
|
|
1440
|
-
return
|
|
1441
|
-
const
|
|
1442
|
-
if (
|
|
1532
|
+
return r.addEventListener("keydown", (p) => {
|
|
1533
|
+
const f = s.indexOf(p.target.closest(".tile"));
|
|
1534
|
+
if (f < 0) return;
|
|
1443
1535
|
const v = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[p.key];
|
|
1444
|
-
let
|
|
1445
|
-
if (v)
|
|
1446
|
-
else if (p.key === "Home")
|
|
1447
|
-
else if (p.key === "End")
|
|
1536
|
+
let u;
|
|
1537
|
+
if (v) u = (f + v + s.length) % s.length;
|
|
1538
|
+
else if (p.key === "Home") u = 0;
|
|
1539
|
+
else if (p.key === "End") u = s.length - 1;
|
|
1448
1540
|
else return;
|
|
1449
|
-
p.preventDefault(),
|
|
1450
|
-
}),
|
|
1541
|
+
p.preventDefault(), l(s[u].dataset.value, !0);
|
|
1542
|
+
}), i(null), { el: r, setValue: i, setNear: d };
|
|
1451
1543
|
}
|
|
1452
|
-
const
|
|
1453
|
-
function
|
|
1544
|
+
const L = (e) => getComputedStyle(document.documentElement).getPropertyValue(e).trim();
|
|
1545
|
+
function Ce(e, t) {
|
|
1454
1546
|
const n = /^(-?\d*\.?\d+)([a-z%]*)$/i.exec(String(e).trim());
|
|
1455
1547
|
if (!n) return null;
|
|
1456
1548
|
const a = parseFloat(n[1]) * t, o = n[2] || "px";
|
|
1457
1549
|
return o === "px" ? `${Math.round(a)}px` : `${+a.toFixed(3)}${o}`;
|
|
1458
1550
|
}
|
|
1459
|
-
const
|
|
1460
|
-
function
|
|
1461
|
-
const p = (
|
|
1462
|
-
if (typeof
|
|
1463
|
-
return { [a]: `${Math.min(Math.max(
|
|
1464
|
-
const m = p(
|
|
1551
|
+
const fo = 999;
|
|
1552
|
+
function aa({ id: e, title: t, hint: n, token: a, uses: o = [], presets: r, max: s, extra: i = [], extraTokens: l = () => ({}), shape: d = "card" }) {
|
|
1553
|
+
const p = (u) => r.find((m) => m.value === u), f = (u) => u >= fo ? "full" : `${u}px`, v = (u) => {
|
|
1554
|
+
if (typeof u.radius == "number" && isFinite(u.radius))
|
|
1555
|
+
return { [a]: `${Math.min(Math.max(u.radius, 0), s)}px` };
|
|
1556
|
+
const m = p(u.corners);
|
|
1465
1557
|
return m ? { [a]: `${m.radius}px` } : {};
|
|
1466
1558
|
};
|
|
1467
1559
|
return {
|
|
1468
1560
|
id: e,
|
|
1469
1561
|
title: t,
|
|
1470
1562
|
hint: n,
|
|
1471
|
-
presets:
|
|
1563
|
+
presets: r,
|
|
1472
1564
|
uses: [a, ...o],
|
|
1473
|
-
tokens(
|
|
1474
|
-
const m =
|
|
1565
|
+
tokens(u) {
|
|
1566
|
+
const m = u[e] || {};
|
|
1475
1567
|
return { ...v(m), ...l(m) };
|
|
1476
1568
|
},
|
|
1477
1569
|
// One render function per knob, so a layout can place them separately.
|
|
1478
1570
|
// Each takes an optional label (e.g. "Buttons" on the Corners page).
|
|
1479
1571
|
parts: {
|
|
1480
|
-
corners(
|
|
1481
|
-
const b =
|
|
1572
|
+
corners(u, m, { label: x = "Corners" } = {}) {
|
|
1573
|
+
const b = D(x), h = ge({
|
|
1482
1574
|
label: `${t} corners`,
|
|
1483
|
-
options:
|
|
1484
|
-
onSelect: (
|
|
1485
|
-
}),
|
|
1575
|
+
options: r.map(($) => ({ value: $.value, label: $.label, title: f($.radius) })),
|
|
1576
|
+
onSelect: ($) => m.setSection(e, { corners: $, radius: null })
|
|
1577
|
+
}), w = na({
|
|
1486
1578
|
label: `${t} corner radius`,
|
|
1487
1579
|
min: 0,
|
|
1488
|
-
max:
|
|
1489
|
-
format:
|
|
1490
|
-
onInput: (
|
|
1580
|
+
max: s,
|
|
1581
|
+
format: f,
|
|
1582
|
+
onInput: ($) => m.setSection(e, { radius: $, corners: null })
|
|
1491
1583
|
});
|
|
1492
|
-
b.append(h.el,
|
|
1493
|
-
const
|
|
1494
|
-
const
|
|
1495
|
-
if (h.setValue(
|
|
1496
|
-
|
|
1584
|
+
b.append(h.el, w.el), u.append(b.el);
|
|
1585
|
+
const S = () => {
|
|
1586
|
+
const $ = parseFloat((m.readToken || L)(a)), K = m.getState()[e] || {}, Z = p(K.corners) || r.find((Le) => Le.radius === $);
|
|
1587
|
+
if (h.setValue(Z ? Z.value : null), isNaN($)) {
|
|
1588
|
+
w.setValue(0, "—"), b.setValue("");
|
|
1497
1589
|
return;
|
|
1498
1590
|
}
|
|
1499
|
-
|
|
1591
|
+
w.setValue(Math.min($, s), f($)), b.setValue(f($));
|
|
1500
1592
|
};
|
|
1501
|
-
|
|
1593
|
+
S(), m.onChange(S);
|
|
1502
1594
|
},
|
|
1503
|
-
...Object.fromEntries(i.map((
|
|
1595
|
+
...Object.fromEntries(i.map((u) => [u.key, (m, x, { label: b } = {}) => m.append(Me(x, { section: e, ...u, label: b || u.label }))])),
|
|
1504
1596
|
// Tabbed layout: presets as tiles showing the real rounding.
|
|
1505
|
-
cornerTiles(
|
|
1597
|
+
cornerTiles(u, m, { label: x = "Corners" } = {}) {
|
|
1506
1598
|
const b = (h) => `<span class="shape shape-${d}" style="border-radius:${Math.min(h, d === "button" ? 12 : 16)}px"></span>`;
|
|
1507
|
-
|
|
1599
|
+
u.append(_(m, {
|
|
1508
1600
|
section: e,
|
|
1509
1601
|
key: "corners",
|
|
1510
1602
|
label: x,
|
|
1511
|
-
columns:
|
|
1512
|
-
options:
|
|
1603
|
+
columns: r.length,
|
|
1604
|
+
options: r.map((h) => ({ value: h.value, label: h.label, title: f(h.radius), preview: b(h.radius) })),
|
|
1513
1605
|
current: (h) => {
|
|
1514
|
-
const
|
|
1515
|
-
if (
|
|
1516
|
-
|
|
1517
|
-
|
|
1606
|
+
const w = h[e] || {};
|
|
1607
|
+
if (w.radius != null) return null;
|
|
1608
|
+
if (p(w.corners)) return w.corners;
|
|
1609
|
+
if (he(h, a)) return null;
|
|
1610
|
+
const S = parseFloat((m.readToken || L)(a));
|
|
1611
|
+
return r.find(($) => $.radius === S)?.value || null;
|
|
1518
1612
|
},
|
|
1519
1613
|
select: (h) => m.setSection(e, { corners: h, radius: null }),
|
|
1520
|
-
valueText: () => {
|
|
1521
|
-
const
|
|
1522
|
-
|
|
1614
|
+
valueText: (h, w) => {
|
|
1615
|
+
const S = parseFloat((m.readToken || L)(a));
|
|
1616
|
+
if (isNaN(S)) return "";
|
|
1617
|
+
const $ = w[e] || {};
|
|
1618
|
+
return f(S) + Cn(w, a, $.radius != null ? "custom" : p($.corners)?.label);
|
|
1523
1619
|
}
|
|
1524
1620
|
}));
|
|
1525
1621
|
},
|
|
1526
|
-
...Object.fromEntries(i.filter((
|
|
1622
|
+
...Object.fromEntries(i.filter((u) => u.preview).map((u) => [`${u.key}Tiles`, (m, x, { label: b } = {}) => m.append(_(x, {
|
|
1527
1623
|
section: e,
|
|
1528
|
-
key:
|
|
1529
|
-
label: b ||
|
|
1530
|
-
fallback:
|
|
1531
|
-
columns:
|
|
1532
|
-
options:
|
|
1624
|
+
key: u.key,
|
|
1625
|
+
label: b || u.label,
|
|
1626
|
+
fallback: u.fallback,
|
|
1627
|
+
columns: u.columns || u.options.length,
|
|
1628
|
+
options: u.options.map((h) => ({ ...h, preview: u.preview(h.value) })),
|
|
1629
|
+
...u.token && {
|
|
1630
|
+
current: (h) => (h[e] || {})[u.key] || (he(h, u.token) ? null : u.fallback || null),
|
|
1631
|
+
valueText: (h, w) => {
|
|
1632
|
+
if (!he(w, u.token)) return "";
|
|
1633
|
+
const S = u.options.find(($) => $.value === (w[e] || {})[u.key]);
|
|
1634
|
+
return `${(x.readToken || L)(u.token)}${Cn(w, u.token, S && S.label)}`;
|
|
1635
|
+
}
|
|
1636
|
+
},
|
|
1637
|
+
// A knob can bring its own rules (e.g. Cards › Fill reads "from Style").
|
|
1638
|
+
...u.current && { current: (h) => u.current(h[e] || {}) },
|
|
1639
|
+
...u.valueText && { valueText: (h, w) => u.valueText(w[e] || {}, x.readToken || L) },
|
|
1640
|
+
...u.select && { select: (h) => x.setSection(e, u.select(h)) }
|
|
1533
1641
|
}))]))
|
|
1534
1642
|
},
|
|
1535
|
-
render(
|
|
1536
|
-
Object.entries(this.parts).filter(([x]) => !x.endsWith("Tiles")).forEach(([, x]) => x(
|
|
1643
|
+
render(u, m) {
|
|
1644
|
+
Object.entries(this.parts).filter(([x]) => !x.endsWith("Tiles")).forEach(([, x]) => x(u, m));
|
|
1537
1645
|
}
|
|
1538
1646
|
};
|
|
1539
1647
|
}
|
|
1540
|
-
function
|
|
1541
|
-
const i =
|
|
1648
|
+
function Me(e, { section: t, key: n, label: a, options: o, fallback: r, valueText: s }) {
|
|
1649
|
+
const i = D(a), l = ge({
|
|
1542
1650
|
label: a,
|
|
1543
1651
|
options: o,
|
|
1544
1652
|
onSelect: (p) => e.setSection(t, { [n]: p })
|
|
1545
1653
|
});
|
|
1546
1654
|
i.append(l.el);
|
|
1547
1655
|
const d = () => {
|
|
1548
|
-
const p = (e.getState()[t] || {})[n] ||
|
|
1549
|
-
l.setValue(p), i.setValue(
|
|
1656
|
+
const p = (e.getState()[t] || {})[n] || r;
|
|
1657
|
+
l.setValue(p), i.setValue(s ? s(p) : "");
|
|
1550
1658
|
};
|
|
1551
1659
|
return d(), e.onChange(d), i.el;
|
|
1552
1660
|
}
|
|
1553
|
-
function
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
const
|
|
1557
|
-
const u =
|
|
1558
|
-
|
|
1661
|
+
function go(e, { section: t, key: n, label: a, min: o, max: r, fallback: s, format: i, note: l, value: d }) {
|
|
1662
|
+
const p = D(a), f = na({ label: a, min: o, max: r, format: i, onInput: (u) => e.setSection(t, { [n]: u }) });
|
|
1663
|
+
p.append(f.el);
|
|
1664
|
+
const v = () => {
|
|
1665
|
+
const u = e.getState(), m = d ? d(u) : (u[t] || {})[n];
|
|
1666
|
+
f.setValue(typeof m == "number" ? m : s), l && (p.el.querySelector(".field-label").textContent = `${a}${l(u) ? ` · ${l(u)}` : ""}`);
|
|
1559
1667
|
};
|
|
1560
|
-
return
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1668
|
+
return v(), e.onChange(v), p.el;
|
|
1669
|
+
}
|
|
1670
|
+
let oa = () => ({});
|
|
1671
|
+
const mo = (e) => {
|
|
1672
|
+
oa = e;
|
|
1673
|
+
}, he = (e, t) => Object.prototype.hasOwnProperty.call(oa(e), t), Cn = (e, t, n) => he(e, t) ? n ? ` · ${n}, adjusted by Character` : " · from Character" : "";
|
|
1674
|
+
function _(e, { section: t, key: n, label: a, options: o, columns: r = 3, className: s = "", fallback: i, valueText: l, current: d, select: p, near: f }) {
|
|
1675
|
+
const v = D(a), u = ho({
|
|
1564
1676
|
label: a,
|
|
1565
1677
|
options: o,
|
|
1566
|
-
columns:
|
|
1567
|
-
className:
|
|
1568
|
-
onSelect: (
|
|
1678
|
+
columns: r,
|
|
1679
|
+
className: s,
|
|
1680
|
+
onSelect: (x) => p ? p(x) : e.setSection(t, { [n]: x })
|
|
1569
1681
|
});
|
|
1570
|
-
|
|
1571
|
-
const
|
|
1572
|
-
const
|
|
1573
|
-
|
|
1682
|
+
v.append(u.el);
|
|
1683
|
+
const m = () => {
|
|
1684
|
+
const x = e.getState(), b = d ? d(x) : (x[t] || {})[n] || i || null;
|
|
1685
|
+
u.setValue(b), u.setNear(f ? f(x) : []), v.setValue(l ? l(b, x) : "");
|
|
1574
1686
|
};
|
|
1575
|
-
return
|
|
1687
|
+
return m(), e.onChange(m), v.el;
|
|
1576
1688
|
}
|
|
1577
|
-
const
|
|
1689
|
+
const Mn = (e, t) => t.map((n) => `${e}${n}`), ra = {
|
|
1578
1690
|
fonts: { label: "Tokens", tokens: ["font-heading", "font-body"], kind: "text" },
|
|
1579
|
-
type: { label: "Tokens", tokens:
|
|
1691
|
+
type: { label: "Tokens", tokens: Mn("text-", ["xs", "sm", "base", "lg", "xl", "2xl", "3xl"]), kind: "length" },
|
|
1580
1692
|
leading: { label: "Line height", tokens: ["leading-tight", "leading-normal"], kind: "number" },
|
|
1581
1693
|
weights: { label: "Weights", tokens: ["weight-heading", "weight-strong"], kind: "number" },
|
|
1582
1694
|
colours: { label: "Tokens", tokens: ["bg", "surface", "surface-raised", "text", "text-muted", "border", "primary", "primary-hover", "on-primary", "focus", "success", "warning", "danger"], kind: "colour" },
|
|
1583
|
-
spacing: { label: "Steps", tokens:
|
|
1695
|
+
spacing: { label: "Steps", tokens: Mn("space-", [1, 2, 3, 4, 5, 6, 7, 8]), kind: "length", bar: 64 },
|
|
1584
1696
|
controls: { label: "Controls", tokens: ["control-height", "control-pad-x"], kind: "length" },
|
|
1585
1697
|
corners: { label: "Tokens", tokens: ["radius-control", "radius-container"], kind: "length" },
|
|
1586
1698
|
borders: { label: "Tokens", tokens: ["border-width", "border-width-container"], kind: "length" },
|
|
@@ -1588,124 +1700,138 @@ const gn = (e, t) => t.map((n) => `${e}${n}`), Kn = {
|
|
|
1588
1700
|
buttonStyle: { label: "Button", tokens: ["button-bg", "button-fg", "button-border", "button-hover"], kind: "colour" },
|
|
1589
1701
|
cardStyle: { label: "Card", tokens: ["card-bg", "card-border"], kind: "colour" },
|
|
1590
1702
|
cardShadow: { label: "Card shadow", tokens: ["card-shadow"], kind: "text" }
|
|
1591
|
-
},
|
|
1592
|
-
function
|
|
1703
|
+
}, Y = (e) => `--twd-${e}`, ht = (e, t) => Object.prototype.hasOwnProperty.call(e.getState().custom || {}, t), $e = (e, t) => ra[t].tokens.some((n) => (e.custom || {})[n] != null);
|
|
1704
|
+
function bo(e, t) {
|
|
1593
1705
|
const n = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
|
|
1594
1706
|
return !n || t === "text" || t === "colour" ? null : n[2] === "rem" ? { n: +(parseFloat(n[1]) * 16).toFixed(1), unit: "px" } : { n: +parseFloat(n[1]).toFixed(3), unit: n[2] || (t === "length" ? "px" : "") };
|
|
1595
1707
|
}
|
|
1596
|
-
function
|
|
1708
|
+
function xo(e, t) {
|
|
1597
1709
|
const n = e.trim();
|
|
1598
1710
|
return n ? t === "length" && /^-?\d*\.?\d+$/.test(n) ? `${n}px` : n : null;
|
|
1599
1711
|
}
|
|
1600
|
-
function
|
|
1712
|
+
function wo(e, t, n, a) {
|
|
1601
1713
|
const o = document.createElement("div");
|
|
1602
1714
|
o.className = `token-row kind-${n}`, o.innerHTML = `
|
|
1603
|
-
<code class="token-name" title="${
|
|
1715
|
+
<code class="token-name" title="${Y(t)}">${t}</code>
|
|
1604
1716
|
${a ? '<span class="token-bar"><span></span></span>' : '<span class="token-gap"></span>'}
|
|
1605
1717
|
<span class="custom-tag" hidden>Custom</span>
|
|
1606
1718
|
<label class="token-value">
|
|
1607
1719
|
${n === "colour" ? '<span class="token-swatch"></span>' : ""}
|
|
1608
|
-
<input type="text" spellcheck="false" aria-label="${
|
|
1720
|
+
<input type="text" spellcheck="false" aria-label="${Y(t)}" ${n === "length" || n === "number" ? 'inputmode="decimal"' : ""}>
|
|
1609
1721
|
<span class="token-unit"></span>
|
|
1610
1722
|
</label>
|
|
1611
|
-
<button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${
|
|
1723
|
+
<button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${Y(t)}" hidden>${P.reset}</button>
|
|
1612
1724
|
`;
|
|
1613
|
-
const
|
|
1614
|
-
let
|
|
1725
|
+
const r = o.querySelector("input"), s = o.querySelector(".token-unit"), i = o.querySelector(".token-bar span"), l = o.querySelector(".token-swatch"), d = o.querySelector(".custom-tag"), p = o.querySelector(".token-reset");
|
|
1726
|
+
let f = "";
|
|
1615
1727
|
const v = () => {
|
|
1616
|
-
let m =
|
|
1617
|
-
|
|
1618
|
-
const x =
|
|
1728
|
+
let m = r.value;
|
|
1729
|
+
f && /^-?\d*\.?\d+$/.test(m.trim()) && (m = `${m.trim()}${f}`);
|
|
1730
|
+
const x = xo(m, n), b = L(Y(t));
|
|
1619
1731
|
if (x === null) {
|
|
1620
|
-
|
|
1732
|
+
ht(e, t) ? e.setSection("custom", { [t]: null }) : u();
|
|
1621
1733
|
return;
|
|
1622
1734
|
}
|
|
1623
|
-
if (x === b && !
|
|
1735
|
+
if (x === b && !ht(e, t)) return u();
|
|
1624
1736
|
e.setSection("custom", { [t]: x });
|
|
1625
1737
|
};
|
|
1626
|
-
|
|
1627
|
-
m.key === "Enter" && (m.preventDefault(),
|
|
1738
|
+
r.addEventListener("change", v), r.addEventListener("blur", () => u()), r.addEventListener("keydown", (m) => {
|
|
1739
|
+
m.key === "Enter" && (m.preventDefault(), r.blur()), m.key === "Escape" && (m.stopPropagation(), u(), r.blur());
|
|
1628
1740
|
}), p.addEventListener("click", () => e.setSection("custom", { [t]: null }));
|
|
1629
|
-
function
|
|
1630
|
-
const m =
|
|
1631
|
-
|
|
1741
|
+
function u() {
|
|
1742
|
+
const m = L(Y(t)), x = ht(e, t), b = bo(m, n);
|
|
1743
|
+
f = b ? b.unit : "", document.activeElement !== r && o.getRootNode().activeElement !== r && (r.value = b ? String(b.n) : m), r.title = m, s.textContent = f, s.hidden = !f, i && (i.style.width = `${Math.max(2, Math.min(100, (b ? b.n : 0) / a * 100))}%`), l && (l.style.background = m), o.classList.toggle("edited", x), d.hidden = !x, p.hidden = !x;
|
|
1632
1744
|
}
|
|
1633
|
-
return
|
|
1745
|
+
return u(), e.onChange(u), o;
|
|
1634
1746
|
}
|
|
1635
|
-
const
|
|
1747
|
+
const vo = {
|
|
1636
1748
|
id: "custom",
|
|
1637
1749
|
title: "Custom tokens",
|
|
1638
1750
|
hint: "Any token, set to a fixed value",
|
|
1639
1751
|
uses: [],
|
|
1640
1752
|
// not offered for a single picked element (yet)
|
|
1641
1753
|
tokens(e) {
|
|
1642
|
-
return Object.fromEntries(Object.entries(e.custom || {}).map(([t, n]) => [
|
|
1754
|
+
return Object.fromEntries(Object.entries(e.custom || {}).map(([t, n]) => [Y(t), n]));
|
|
1643
1755
|
},
|
|
1644
|
-
describe: (e, t) => ({ label:
|
|
1756
|
+
describe: (e, t) => ({ label: Y(e), value: String(t) }),
|
|
1645
1757
|
// One part per Edit page group, e.g. parts.spacing → rows for --twd-space-1 … 8.
|
|
1646
|
-
parts: Object.fromEntries(Object.entries(
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
const
|
|
1650
|
-
|
|
1758
|
+
parts: Object.fromEntries(Object.entries(ra).map(([e, t]) => [e, (n, a, { label: o = t.label } = {}) => {
|
|
1759
|
+
const r = D(o);
|
|
1760
|
+
r.el.classList.add("token-field");
|
|
1761
|
+
const s = t.tokens[0], i = t.tokens[t.tokens.length - 1];
|
|
1762
|
+
r.setValue(t.tokens.length > 2 && /-\w+$/.test(s) ? `${Y(s)} … ${i.split("-").pop()}` : "");
|
|
1651
1763
|
const l = document.createElement("div");
|
|
1652
|
-
l.className = "token-rows", t.tokens.forEach((d) => l.append(
|
|
1764
|
+
l.className = "token-rows", t.tokens.forEach((d) => l.append(wo(a, d, t.kind, t.bar))), r.append(l), n.append(r.el);
|
|
1653
1765
|
}]))
|
|
1654
|
-
},
|
|
1655
|
-
function
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
const
|
|
1766
|
+
}, X = { compact: 0.75, regular: 1, comfy: 1.25 }, jt = [0.75, 1.25], Tn = [1, 2, 3, 4, 5, 6, 7, 8].map((e) => `--twd-space-${e}`);
|
|
1767
|
+
function sa(e) {
|
|
1768
|
+
const t = Object.entries(X).sort((a, o) => a[1] - o[1]);
|
|
1769
|
+
if (t.some(([, a]) => a === e)) return [];
|
|
1770
|
+
const n = t.findIndex(([, a]) => a > e);
|
|
1771
|
+
return n > 0 ? [t[n - 1][0], t[n][0]] : [];
|
|
1772
|
+
}
|
|
1773
|
+
const ft = (e) => e[0].toUpperCase() + e.slice(1);
|
|
1774
|
+
function Dt(e) {
|
|
1775
|
+
const t = Object.keys(X).find((a) => X[a] === e);
|
|
1776
|
+
if (t) return ft(t);
|
|
1777
|
+
const n = sa(e);
|
|
1778
|
+
return n.length ? `Between ${ft(n[0])} and ${ft(n[1])}` : e > 1 ? "Comfy" : "Compact";
|
|
1779
|
+
}
|
|
1780
|
+
function H(e = {}) {
|
|
1781
|
+
return typeof e.factor == "number" && isFinite(e.factor) ? Math.min(jt[1], Math.max(jt[0], e.factor)) : X[e.spacing] || 1;
|
|
1782
|
+
}
|
|
1783
|
+
const gt = (e) => `${Math.round(e * 100)}%`, yo = {
|
|
1659
1784
|
id: "density",
|
|
1660
1785
|
title: "Density",
|
|
1661
1786
|
hint: "Compact · regular · comfortable",
|
|
1662
|
-
uses:
|
|
1787
|
+
uses: Tn,
|
|
1663
1788
|
tokens(e) {
|
|
1664
|
-
const t =
|
|
1789
|
+
const t = H(e.density);
|
|
1665
1790
|
if (t === 1) return {};
|
|
1666
1791
|
const n = {};
|
|
1667
|
-
return
|
|
1668
|
-
const o =
|
|
1792
|
+
return Tn.forEach((a) => {
|
|
1793
|
+
const o = Ce(L(a), t);
|
|
1669
1794
|
o && (n[a] = o);
|
|
1670
1795
|
}), n;
|
|
1671
1796
|
},
|
|
1672
1797
|
/** How the Changes list and prompt describe a setting. */
|
|
1673
|
-
describe: (e, t) => e === "factor" ? { label: "Spacing", value:
|
|
1798
|
+
describe: (e, t) => e === "factor" ? { label: "Spacing", value: gt(t) } : null,
|
|
1674
1799
|
parts: {
|
|
1675
1800
|
// Tabbed layout: presets as tiles with a tiny layout sketch.
|
|
1676
1801
|
spacingTiles(e, t, { label: n = "Density" } = {}) {
|
|
1677
|
-
const a = (o,
|
|
1678
|
-
e.append(
|
|
1802
|
+
const a = (o, r) => `<span class="sketch" style="padding:${r}px;gap:${o}px"><i></i><i></i><i></i></span>`;
|
|
1803
|
+
e.append(_(t, {
|
|
1679
1804
|
section: "density",
|
|
1680
1805
|
key: "spacing",
|
|
1681
1806
|
label: n,
|
|
1682
1807
|
options: [{ value: "compact", label: "Compact", preview: a(2, 5) }, { value: "regular", label: "Regular", preview: a(4, 8) }, { value: "comfy", label: "Comfy", preview: a(6, 11) }],
|
|
1683
1808
|
current: (o) => {
|
|
1684
|
-
const
|
|
1685
|
-
return Object.keys(
|
|
1809
|
+
const r = H(o.density);
|
|
1810
|
+
return Object.keys(X).find((s) => X[s] === r) || null;
|
|
1686
1811
|
},
|
|
1687
1812
|
select: (o) => t.setSection("density", { spacing: o, factor: null }),
|
|
1688
|
-
|
|
1813
|
+
near: (o) => $e(o, "spacing") ? [] : sa(H(o.density)),
|
|
1814
|
+
valueText: (o, r) => $e(r, "spacing") ? "Custom" : o ? "" : `${Dt(H(r.density))} · ${gt(H(r.density))}`
|
|
1689
1815
|
}));
|
|
1690
1816
|
},
|
|
1691
1817
|
spacing(e, t, { label: n = "Spacing" } = {}) {
|
|
1692
|
-
const a =
|
|
1818
|
+
const a = D(n), o = ge({
|
|
1693
1819
|
label: n,
|
|
1694
1820
|
options: [{ value: "compact", label: "Compact" }, { value: "regular", label: "Regular" }, { value: "comfy", label: "Comfy" }],
|
|
1695
|
-
onSelect: (
|
|
1821
|
+
onSelect: (s) => t.setSection("density", { spacing: s, factor: null })
|
|
1696
1822
|
});
|
|
1697
1823
|
a.append(o.el);
|
|
1698
|
-
const
|
|
1699
|
-
const
|
|
1700
|
-
o.setValue(l || null), a.setValue(
|
|
1824
|
+
const r = () => {
|
|
1825
|
+
const s = t.getState().density || {}, i = H(s), l = Object.keys(X).find((d) => X[d] === i);
|
|
1826
|
+
o.setValue(l || null), a.setValue($e(t.getState(), "spacing") ? "Custom" : l ? "" : gt(i));
|
|
1701
1827
|
};
|
|
1702
|
-
|
|
1828
|
+
r(), t.onChange(r), e.append(a.el);
|
|
1703
1829
|
}
|
|
1704
1830
|
},
|
|
1705
1831
|
render(e, t) {
|
|
1706
1832
|
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
1707
1833
|
}
|
|
1708
|
-
},
|
|
1834
|
+
}, ko = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 6l4 4M4 20l1-4 9.5-9.5a2.1 2.1 0 013 3L8 19l-4 1z"/></svg>', So = [
|
|
1709
1835
|
[50, "white", 10],
|
|
1710
1836
|
[100, "white", 20],
|
|
1711
1837
|
[200, "white", 36],
|
|
@@ -1717,35 +1843,35 @@ const ot = (e) => `${Math.round(e * 100)}%`, to = {
|
|
|
1717
1843
|
[800, "black", 58],
|
|
1718
1844
|
[900, "black", 45],
|
|
1719
1845
|
[950, "black", 30]
|
|
1720
|
-
],
|
|
1846
|
+
], mt = (e) => {
|
|
1721
1847
|
let t = String(e).trim().replace(/^#/, "");
|
|
1722
1848
|
return /^[0-9a-f]{3}$/i.test(t) && (t = t.split("").map((n) => n + n).join("")), /^[0-9a-f]{6}$/i.test(t) ? `#${t.toUpperCase()}` : null;
|
|
1723
1849
|
};
|
|
1724
|
-
function
|
|
1850
|
+
function $o({ label: e, onInput: t }) {
|
|
1725
1851
|
const n = document.createElement("div");
|
|
1726
1852
|
n.className = "colour";
|
|
1727
1853
|
const a = document.createElement("div");
|
|
1728
1854
|
a.className = "colour-field";
|
|
1729
1855
|
const o = document.createElement("input");
|
|
1730
1856
|
o.type = "color", o.className = "colour-picker", o.tabIndex = -1, o.setAttribute("aria-hidden", "true");
|
|
1731
|
-
const
|
|
1732
|
-
|
|
1733
|
-
const
|
|
1734
|
-
|
|
1857
|
+
const r = document.createElement("button");
|
|
1858
|
+
r.type = "button", r.className = "colour-swatch", r.setAttribute("aria-label", `${e}: open colour picker`);
|
|
1859
|
+
const s = document.createElement("input");
|
|
1860
|
+
s.type = "text", s.className = "colour-hex", s.spellcheck = !1, s.maxLength = 7, s.setAttribute("aria-label", `${e} hex value`);
|
|
1735
1861
|
const i = document.createElement("button");
|
|
1736
|
-
i.type = "button", i.className = "colour-pipette", i.innerHTML =
|
|
1862
|
+
i.type = "button", i.className = "colour-pipette", i.innerHTML = ko, i.title = "Pick a colour from the screen", i.setAttribute("aria-label", "Pick a colour from the screen"), a.append(o, r, s, i);
|
|
1737
1863
|
const l = document.createElement("div");
|
|
1738
|
-
l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML =
|
|
1864
|
+
l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML = So.map(([u, m, x]) => {
|
|
1739
1865
|
const b = m ? `color-mix(in oklch, var(--brand) ${x}%, ${m})` : "var(--brand)";
|
|
1740
|
-
return `<span title="${
|
|
1866
|
+
return `<span title="${u}" style="background:${b}"></span>`;
|
|
1741
1867
|
}).join(""), n.append(a, l);
|
|
1742
1868
|
let d = "#000000";
|
|
1743
|
-
const p = (
|
|
1744
|
-
d =
|
|
1745
|
-
},
|
|
1746
|
-
|
|
1869
|
+
const p = (u) => {
|
|
1870
|
+
d = u, o.value = u.toLowerCase(), r.style.background = u, n.getRootNode().activeElement !== s && (s.value = u), n.style.setProperty("--brand", u);
|
|
1871
|
+
}, f = (u) => {
|
|
1872
|
+
u !== d && (p(u), t(u));
|
|
1747
1873
|
};
|
|
1748
|
-
|
|
1874
|
+
r.addEventListener("click", () => {
|
|
1749
1875
|
if (o.showPicker)
|
|
1750
1876
|
try {
|
|
1751
1877
|
o.showPicker();
|
|
@@ -1753,27 +1879,27 @@ function oo({ label: e, onInput: t }) {
|
|
|
1753
1879
|
} catch {
|
|
1754
1880
|
}
|
|
1755
1881
|
o.click();
|
|
1756
|
-
}), o.addEventListener("input", () =>
|
|
1757
|
-
const
|
|
1758
|
-
|
|
1882
|
+
}), o.addEventListener("input", () => f(o.value.toUpperCase())), s.addEventListener("input", () => {
|
|
1883
|
+
const u = mt(s.value);
|
|
1884
|
+
u && f(u);
|
|
1759
1885
|
});
|
|
1760
1886
|
const v = () => {
|
|
1761
|
-
|
|
1887
|
+
s.value = d;
|
|
1762
1888
|
};
|
|
1763
|
-
return
|
|
1764
|
-
|
|
1889
|
+
return s.addEventListener("blur", v), s.addEventListener("keydown", (u) => {
|
|
1890
|
+
u.key === "Enter" && (v(), s.select());
|
|
1765
1891
|
}), i.addEventListener("click", async () => {
|
|
1766
1892
|
if ("EyeDropper" in window)
|
|
1767
1893
|
try {
|
|
1768
|
-
const { sRGBHex:
|
|
1769
|
-
m &&
|
|
1894
|
+
const { sRGBHex: u } = await new window.EyeDropper().open(), m = mt(u);
|
|
1895
|
+
m && f(m);
|
|
1770
1896
|
} catch {
|
|
1771
1897
|
}
|
|
1772
1898
|
else
|
|
1773
|
-
|
|
1774
|
-
}), { el: n, setValue: (
|
|
1899
|
+
r.click();
|
|
1900
|
+
}), { el: n, setValue: (u) => p(mt(u) || d) };
|
|
1775
1901
|
}
|
|
1776
|
-
function
|
|
1902
|
+
function Eo({ label: e, options: t, onSelect: n }) {
|
|
1777
1903
|
const a = document.createElement("div");
|
|
1778
1904
|
a.className = "swatches", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
|
|
1779
1905
|
const o = t.map((l) => {
|
|
@@ -1781,36 +1907,36 @@ function ro({ label: e, options: t, onSelect: n }) {
|
|
|
1781
1907
|
return d.type = "button", d.className = "swatch", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.innerHTML = `<span class="swatch-ring"><span class="swatch-dot" style="background:${l.colour}"></span></span><span class="swatch-label">${l.label}</span>`, d.addEventListener("click", () => i(l.value)), d;
|
|
1782
1908
|
});
|
|
1783
1909
|
a.append(...o);
|
|
1784
|
-
let
|
|
1785
|
-
function
|
|
1786
|
-
|
|
1910
|
+
let r = null;
|
|
1911
|
+
function s(l) {
|
|
1912
|
+
r = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
|
|
1787
1913
|
const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
|
|
1788
|
-
o.forEach((p,
|
|
1789
|
-
p.tabIndex =
|
|
1914
|
+
o.forEach((p, f) => {
|
|
1915
|
+
p.tabIndex = f === d ? 0 : -1;
|
|
1790
1916
|
});
|
|
1791
1917
|
}
|
|
1792
1918
|
function i(l, d) {
|
|
1793
|
-
l !==
|
|
1919
|
+
l !== r && (s(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
|
|
1794
1920
|
}
|
|
1795
1921
|
return a.addEventListener("keydown", (l) => {
|
|
1796
1922
|
const d = o.indexOf(l.target.closest(".swatch"));
|
|
1797
1923
|
if (d < 0) return;
|
|
1798
1924
|
const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
|
|
1799
|
-
let
|
|
1800
|
-
if (p)
|
|
1801
|
-
else if (l.key === "Home")
|
|
1802
|
-
else if (l.key === "End")
|
|
1925
|
+
let f;
|
|
1926
|
+
if (p) f = (d + p + o.length) % o.length;
|
|
1927
|
+
else if (l.key === "Home") f = 0;
|
|
1928
|
+
else if (l.key === "End") f = o.length - 1;
|
|
1803
1929
|
else return;
|
|
1804
|
-
l.preventDefault(), i(o[
|
|
1805
|
-
}),
|
|
1806
|
-
}
|
|
1807
|
-
const
|
|
1808
|
-
slate:
|
|
1809
|
-
gray:
|
|
1810
|
-
zinc:
|
|
1811
|
-
neutral:
|
|
1812
|
-
stone:
|
|
1813
|
-
}, N = (e) => Object.fromEntries(e.split(" ").map((t, n) => [400 + 100 * n, `#${t}`])),
|
|
1930
|
+
l.preventDefault(), i(o[f].dataset.value, !0);
|
|
1931
|
+
}), s(null), { el: a, setValue: s };
|
|
1932
|
+
}
|
|
1933
|
+
const Co = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], ke = (e) => Object.fromEntries(e.split(" ").map((t, n) => [Co[n], `#${t}`])), bt = {
|
|
1934
|
+
slate: ke("f8fafc f1f5f9 e2e8f0 cbd5e1 94a3b8 64748b 475569 334155 1e293b 0f172a 020617"),
|
|
1935
|
+
gray: ke("f9fafb f3f4f6 e5e7eb d1d5db 9ca3af 6b7280 4b5563 374151 1f2937 111827 030712"),
|
|
1936
|
+
zinc: ke("fafafa f4f4f5 e4e4e7 d4d4d8 a1a1aa 71717a 52525b 3f3f46 27272a 18181b 09090b"),
|
|
1937
|
+
neutral: ke("fafafa f5f5f5 e5e5e5 d4d4d4 a3a3a3 737373 525252 404040 262626 171717 0a0a0a"),
|
|
1938
|
+
stone: ke("fafaf9 f5f5f4 e7e5e4 d6d3d1 a8a29e 78716c 57534e 44403c 292524 1c1917 0c0a09")
|
|
1939
|
+
}, N = (e) => Object.fromEntries(e.split(" ").map((t, n) => [400 + 100 * n, `#${t}`])), xt = {
|
|
1814
1940
|
red: N("f87171 ef4444 dc2626 b91c1c 991b1b"),
|
|
1815
1941
|
orange: N("fb923c f97316 ea580c c2410c 9a3412"),
|
|
1816
1942
|
amber: N("fbbf24 f59e0b d97706 b45309 92400e"),
|
|
@@ -1828,17 +1954,17 @@ const so = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], xe = (e) => O
|
|
|
1828
1954
|
fuchsia: N("e879f9 d946ef c026d3 a21caf 86198f"),
|
|
1829
1955
|
pink: N("f472b6 ec4899 db2777 be185d 9d174d"),
|
|
1830
1956
|
rose: N("fb7185 f43f5e e11d48 be123c 9f1239")
|
|
1831
|
-
},
|
|
1957
|
+
}, Xe = [
|
|
1832
1958
|
{ value: "indigo-zinc", label: "Indigo · Zinc", primary: "indigo", neutral: "zinc" },
|
|
1833
1959
|
{ value: "emerald-stone", label: "Emerald · Stone", primary: "emerald", neutral: "stone" },
|
|
1834
1960
|
{ value: "rose-slate", label: "Rose · Slate", primary: "rose", neutral: "slate" },
|
|
1835
1961
|
{ value: "amber-neutral", label: "Amber · Neutral", primary: "amber", neutral: "neutral" }
|
|
1836
|
-
],
|
|
1962
|
+
], Ln = "#4F46E5", Mo = "#A0743C", To = [
|
|
1837
1963
|
// Figma Swatch row sample colours
|
|
1838
1964
|
{ value: "pure", label: "Pure", colour: "#8E8E93" },
|
|
1839
1965
|
{ value: "subtle", label: "Subtle", colour: "#8A89A6" },
|
|
1840
1966
|
{ value: "warm", label: "Warm", colour: "#9C9389" }
|
|
1841
|
-
],
|
|
1967
|
+
], An = ["bg", "surface", "surface-raised", "text", "text-muted", "border"], Lo = {
|
|
1842
1968
|
// proposal
|
|
1843
1969
|
bg: "#0F0F11",
|
|
1844
1970
|
surface: "#18181B",
|
|
@@ -1846,32 +1972,32 @@ const so = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], xe = (e) => O
|
|
|
1846
1972
|
text: "#F4F4F5",
|
|
1847
1973
|
"text-muted": "#A1A1AA",
|
|
1848
1974
|
border: "#2E2E33"
|
|
1849
|
-
},
|
|
1850
|
-
function
|
|
1851
|
-
const t = (l) => (l /= 255, l <= 0.03928 ? l / 12.92 : ((l + 0.055) / 1.055) ** 2.4), [n, a, o] = [1, 3, 5].map((l) => t(parseInt(e.slice(l, l + 2), 16))),
|
|
1852
|
-
return
|
|
1975
|
+
}, Ao = { bg: 4, surface: 2, "surface-raised": 2, text: 5, "text-muted": 12, border: 10 }, ee = (e) => /^#[0-9a-f]{6}$/i.test(e || ""), wt = (e) => [500, 600, 700].includes(Number(e.shade)) ? Number(e.shade) : 600, vt = (e) => e[0].toUpperCase() + e.slice(1);
|
|
1976
|
+
function Pn(e) {
|
|
1977
|
+
const t = (l) => (l /= 255, l <= 0.03928 ? l / 12.92 : ((l + 0.055) / 1.055) ** 2.4), [n, a, o] = [1, 3, 5].map((l) => t(parseInt(e.slice(l, l + 2), 16))), r = 0.2126 * n + 0.7152 * a + 0.0722 * o, s = 1.05 / (r + 0.05), i = (r + 0.05) / (98e-4 + 0.05);
|
|
1978
|
+
return s >= i ? "#FFFFFF" : "#18181B";
|
|
1853
1979
|
}
|
|
1854
|
-
const
|
|
1980
|
+
const Je = {
|
|
1855
1981
|
id: "colour",
|
|
1856
1982
|
title: "Colour",
|
|
1857
1983
|
hint: "Brand colour · neutral tint · mode",
|
|
1858
|
-
uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...
|
|
1984
|
+
uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...An.map((e) => `--twd-${e}`)],
|
|
1859
1985
|
tokens(e) {
|
|
1860
|
-
const t = e.colour || {}, n = {}, a = t.mode === "dark", o =
|
|
1861
|
-
if (!
|
|
1862
|
-
const i =
|
|
1863
|
-
n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] =
|
|
1986
|
+
const t = e.colour || {}, n = {}, a = t.mode === "dark", o = xt[t.primary];
|
|
1987
|
+
if (!ee(t.brand) && o) {
|
|
1988
|
+
const i = wt(t);
|
|
1989
|
+
n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] = Pn(o[i]), n["--twd-focus"] = o[500];
|
|
1864
1990
|
}
|
|
1865
|
-
|
|
1866
|
-
const
|
|
1867
|
-
if (
|
|
1868
|
-
const i = a ? { bg:
|
|
1991
|
+
ee(t.brand) && (n["--twd-primary"] = t.brand, n["--twd-primary-hover"] = a ? `color-mix(in oklch, ${t.brand} 85%, white)` : `color-mix(in oklch, ${t.brand} 86%, black)`, n["--twd-on-primary"] = Pn(t.brand), n["--twd-focus"] = t.brand);
|
|
1992
|
+
const r = t.tint === "subtle" ? ee(t.brand) ? t.brand : L("--twd-primary") || Ln : t.tint === "warm" ? Mo : null, s = bt[t.neutral];
|
|
1993
|
+
if (s) {
|
|
1994
|
+
const i = a ? { bg: s[950], surface: s[900], "surface-raised": s[800], text: s[100], "text-muted": s[400], border: s[800] } : { bg: s[50], surface: "#ffffff", "surface-raised": "#ffffff", text: s[900], "text-muted": s[500], border: s[200] };
|
|
1869
1995
|
Object.entries(i).forEach(([l, d]) => {
|
|
1870
1996
|
n[`--twd-${l}`] = d;
|
|
1871
1997
|
});
|
|
1872
|
-
} else (a ||
|
|
1873
|
-
const l = a ?
|
|
1874
|
-
l && (n[`--twd-${i}`] =
|
|
1998
|
+
} else (a || r) && An.forEach((i) => {
|
|
1999
|
+
const l = a ? Lo[i] : L(`--twd-${i}`);
|
|
2000
|
+
l && (n[`--twd-${i}`] = r ? `color-mix(in oklch, ${l} ${100 - Ao[i]}%, ${r})` : l);
|
|
1875
2001
|
});
|
|
1876
2002
|
return n;
|
|
1877
2003
|
},
|
|
@@ -1879,61 +2005,61 @@ const He = {
|
|
|
1879
2005
|
// ---- Tabbed layout (Colour tab): themes, then Tailwind fine-tuning ----
|
|
1880
2006
|
themeTiles(e, t, { label: n = "Theme" } = {}) {
|
|
1881
2007
|
const a = (o) => {
|
|
1882
|
-
const
|
|
1883
|
-
return `<span class="theme" style="--bg:${
|
|
2008
|
+
const r = bt[o.neutral], s = xt[o.primary];
|
|
2009
|
+
return `<span class="theme" style="--bg:${r[50]};--line:${r[200]};--ink:${r[900]};--soft:${r[500]};--pri:${s[600]}"><i><b></b><s></s><u></u></i></span><span class="palette">${[s[400], s[500], s[600], s[700], s[800], r[200], r[500], r[900]].map((i) => `<i style="background:${i}"></i>`).join("")}</span>`;
|
|
1884
2010
|
};
|
|
1885
|
-
e.append(
|
|
2011
|
+
e.append(_(t, {
|
|
1886
2012
|
section: "colour",
|
|
1887
2013
|
key: "theme",
|
|
1888
2014
|
label: n,
|
|
1889
2015
|
columns: 2,
|
|
1890
2016
|
className: "tiles-themes",
|
|
1891
|
-
options:
|
|
2017
|
+
options: Xe.map((o) => ({ value: o.value, label: o.label, preview: a(o) })),
|
|
1892
2018
|
current: (o) => {
|
|
1893
|
-
const
|
|
1894
|
-
return
|
|
2019
|
+
const r = o.colour || {};
|
|
2020
|
+
return ee(r.brand) ? null : Xe.find((s) => s.primary === r.primary && s.neutral === r.neutral)?.value || null;
|
|
1895
2021
|
},
|
|
1896
2022
|
select: (o) => {
|
|
1897
|
-
const
|
|
1898
|
-
t.setSection("colour", { primary:
|
|
2023
|
+
const r = Xe.find((s) => s.value === o);
|
|
2024
|
+
t.setSection("colour", { primary: r.primary, neutral: r.neutral, shade: null, brand: null, tint: null });
|
|
1899
2025
|
},
|
|
1900
|
-
valueText: (o,
|
|
1901
|
-
const
|
|
1902
|
-
return o ? "" :
|
|
2026
|
+
valueText: (o, r) => {
|
|
2027
|
+
const s = r.colour || {};
|
|
2028
|
+
return o ? "" : s.primary || s.neutral || s.brand ? "Custom" : "Prototype colours";
|
|
1903
2029
|
}
|
|
1904
2030
|
}));
|
|
1905
2031
|
},
|
|
1906
2032
|
primary(e, t, { label: n = "Primary" } = {}) {
|
|
1907
|
-
const a =
|
|
2033
|
+
const a = D(n), o = _(t, {
|
|
1908
2034
|
section: "colour",
|
|
1909
2035
|
key: "primary",
|
|
1910
2036
|
label: `${n} colour family`,
|
|
1911
2037
|
columns: 9,
|
|
1912
2038
|
className: "tiles-swatches",
|
|
1913
|
-
options: Object.entries(
|
|
2039
|
+
options: Object.entries(xt).map(([i, l]) => ({ value: i, label: vt(i), title: vt(i), preview: `<span class="dot" style="background:${l[500]}"></span>` })),
|
|
1914
2040
|
current: (i) => {
|
|
1915
2041
|
const l = i.colour || {};
|
|
1916
|
-
return
|
|
2042
|
+
return ee(l.brand) ? null : l.primary || null;
|
|
1917
2043
|
},
|
|
1918
2044
|
select: (i) => t.setSection("colour", { primary: i, brand: null })
|
|
1919
2045
|
});
|
|
1920
2046
|
o.querySelector(".field-head").remove();
|
|
1921
|
-
const
|
|
1922
|
-
a.append(o,
|
|
1923
|
-
const
|
|
2047
|
+
const r = ge({ label: "Shade", options: [500, 600, 700].map((i) => ({ value: String(i), label: String(i) })), onSelect: (i) => t.setSection("colour", { shade: Number(i) }) });
|
|
2048
|
+
a.append(o, r.el);
|
|
2049
|
+
const s = () => {
|
|
1924
2050
|
const i = t.getState().colour || {};
|
|
1925
|
-
|
|
2051
|
+
r.setValue(String(wt(i))), a.setValue(ee(i.brand) ? i.brand.toLowerCase() : i.primary ? `${i.primary}-${wt(i)}` : "");
|
|
1926
2052
|
};
|
|
1927
|
-
|
|
2053
|
+
s(), t.onChange(s), e.append(a.el);
|
|
1928
2054
|
},
|
|
1929
2055
|
neutralTiles(e, t, { label: n = "Neutral" } = {}) {
|
|
1930
|
-
e.append(
|
|
2056
|
+
e.append(_(t, {
|
|
1931
2057
|
section: "colour",
|
|
1932
2058
|
key: "neutral",
|
|
1933
2059
|
label: n,
|
|
1934
2060
|
columns: 5,
|
|
1935
2061
|
className: "tiles-greys",
|
|
1936
|
-
options: Object.entries(
|
|
2062
|
+
options: Object.entries(bt).map(([a, o]) => ({ value: a, label: vt(a), preview: `<span class="greys">${[o[50], o[200], o[500], o[900]].map((r) => `<i style="background:${r}"></i>`).join("")}</span>` })),
|
|
1937
2063
|
select: (a) => t.setSection("colour", { neutral: a, tint: null }),
|
|
1938
2064
|
valueText: (a) => a || ""
|
|
1939
2065
|
}));
|
|
@@ -1942,22 +2068,22 @@ const He = {
|
|
|
1942
2068
|
const n = document.createElement("details");
|
|
1943
2069
|
n.className = "more more-custom", n.innerHTML = '<summary><span class="rainbow" aria-hidden="true"></span><span>Custom colour…</span><span class="muted">any hex</span></summary>';
|
|
1944
2070
|
const a = document.createElement("div");
|
|
1945
|
-
a.className = "more-body", n.append(a),
|
|
2071
|
+
a.className = "more-body", n.append(a), Je.parts.brand(a, t, { label: "Brand" }), ee((t.getState().colour || {}).brand) && (n.open = !0), e.append(n);
|
|
1946
2072
|
},
|
|
1947
2073
|
brand(e, t, { label: n = "Brand" } = {}) {
|
|
1948
|
-
const a =
|
|
2074
|
+
const a = D(n), o = $o({ label: "Brand colour", onInput: (s) => t.setSection("colour", { brand: s }) });
|
|
1949
2075
|
a.append(o.el);
|
|
1950
|
-
const
|
|
1951
|
-
|
|
2076
|
+
const r = () => o.setValue((t.getState().colour || {}).brand || (t.readToken || L)("--twd-primary") || Ln);
|
|
2077
|
+
r(), t.onChange(r), e.append(a.el);
|
|
1952
2078
|
},
|
|
1953
2079
|
neutral(e, t, { label: n = "Neutral" } = {}) {
|
|
1954
|
-
const a =
|
|
2080
|
+
const a = D(n), o = Eo({ label: "Neutral tint", options: To, onSelect: (s) => t.setSection("colour", { tint: s }) });
|
|
1955
2081
|
a.append(o.el);
|
|
1956
|
-
const
|
|
1957
|
-
|
|
2082
|
+
const r = () => o.setValue((t.getState().colour || {}).tint || "pure");
|
|
2083
|
+
r(), t.onChange(r), e.append(a.el);
|
|
1958
2084
|
},
|
|
1959
2085
|
mode(e, t, { label: n = "Mode" } = {}) {
|
|
1960
|
-
e.append(
|
|
2086
|
+
e.append(Me(t, {
|
|
1961
2087
|
section: "colour",
|
|
1962
2088
|
key: "mode",
|
|
1963
2089
|
label: n,
|
|
@@ -1969,12 +2095,12 @@ const He = {
|
|
|
1969
2095
|
render(e, t) {
|
|
1970
2096
|
["brand", "neutral", "mode"].forEach((n) => this.parts[n](e, t));
|
|
1971
2097
|
}
|
|
1972
|
-
},
|
|
2098
|
+
}, Ve = (e, t = "var(--twd-surface)") => `color-mix(in oklch, var(--twd-primary) ${e}%, ${t})`, Po = {
|
|
1973
2099
|
// proposal recipes; Solid leaves tokens.css defaults
|
|
1974
|
-
soft: { bg:
|
|
1975
|
-
outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover:
|
|
1976
|
-
ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover:
|
|
1977
|
-
},
|
|
2100
|
+
soft: { bg: Ve(14), fg: "var(--twd-primary)", border: "transparent", hover: Ve(22) },
|
|
2101
|
+
outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover: Ve(8, "transparent") },
|
|
2102
|
+
ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover: Ve(10, "transparent") }
|
|
2103
|
+
}, Oo = { small: { height: 0.8, pad: 0.75 }, large: { height: 1.2, pad: 1.25 } }, ia = aa({
|
|
1978
2104
|
id: "buttons",
|
|
1979
2105
|
title: "Buttons",
|
|
1980
2106
|
hint: "Style · corners · size",
|
|
@@ -1994,14 +2120,14 @@ const He = {
|
|
|
1994
2120
|
{ value: "pill", label: "Pill", radius: 999 }
|
|
1995
2121
|
],
|
|
1996
2122
|
extraTokens(e) {
|
|
1997
|
-
const t = {}, n =
|
|
1998
|
-
n && Object.entries(n).forEach(([
|
|
1999
|
-
t[`--twd-button-${
|
|
2123
|
+
const t = {}, n = Po[e.style];
|
|
2124
|
+
n && Object.entries(n).forEach(([s, i]) => {
|
|
2125
|
+
t[`--twd-button-${s}`] = i;
|
|
2000
2126
|
});
|
|
2001
|
-
const a =
|
|
2127
|
+
const a = Oo[e.size];
|
|
2002
2128
|
if (!a) return t;
|
|
2003
|
-
const o =
|
|
2004
|
-
return o && (t["--twd-control-height"] = o),
|
|
2129
|
+
const o = Ce(L("--twd-control-height"), a.height), r = Ce(L("--twd-control-pad-x"), a.pad);
|
|
2130
|
+
return o && (t["--twd-control-height"] = o), r && (t["--twd-control-pad-x"] = r), t;
|
|
2005
2131
|
},
|
|
2006
2132
|
extra: [
|
|
2007
2133
|
{
|
|
@@ -2016,16 +2142,18 @@ const He = {
|
|
|
2016
2142
|
key: "size",
|
|
2017
2143
|
label: "Size",
|
|
2018
2144
|
fallback: "medium",
|
|
2145
|
+
valueText: (e, t) => t("--twd-control-height"),
|
|
2146
|
+
// the height in effect (Shape pass)
|
|
2019
2147
|
preview: (e) => `<span class="shape shape-button size-${e}"></span>`,
|
|
2020
2148
|
options: [{ value: "small", label: "Small" }, { value: "medium", label: "Medium" }, { value: "large", label: "Large" }]
|
|
2021
2149
|
}
|
|
2022
2150
|
]
|
|
2023
|
-
}),
|
|
2151
|
+
}), jo = {
|
|
2024
2152
|
// proposal recipes; Filled leaves tokens.css defaults
|
|
2025
2153
|
outlined: { bg: "transparent", border: "color-mix(in oklch, var(--twd-border) 60%, var(--twd-text))", shadow: "none" },
|
|
2026
2154
|
elevated: { bg: "var(--twd-surface-raised)", border: "transparent", shadow: "var(--twd-shadow-md)" },
|
|
2027
2155
|
tinted: { bg: "color-mix(in oklch, var(--twd-primary) 8%, var(--twd-surface))", border: "color-mix(in oklch, var(--twd-primary) 25%, var(--twd-surface))", shadow: "none" }
|
|
2028
|
-
},
|
|
2156
|
+
}, Ft = aa({
|
|
2029
2157
|
id: "cards",
|
|
2030
2158
|
title: "Cards",
|
|
2031
2159
|
hint: "Style · corners · border",
|
|
@@ -2045,11 +2173,11 @@ const He = {
|
|
|
2045
2173
|
// proposal (cards don't go fully pill)
|
|
2046
2174
|
],
|
|
2047
2175
|
extraTokens(e) {
|
|
2048
|
-
const t = {}, n =
|
|
2176
|
+
const t = {}, n = jo[e.style];
|
|
2049
2177
|
if (n && Object.entries(n).forEach(([a, o]) => {
|
|
2050
2178
|
t[`--twd-card-${a}`] = o;
|
|
2051
2179
|
}), e.border === "none" && (t["--twd-border-width-container"] = "0px"), e.border === "thick") {
|
|
2052
|
-
const a =
|
|
2180
|
+
const a = Ce(L("--twd-border-width-container"), 2);
|
|
2053
2181
|
a && (t["--twd-border-width-container"] = a);
|
|
2054
2182
|
}
|
|
2055
2183
|
return t;
|
|
@@ -2060,59 +2188,64 @@ const He = {
|
|
|
2060
2188
|
label: "Style",
|
|
2061
2189
|
fallback: "filled",
|
|
2062
2190
|
columns: 4,
|
|
2063
|
-
|
|
2191
|
+
// A mini card with two content lines on a page backdrop (Figma 281:1421)
|
|
2192
|
+
preview: (e) => `<span class="mini"><span class="mini-card style-${e}"><i></i><i></i></span></span>`,
|
|
2064
2193
|
options: [{ value: "filled", label: "Filled" }, { value: "outlined", label: "Outlined" }, { value: "elevated", label: "Elevated" }, { value: "tinted", label: "Tinted" }]
|
|
2065
2194
|
},
|
|
2066
2195
|
{
|
|
2067
2196
|
key: "border",
|
|
2068
2197
|
label: "Border",
|
|
2069
2198
|
fallback: "thin",
|
|
2199
|
+
token: "--twd-border-width-container",
|
|
2200
|
+
// Bold widens it
|
|
2070
2201
|
preview: (e) => `<span class="shape shape-card border-${e}"></span>`,
|
|
2071
2202
|
options: [{ value: "none", label: "None" }, { value: "thin", label: "Thin" }, { value: "thick", label: "Thick" }]
|
|
2072
2203
|
}
|
|
2073
2204
|
]
|
|
2074
|
-
}),
|
|
2205
|
+
}), qe = (e) => !(e.depth || {}).style && he(e, "--twd-shadow-md"), Fo = (e) => ((e.character || {}).bold || 0.5) > 0.5, G = 60, yt = {
|
|
2075
2206
|
soft: { sm: [0, 1, 2, 0, 0.06], md: [0, 4, 12, 0, 0.08], lg: [0, 12, 32, 0, 0.12] },
|
|
2076
2207
|
// = tokens.css
|
|
2077
2208
|
crisp: { sm: [0, 1, 1, 0, 0.12], md: [0, 2, 4, 0, 0.14], lg: [0, 4, 8, 0, 0.16] },
|
|
2078
2209
|
bold: { sm: [2, 2, 0, 0, 0.85], md: [3, 3, 0, 0, 0.85], lg: [5, 5, 0, 0, 0.85] }
|
|
2079
|
-
},
|
|
2080
|
-
const
|
|
2081
|
-
return
|
|
2082
|
-
},
|
|
2210
|
+
}, He = ([e, t, n, a, o], r) => {
|
|
2211
|
+
const s = Math.min(1, +(o * r).toFixed(3));
|
|
2212
|
+
return s <= 0 ? "none" : `${e}px ${t}px ${n}px ${a}px rgb(0 0 0 / ${s})`;
|
|
2213
|
+
}, Nt = {
|
|
2083
2214
|
id: "depth",
|
|
2084
2215
|
title: "Depth",
|
|
2085
2216
|
hint: "Flat · soft · crisp · bold",
|
|
2086
2217
|
uses: ["--twd-shadow-sm", "--twd-shadow-md", "--twd-shadow-lg"],
|
|
2087
2218
|
tokens(e) {
|
|
2088
|
-
const t = e.depth || {}, n = t.style || "soft", a = typeof t.intensity == "number" ? Math.min(100, Math.max(0, t.intensity)) :
|
|
2219
|
+
const t = e.depth || {}, n = t.style || "soft", a = typeof t.intensity == "number" ? Math.min(100, Math.max(0, t.intensity)) : G;
|
|
2089
2220
|
if (n === "flat")
|
|
2090
2221
|
return { "--twd-shadow-sm": "none", "--twd-shadow-md": "none", "--twd-shadow-lg": "none" };
|
|
2091
|
-
const o =
|
|
2222
|
+
const o = yt[n];
|
|
2092
2223
|
if (!o) return {};
|
|
2093
|
-
if (n === "soft" && a ===
|
|
2094
|
-
const
|
|
2224
|
+
if (n === "soft" && a === G) return {};
|
|
2225
|
+
const r = a / G;
|
|
2095
2226
|
return {
|
|
2096
|
-
"--twd-shadow-sm":
|
|
2097
|
-
"--twd-shadow-md":
|
|
2098
|
-
"--twd-shadow-lg":
|
|
2227
|
+
"--twd-shadow-sm": He(o.sm, r),
|
|
2228
|
+
"--twd-shadow-md": He(o.md, r),
|
|
2229
|
+
"--twd-shadow-lg": He(o.lg, r)
|
|
2099
2230
|
};
|
|
2100
2231
|
},
|
|
2101
2232
|
parts: {
|
|
2102
2233
|
// Tabbed layout: each style as a small card wearing that shadow.
|
|
2103
2234
|
styleTiles(e, t, { label: n = "Style" } = {}) {
|
|
2104
|
-
const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${
|
|
2105
|
-
e.append(
|
|
2235
|
+
const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${yt[o] ? He(yt[o].md, 1) : "none"}"></span>`;
|
|
2236
|
+
e.append(_(t, {
|
|
2106
2237
|
section: "depth",
|
|
2107
2238
|
key: "style",
|
|
2108
2239
|
label: n,
|
|
2109
2240
|
fallback: "soft",
|
|
2110
2241
|
columns: 4,
|
|
2111
|
-
options: ["flat", "soft", "crisp", "bold"].map((o) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(o) }))
|
|
2242
|
+
options: ["flat", "soft", "crisp", "bold"].map((o) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(o) })),
|
|
2243
|
+
current: (o) => (o.depth || {}).style || (qe(o) ? null : "soft"),
|
|
2244
|
+
valueText: (o, r) => qe(r) ? `${Fo(r) ? "Bold recipe" : "Soft, faded"} · from Character` : ""
|
|
2112
2245
|
}));
|
|
2113
2246
|
},
|
|
2114
2247
|
style(e, t, { label: n = "Style" } = {}) {
|
|
2115
|
-
e.append(
|
|
2248
|
+
e.append(Me(t, {
|
|
2116
2249
|
section: "depth",
|
|
2117
2250
|
key: "style",
|
|
2118
2251
|
label: n,
|
|
@@ -2121,98 +2254,115 @@ const He = {
|
|
|
2121
2254
|
}));
|
|
2122
2255
|
},
|
|
2123
2256
|
intensity(e, t, { label: n = "Intensity" } = {}) {
|
|
2124
|
-
e.append(
|
|
2257
|
+
e.append(go(t, {
|
|
2258
|
+
section: "depth",
|
|
2259
|
+
key: "intensity",
|
|
2260
|
+
label: n,
|
|
2261
|
+
min: 0,
|
|
2262
|
+
max: 100,
|
|
2263
|
+
fallback: G,
|
|
2264
|
+
format: (a) => `${a}%`,
|
|
2265
|
+
note: (a) => qe(a) ? "from Character" : "",
|
|
2266
|
+
// What the pad actually uses (character.js): Bold = 60% × bold; Subtle fades this slider's value.
|
|
2267
|
+
value: (a) => {
|
|
2268
|
+
if (!qe(a)) return (a.depth || {}).intensity;
|
|
2269
|
+
const o = a.character.bold, r = typeof (a.depth || {}).intensity == "number" ? a.depth.intensity : G;
|
|
2270
|
+
return Math.round(o > 0.5 ? G * (2 * o - 1) : r * (1 - (1 - 2 * o)));
|
|
2271
|
+
}
|
|
2272
|
+
}));
|
|
2125
2273
|
}
|
|
2126
2274
|
},
|
|
2127
2275
|
render(e, t) {
|
|
2128
2276
|
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
2129
2277
|
}
|
|
2130
|
-
},
|
|
2131
|
-
function
|
|
2132
|
-
return typeof e.bold == "number" && isFinite(e.bold) && e.bold !== 0.5 ?
|
|
2278
|
+
}, No = (e) => Math.min(1, Math.max(0, e)), On = (e) => Math.round(e / 50) * 50, kt = (e) => `${+e.toFixed(1)}%`;
|
|
2279
|
+
function Bt(e = {}) {
|
|
2280
|
+
return typeof e.bold == "number" && isFinite(e.bold) && e.bold !== 0.5 ? No(e.bold) : null;
|
|
2133
2281
|
}
|
|
2134
|
-
const
|
|
2282
|
+
const _e = (e, t, n) => (e.tokens(t) || {})[n] || L(n), la = {
|
|
2135
2283
|
id: "character",
|
|
2136
2284
|
title: "Character",
|
|
2137
2285
|
hint: "Subtle ↔ Bold",
|
|
2138
2286
|
uses: [],
|
|
2139
2287
|
// set from the Simple page only
|
|
2140
2288
|
tokens(e) {
|
|
2141
|
-
const t =
|
|
2289
|
+
const t = Bt(e.character);
|
|
2142
2290
|
if (t === null) return {};
|
|
2143
|
-
const n = {}, a = 2 * t - 1, o = Math.max(0, a),
|
|
2144
|
-
n["--twd-weight-heading"] = String(
|
|
2145
|
-
const
|
|
2146
|
-
if (
|
|
2147
|
-
n["--twd-border"] = `color-mix(in oklch, ${
|
|
2148
|
-
const l =
|
|
2149
|
-
n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${
|
|
2150
|
-
const p = d === "--twd-border-width-container" ?
|
|
2151
|
-
|
|
2152
|
-
}), [[
|
|
2153
|
-
const
|
|
2154
|
-
|
|
2291
|
+
const n = {}, a = 2 * t - 1, o = Math.max(0, a), r = Math.max(0, -a);
|
|
2292
|
+
n["--twd-weight-heading"] = String(On(600 + 200 * a)), n["--twd-weight-strong"] = String(On(600 + 100 * a));
|
|
2293
|
+
const s = _e(Je, e, "--twd-border");
|
|
2294
|
+
if (r && (n["--twd-border"] = `color-mix(in oklch, ${s} ${kt(100 - 50 * r)}, var(--twd-bg))`), o) {
|
|
2295
|
+
n["--twd-border"] = `color-mix(in oklch, ${s} ${kt(100 - 90 * o)}, var(--twd-text))`;
|
|
2296
|
+
const l = _e(Je, e, "--twd-text-muted");
|
|
2297
|
+
n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${kt(100 - 35 * o)}, var(--twd-text))`, ["--twd-border-width", "--twd-border-width-container"].forEach((d) => {
|
|
2298
|
+
const p = d === "--twd-border-width-container" ? _e(Ft, e, d) : L(d), f = parseFloat(p);
|
|
2299
|
+
f > 0 && (n[d] = `${Math.round(f * (1 + o))}px`);
|
|
2300
|
+
}), [[ia, "--twd-radius-control"], [Ft, "--twd-radius-container"]].forEach(([d, p]) => {
|
|
2301
|
+
const f = parseFloat(_e(d, e, p));
|
|
2302
|
+
f > 0 && f < 999 && (n[p] = Ce(`${f}px`, 1 - 0.5 * o));
|
|
2155
2303
|
});
|
|
2156
2304
|
}
|
|
2157
2305
|
const i = e.depth || {};
|
|
2158
2306
|
if (!i.style) {
|
|
2159
|
-
const l = typeof i.intensity == "number" ? i.intensity :
|
|
2307
|
+
const l = typeof i.intensity == "number" ? i.intensity : G, d = r ? Nt.tokens({ depth: { style: "soft", intensity: l * (1 - r) } }) : Nt.tokens({ depth: { style: "bold", intensity: G * o } });
|
|
2160
2308
|
Object.assign(n, d);
|
|
2161
2309
|
}
|
|
2162
2310
|
return n;
|
|
2163
2311
|
},
|
|
2164
2312
|
describe: (e, t) => e === "bold" ? { label: "Subtle ↔ Bold", value: `${Math.round(t * 100)}%` } : null
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2313
|
+
};
|
|
2314
|
+
mo((e) => la.tokens(e));
|
|
2315
|
+
const Ro = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>', zo = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
|
|
2316
|
+
let Io = 0;
|
|
2317
|
+
function Do({ label: e, options: t, onSelect: n, placeholder: a, onOpen: o }) {
|
|
2318
|
+
const r = `twd-fonts-${++Io}`, s = document.createElement("div");
|
|
2319
|
+
s.className = "font-select";
|
|
2170
2320
|
const i = document.createElement("button");
|
|
2171
|
-
i.type = "button", i.className = "font-trigger", i.setAttribute("aria-haspopup", "listbox"), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-controls",
|
|
2321
|
+
i.type = "button", i.className = "font-trigger", i.setAttribute("aria-haspopup", "listbox"), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-controls", r), i.innerHTML = `
|
|
2172
2322
|
<span class="font-aa" aria-hidden="true">Aa</span>
|
|
2173
2323
|
<span class="font-name"></span>
|
|
2174
2324
|
<span class="font-fonts"></span>
|
|
2175
|
-
<span class="font-chevron">${
|
|
2325
|
+
<span class="font-chevron">${Ro}</span>`;
|
|
2176
2326
|
const l = document.createElement("div");
|
|
2177
|
-
l.className = "font-menu", l.id =
|
|
2327
|
+
l.className = "font-menu", l.id = r, l.setAttribute("role", "listbox"), l.setAttribute("aria-label", e), l.hidden = !0;
|
|
2178
2328
|
const d = t.map((h) => {
|
|
2179
|
-
const
|
|
2180
|
-
return
|
|
2329
|
+
const w = document.createElement("div");
|
|
2330
|
+
return w.className = "font-option", w.setAttribute("role", "option"), w.tabIndex = -1, w.dataset.value = h.value, w.innerHTML = `
|
|
2181
2331
|
<span class="font-aa" style="font-family:${h.preview}" aria-hidden="true">Aa</span>
|
|
2182
2332
|
<span class="font-text"><span class="font-name">${h.name}</span><span class="font-fonts">${h.fonts}</span></span>
|
|
2183
|
-
<span class="font-check">${
|
|
2333
|
+
<span class="font-check">${zo}</span>`, w.addEventListener("click", () => b(h.value)), w;
|
|
2184
2334
|
});
|
|
2185
|
-
l.append(...d),
|
|
2335
|
+
l.append(...d), s.append(i, l);
|
|
2186
2336
|
let p = null;
|
|
2187
|
-
function
|
|
2188
|
-
const
|
|
2189
|
-
p =
|
|
2337
|
+
function f(h) {
|
|
2338
|
+
const w = t.find(($) => $.value === h), S = w || a || t[0];
|
|
2339
|
+
p = w ? w.value : a ? null : t[0].value, i.querySelector(".font-name").textContent = S.name, i.querySelector(".font-fonts").textContent = S.fonts, i.querySelector(".font-aa").style.fontFamily = S.preview, i.setAttribute("aria-label", `${e}: ${S.name}, ${S.fonts}`), d.forEach(($) => $.setAttribute("aria-selected", String($.dataset.value === p)));
|
|
2190
2340
|
}
|
|
2191
2341
|
function v() {
|
|
2192
|
-
const h = i.getBoundingClientRect(),
|
|
2193
|
-
l.style.top = `${
|
|
2342
|
+
const h = i.getBoundingClientRect(), w = l.offsetHeight, $ = window.innerHeight - h.bottom - 8 >= w ? h.bottom + 4 : Math.max(8, h.top - 4 - w);
|
|
2343
|
+
l.style.top = `${$}px`, l.style.left = `${h.left}px`, l.style.width = `${h.width}px`;
|
|
2194
2344
|
}
|
|
2195
|
-
function
|
|
2196
|
-
l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(), v(), (d.find((h) => h.dataset.value === p) || d[0]).focus(), setTimeout(() => document.addEventListener("pointerdown", x, !0)), window.addEventListener("resize", m),
|
|
2345
|
+
function u() {
|
|
2346
|
+
l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(), v(), (d.find((h) => h.dataset.value === p) || d[0]).focus(), setTimeout(() => document.addEventListener("pointerdown", x, !0)), window.addEventListener("resize", m), s.closest(".body")?.addEventListener("scroll", m, { once: !0 }));
|
|
2197
2347
|
}
|
|
2198
2348
|
function m(h) {
|
|
2199
2349
|
l.hidden || (l.hidden = !0, i.setAttribute("aria-expanded", "false"), document.removeEventListener("pointerdown", x, !0), window.removeEventListener("resize", m), h === !0 && i.focus());
|
|
2200
2350
|
}
|
|
2201
2351
|
function x(h) {
|
|
2202
|
-
h.composedPath().includes(
|
|
2352
|
+
h.composedPath().includes(s) || m();
|
|
2203
2353
|
}
|
|
2204
2354
|
function b(h) {
|
|
2205
|
-
h !== p && (
|
|
2355
|
+
h !== p && (f(h), n(h)), m(!0);
|
|
2206
2356
|
}
|
|
2207
|
-
return i.addEventListener("click", () => l.hidden ?
|
|
2208
|
-
(h.key === "ArrowDown" || h.key === "ArrowUp") && (h.preventDefault(),
|
|
2357
|
+
return i.addEventListener("click", () => l.hidden ? u() : m(!0)), i.addEventListener("keydown", (h) => {
|
|
2358
|
+
(h.key === "ArrowDown" || h.key === "ArrowUp") && (h.preventDefault(), u());
|
|
2209
2359
|
}), l.addEventListener("keydown", (h) => {
|
|
2210
|
-
const
|
|
2211
|
-
let
|
|
2212
|
-
if (h.key === "ArrowDown")
|
|
2213
|
-
else if (h.key === "ArrowUp")
|
|
2214
|
-
else if (h.key === "Home")
|
|
2215
|
-
else if (h.key === "End")
|
|
2360
|
+
const w = d.indexOf(h.target);
|
|
2361
|
+
let S = null;
|
|
2362
|
+
if (h.key === "ArrowDown") S = Math.min(d.length - 1, w + 1);
|
|
2363
|
+
else if (h.key === "ArrowUp") S = Math.max(0, w - 1);
|
|
2364
|
+
else if (h.key === "Home") S = 0;
|
|
2365
|
+
else if (h.key === "End") S = d.length - 1;
|
|
2216
2366
|
else if (h.key === "Enter" || h.key === " ") {
|
|
2217
2367
|
h.preventDefault(), b(h.target.dataset.value);
|
|
2218
2368
|
return;
|
|
@@ -2223,57 +2373,57 @@ function yo({ label: e, options: t, onSelect: n, placeholder: a, onOpen: o }) {
|
|
|
2223
2373
|
m();
|
|
2224
2374
|
return;
|
|
2225
2375
|
}
|
|
2226
|
-
|
|
2227
|
-
}),
|
|
2228
|
-
}
|
|
2229
|
-
const
|
|
2230
|
-
{ value: "neutral", name: "Neutral", fonts: "Inter · Inter", heading: `"Inter", ${
|
|
2231
|
-
{ value: "modern", name: "Modern", fonts: "Geist · Geist", heading: `"Geist", ${
|
|
2232
|
-
{ value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${
|
|
2233
|
-
{ value: "technical", name: "Technical", fonts: "Space Grotesk · DM Sans", heading: `"Space Grotesk", ${
|
|
2234
|
-
{ value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${
|
|
2235
|
-
{ value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${
|
|
2236
|
-
{ value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${
|
|
2237
|
-
{ value: "friendly", name: "Friendly", fonts: "Manrope · Manrope", heading: `"Manrope", ${
|
|
2238
|
-
],
|
|
2239
|
-
function
|
|
2240
|
-
const t = e.filter((a) => !
|
|
2376
|
+
S !== null && (h.preventDefault(), d[S].focus());
|
|
2377
|
+
}), f(a ? null : t[0].value), { el: s, setValue: f };
|
|
2378
|
+
}
|
|
2379
|
+
const z = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif', Ke = 'Georgia, "Times New Roman", serif', ae = [
|
|
2380
|
+
{ value: "neutral", name: "Neutral", fonts: "Inter · Inter", heading: `"Inter", ${z}`, body: `"Inter", ${z}`, load: ["Inter:wght@400;500;600;700"] },
|
|
2381
|
+
{ value: "modern", name: "Modern", fonts: "Geist · Geist", heading: `"Geist", ${z}`, body: `"Geist", ${z}`, load: ["Geist:wght@400;500;600;700"] },
|
|
2382
|
+
{ value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${Ke}`, body: `"Inter", ${z}`, load: ["Fraunces:wght@400;600;700", "Inter:wght@400;500;600;700"] },
|
|
2383
|
+
{ value: "technical", name: "Technical", fonts: "Space Grotesk · DM Sans", heading: `"Space Grotesk", ${z}`, body: `"DM Sans", ${z}`, load: ["Space+Grotesk:wght@500;600;700", "DM+Sans:wght@400;500;600;700"] },
|
|
2384
|
+
{ value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${Ke}`, body: `"DM Sans", ${z}`, load: ["Playfair+Display:wght@600;700", "DM+Sans:wght@400;500;600;700"] },
|
|
2385
|
+
{ value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${Ke}`, body: `"IBM Plex Sans", ${z}`, load: ["IBM+Plex+Serif:wght@500;600;700", "IBM+Plex+Sans:wght@400;500;600"] },
|
|
2386
|
+
{ value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${Ke}`, body: `"Inter", ${z}`, load: ["Instrument+Serif", "Inter:wght@400;500;600;700"] },
|
|
2387
|
+
{ value: "friendly", name: "Friendly", fonts: "Manrope · Manrope", heading: `"Manrope", ${z}`, body: `"Manrope", ${z}`, load: ["Manrope:wght@400;500;600;700"] }
|
|
2388
|
+
], Se = { compact: 1.125, balanced: 1.2, expressive: 1.333 }, jn = { xs: -2, sm: -1, base: 0, lg: 1, xl: 2, "2xl": 3, "3xl": 4 }, Fn = /* @__PURE__ */ new Set();
|
|
2389
|
+
function St(e) {
|
|
2390
|
+
const t = e.filter((a) => !Fn.has(a));
|
|
2241
2391
|
if (!t.length) return;
|
|
2242
|
-
t.forEach((a) =>
|
|
2392
|
+
t.forEach((a) => Fn.add(a));
|
|
2243
2393
|
const n = document.createElement("link");
|
|
2244
2394
|
n.rel = "stylesheet", n.href = `https://fonts.googleapis.com/css2?${t.map((a) => `family=${a}`).join("&")}&display=swap`, n.dataset.twiddle = "fonts", document.head.appendChild(n);
|
|
2245
2395
|
}
|
|
2246
|
-
const
|
|
2396
|
+
const Bo = {
|
|
2247
2397
|
id: "typography",
|
|
2248
2398
|
title: "Typography",
|
|
2249
2399
|
hint: "Font pairing · base size · scale",
|
|
2250
|
-
uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(
|
|
2400
|
+
uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(jn).map((e) => `--twd-text-${e}`)],
|
|
2251
2401
|
tokens(e) {
|
|
2252
|
-
const t = e.typography || {}, n = {}, a =
|
|
2253
|
-
if (a && (
|
|
2254
|
-
const o = Number(t.base) || 16,
|
|
2255
|
-
Object.entries(
|
|
2256
|
-
n[`--twd-text-${
|
|
2402
|
+
const t = e.typography || {}, n = {}, a = ae.find((o) => o.value === t.font);
|
|
2403
|
+
if (a && (St(a.load), n["--twd-font-heading"] = a.heading, n["--twd-font-body"] = a.body), t.base || t.scale) {
|
|
2404
|
+
const o = Number(t.base) || 16, r = Se[t.scale] || Se.balanced;
|
|
2405
|
+
Object.entries(jn).forEach(([s, i]) => {
|
|
2406
|
+
n[`--twd-text-${s}`] = `${+(o * r ** i / 16).toFixed(3)}rem`;
|
|
2257
2407
|
});
|
|
2258
2408
|
}
|
|
2259
2409
|
return n;
|
|
2260
2410
|
},
|
|
2261
2411
|
parts: {
|
|
2262
2412
|
fonts(e, t, { label: n = "Fonts" } = {}) {
|
|
2263
|
-
const a =
|
|
2413
|
+
const a = D(n), o = Do({
|
|
2264
2414
|
label: "Font pairing",
|
|
2265
|
-
options:
|
|
2266
|
-
placeholder: { name: "Default", fonts: "Prototype fonts", preview:
|
|
2415
|
+
options: ae.map((s) => ({ value: s.value, name: s.name, fonts: s.fonts, preview: s.heading })),
|
|
2416
|
+
placeholder: { name: "Default", fonts: "Prototype fonts", preview: z },
|
|
2267
2417
|
// Load every pairing's fonts when the list opens, so the "Aa" previews are real.
|
|
2268
|
-
onOpen: () =>
|
|
2269
|
-
onSelect: (
|
|
2418
|
+
onOpen: () => St([...new Set(ae.flatMap((s) => s.load))]),
|
|
2419
|
+
onSelect: (s) => t.setSection("typography", { font: s })
|
|
2270
2420
|
});
|
|
2271
2421
|
a.append(o.el);
|
|
2272
|
-
const
|
|
2273
|
-
|
|
2422
|
+
const r = () => o.setValue((t.getState().typography || {}).font || null);
|
|
2423
|
+
r(), t.onChange(r), e.append(a.el);
|
|
2274
2424
|
},
|
|
2275
2425
|
base(e, t, { label: n = "Base size" } = {}) {
|
|
2276
|
-
e.append(
|
|
2426
|
+
e.append(Me(t, {
|
|
2277
2427
|
section: "typography",
|
|
2278
2428
|
key: "base",
|
|
2279
2429
|
label: n,
|
|
@@ -2285,92 +2435,100 @@ const vo = {
|
|
|
2285
2435
|
// Tabbed layout: pairings as tiles, each "Aa" in its own heading font.
|
|
2286
2436
|
// The fonts load (from Google Fonts) only once the tiles are on screen.
|
|
2287
2437
|
fontTiles(e, t, { label: n = "Fonts" } = {}) {
|
|
2288
|
-
const a = (i) => i.replace(/"/g, """), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`,
|
|
2438
|
+
const a = (i) => i.replace(/"/g, """), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`, r = _(t, {
|
|
2289
2439
|
section: "typography",
|
|
2290
2440
|
key: "font",
|
|
2291
2441
|
label: n,
|
|
2292
2442
|
columns: 2,
|
|
2293
2443
|
className: "tiles-fonts",
|
|
2294
2444
|
options: [
|
|
2295
|
-
{ value: "default", label: "Prototype fonts", sub: "Default", preview: o(
|
|
2296
|
-
...
|
|
2445
|
+
{ value: "default", label: "Prototype fonts", sub: "Default", preview: o(z), wide: !0 },
|
|
2446
|
+
...ae.map((i) => ({ value: i.value, label: i.name, sub: i.fonts, preview: o(i.heading) }))
|
|
2297
2447
|
],
|
|
2298
2448
|
current: (i) => (i.typography || {}).font || "default",
|
|
2299
2449
|
select: (i) => t.setSection("typography", { font: i === "default" ? null : i }),
|
|
2300
|
-
valueText: (i) => i === "default" ? "Default" :
|
|
2301
|
-
}),
|
|
2302
|
-
i.some((l) => l.isIntersecting) && (
|
|
2450
|
+
valueText: (i) => i === "default" ? "Default" : ae.find((l) => l.value === i)?.name || ""
|
|
2451
|
+
}), s = new IntersectionObserver((i) => {
|
|
2452
|
+
i.some((l) => l.isIntersecting) && (St([...new Set(ae.flatMap((l) => l.load))]), s.disconnect());
|
|
2303
2453
|
});
|
|
2304
|
-
|
|
2454
|
+
s.observe(r), e.append(r);
|
|
2455
|
+
},
|
|
2456
|
+
// The weights in effect; Subtle ↔ Bold on the Character pad moves them (linked settings, rule 2).
|
|
2457
|
+
weights(e, t, { label: n = "Weights" } = {}) {
|
|
2458
|
+
const a = D(n), o = () => {
|
|
2459
|
+
const r = t.getState(), s = L("--twd-weight-heading"), i = L("--twd-weight-strong");
|
|
2460
|
+
a.setValue(s || i ? `Headings ${s || "—"} · strong ${i || "—"}${he(r, "--twd-weight-heading") ? " · from Character" : ""}` : "");
|
|
2461
|
+
};
|
|
2462
|
+
o(), t.onChange(o), e.append(a.el);
|
|
2305
2463
|
},
|
|
2306
2464
|
scaleTiles(e, t, { label: n = "Scale" } = {}) {
|
|
2307
|
-
const a = (o) => `<span class="ladder">${[-1, 0, 1, 2].map((
|
|
2308
|
-
e.append(
|
|
2465
|
+
const a = (o) => `<span class="ladder">${[-1, 0, 1, 2].map((r) => `<b style="font-size:${Math.round(13 * o ** r)}px">A</b>`).join("")}</span>`;
|
|
2466
|
+
e.append(_(t, {
|
|
2309
2467
|
section: "typography",
|
|
2310
2468
|
key: "scale",
|
|
2311
2469
|
label: n,
|
|
2312
2470
|
fallback: "balanced",
|
|
2313
|
-
options: Object.entries(
|
|
2314
|
-
valueText: (o) => String(
|
|
2471
|
+
options: Object.entries(Se).map(([o, r]) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(r) })),
|
|
2472
|
+
valueText: (o) => String(Se[o])
|
|
2315
2473
|
}));
|
|
2316
2474
|
},
|
|
2317
2475
|
scale(e, t, { label: n = "Scale" } = {}) {
|
|
2318
|
-
e.append(
|
|
2476
|
+
e.append(Me(t, {
|
|
2319
2477
|
section: "typography",
|
|
2320
2478
|
key: "scale",
|
|
2321
2479
|
label: n,
|
|
2322
2480
|
fallback: "balanced",
|
|
2323
2481
|
options: [{ value: "compact", label: "Compact" }, { value: "balanced", label: "Balanced" }, { value: "expressive", label: "Expressive" }],
|
|
2324
|
-
valueText: (a) => String(
|
|
2482
|
+
valueText: (a) => String(Se[a])
|
|
2325
2483
|
}));
|
|
2326
2484
|
}
|
|
2327
2485
|
},
|
|
2328
2486
|
render(e, t) {
|
|
2329
2487
|
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
2330
2488
|
}
|
|
2331
|
-
}, [
|
|
2332
|
-
function
|
|
2489
|
+
}, [Nn, We] = jt;
|
|
2490
|
+
function Uo(e) {
|
|
2333
2491
|
return e > 0.56 ? "Bold" : e < 0.44 ? "Subtle" : "Balanced";
|
|
2334
2492
|
}
|
|
2335
|
-
function
|
|
2336
|
-
return e
|
|
2493
|
+
function Vo(e) {
|
|
2494
|
+
return $e(e, "spacing") ? "Custom spacing" : Dt(H(e.density));
|
|
2337
2495
|
}
|
|
2338
|
-
const
|
|
2496
|
+
const Ge = (e) => e && e[0].toUpperCase() + e.slice(1), Rn = [
|
|
2339
2497
|
["colour", "Colour", (e) => {
|
|
2340
|
-
const t = e.colour || {}, n =
|
|
2341
|
-
return { text: `${t.brand ? t.brand.toUpperCase() : n && !t.shade ? n.label : t.primary ? `${
|
|
2498
|
+
const t = e.colour || {}, n = Xe.find((o) => o.primary === t.primary && o.neutral === t.neutral);
|
|
2499
|
+
return { text: `${t.brand ? t.brand.toUpperCase() : n && !t.shade ? n.label : t.primary ? `${Ge(t.primary)}${t.neutral ? ` · ${Ge(t.neutral)}` : ""}` : "Prototype colours"}${t.mode === "dark" ? " · Dark" : ""}`, swatch: L("--twd-primary") };
|
|
2342
2500
|
}],
|
|
2343
2501
|
["type", "Type", (e) => {
|
|
2344
2502
|
const t = e.typography || {};
|
|
2345
|
-
return { text: `${
|
|
2503
|
+
return { text: `${ae.find((n) => n.value === t.font)?.name || "Prototype fonts"} · ${t.base || 16}px` };
|
|
2346
2504
|
}],
|
|
2347
|
-
["spacing", "Spacing", (e) => ({ text:
|
|
2348
|
-
["shape", "Shape", (e) => ({ text: `${
|
|
2349
|
-
["shadows", "Shadows", (e) => ({ text:
|
|
2350
|
-
],
|
|
2505
|
+
["spacing", "Spacing", (e) => ({ text: $e(e, "spacing") ? "Custom" : Dt(H(e.density)) })],
|
|
2506
|
+
["shape", "Shape", (e) => ({ text: `${Ge((e.buttons || {}).corners) || "Default"} corners` })],
|
|
2507
|
+
["shadows", "Shadows", (e) => ({ text: Ge((e.depth || {}).style) || (Bt(e.character) !== null ? "From the pad" : "Default") })]
|
|
2508
|
+
], qo = {
|
|
2351
2509
|
summary(e, t, { label: n = "Current look", goTab: a } = {}) {
|
|
2352
|
-
const o =
|
|
2353
|
-
|
|
2510
|
+
const o = D(n), r = document.createElement("div");
|
|
2511
|
+
r.className = "summary", Rn.forEach(([i, l]) => {
|
|
2354
2512
|
const d = document.createElement("button");
|
|
2355
|
-
d.type = "button", d.className = "summary-row", d.dataset.summary = i, d.innerHTML = `<span class="summary-name"></span><span class="summary-swatch" hidden></span><span class="summary-value"></span><span class="chev" aria-hidden="true">${
|
|
2356
|
-
}), o.append(
|
|
2357
|
-
const
|
|
2513
|
+
d.type = "button", d.className = "summary-row", d.dataset.summary = i, d.innerHTML = `<span class="summary-name"></span><span class="summary-swatch" hidden></span><span class="summary-value"></span><span class="chev" aria-hidden="true">${P.chevron}</span>`, d.querySelector(".summary-name").textContent = l, d.addEventListener("click", () => a && a(i)), r.append(d);
|
|
2514
|
+
}), o.append(r);
|
|
2515
|
+
const s = () => {
|
|
2358
2516
|
const i = t.getState();
|
|
2359
|
-
|
|
2360
|
-
const p =
|
|
2361
|
-
p.querySelector(".summary-value").textContent =
|
|
2517
|
+
Rn.forEach(([l, , d]) => {
|
|
2518
|
+
const p = r.querySelector(`[data-summary="${l}"]`), f = d(i);
|
|
2519
|
+
p.querySelector(".summary-value").textContent = f.text;
|
|
2362
2520
|
const v = p.querySelector(".summary-swatch");
|
|
2363
|
-
v.hidden = !
|
|
2521
|
+
v.hidden = !f.swatch, f.swatch && (v.style.background = f.swatch);
|
|
2364
2522
|
});
|
|
2365
2523
|
};
|
|
2366
|
-
|
|
2524
|
+
s(), t.onChange(s), e.append(o.el);
|
|
2367
2525
|
},
|
|
2368
2526
|
character(e, t, { label: n = "Character" } = {}) {
|
|
2369
|
-
const a =
|
|
2527
|
+
const a = D(n), o = uo({
|
|
2370
2528
|
label: n,
|
|
2371
2529
|
ends: { top: "Spacious", bottom: "Compact", left: "Subtle", right: "Bold" },
|
|
2372
|
-
onInput: (
|
|
2373
|
-
const l = +(
|
|
2530
|
+
onInput: (s, i) => {
|
|
2531
|
+
const l = +(We - i * (We - Nn)).toFixed(3), d = +s.toFixed(3);
|
|
2374
2532
|
t.setSections({
|
|
2375
2533
|
density: { factor: l === 1 ? null : l, spacing: null },
|
|
2376
2534
|
character: { bold: d === 0.5 ? null : d }
|
|
@@ -2378,61 +2536,61 @@ const De = (e) => e && e[0].toUpperCase() + e.slice(1), En = [
|
|
|
2378
2536
|
}
|
|
2379
2537
|
});
|
|
2380
2538
|
a.append(o.el);
|
|
2381
|
-
const
|
|
2382
|
-
const
|
|
2383
|
-
o.setValue(d, (
|
|
2539
|
+
const r = () => {
|
|
2540
|
+
const s = t.getState(), i = H(s.density), l = Bt(s.character), d = l === null ? 0.5 : l, p = `${Vo(s)} · ${Uo(d)}`;
|
|
2541
|
+
o.setValue(d, (We - i) / (We - Nn), p), a.setValue(p);
|
|
2384
2542
|
};
|
|
2385
|
-
|
|
2543
|
+
r(), t.onChange(r), e.append(a.el);
|
|
2386
2544
|
}
|
|
2387
|
-
},
|
|
2545
|
+
}, $t = [
|
|
2388
2546
|
{ key: "page-pad", label: "Page padding", step: 5 },
|
|
2389
2547
|
{ key: "section-gap", label: "Section gap", step: 6 },
|
|
2390
2548
|
{ key: "card-pad", label: "Card padding", step: 5 },
|
|
2391
2549
|
{ key: "card-gap", label: "Card gap", step: 2 },
|
|
2392
2550
|
{ key: "control-gap", label: "Control gap", step: 3 },
|
|
2393
2551
|
{ key: "control-pad-x", label: "Control padding", step: null }
|
|
2394
|
-
],
|
|
2552
|
+
], Ho = [1, 2, 3, 4, 5, 6, 7, 8], _o = /^var\(\s*--twd-space-(\d)\s*\)$/, Et = (e) => {
|
|
2395
2553
|
const t = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
|
|
2396
2554
|
return t ? +(parseFloat(t[1]) * (t[2] === "rem" ? 16 : 1)).toFixed(1) : null;
|
|
2397
2555
|
};
|
|
2398
|
-
function
|
|
2556
|
+
function Ko(e, t, n) {
|
|
2399
2557
|
const a = `--twd-${t.key}`, o = document.createElement("div");
|
|
2400
2558
|
o.className = "role-row", n || o.classList.add("unused"), o.innerHTML = `
|
|
2401
2559
|
<span class="role-label" title="${a}${n ? "" : " (not used on this page)"}">${t.label}</span>
|
|
2402
2560
|
<label class="token-value role-fixed" hidden><input type="text" inputmode="decimal" spellcheck="false" aria-label="${t.label}, fixed value"><span class="token-unit">px</span></label>
|
|
2403
2561
|
<span class="step-select">
|
|
2404
2562
|
<select aria-label="${t.label}"></select>
|
|
2405
|
-
<span class="step-chevron" aria-hidden="true">${
|
|
2563
|
+
<span class="step-chevron" aria-hidden="true">${P.down}</span>
|
|
2406
2564
|
</span>
|
|
2407
|
-
<button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${t.label}" hidden>${
|
|
2565
|
+
<button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${t.label}" hidden>${P.reset}</button>
|
|
2408
2566
|
`;
|
|
2409
|
-
const
|
|
2567
|
+
const r = o.querySelector("select"), s = o.querySelector(".role-fixed"), i = s.querySelector("input"), l = o.querySelector(".token-reset"), d = () => (e.getState().custom || {})[t.key], p = () => {
|
|
2410
2568
|
const x = d();
|
|
2411
|
-
if (x == null) return t.step ? { step: t.step } : { fixed:
|
|
2412
|
-
const b =
|
|
2569
|
+
if (x == null) return t.step ? { step: t.step } : { fixed: L(a) };
|
|
2570
|
+
const b = _o.exec(x);
|
|
2413
2571
|
return b ? { step: +b[1] } : { fixed: x };
|
|
2414
|
-
},
|
|
2415
|
-
|
|
2416
|
-
if (
|
|
2417
|
-
const b =
|
|
2418
|
-
|
|
2572
|
+
}, f = (x) => e.setSection("custom", { [t.key]: x }), v = (x) => t.step ? x === `var(--twd-space-${t.step})` : !1;
|
|
2573
|
+
r.addEventListener("change", () => {
|
|
2574
|
+
if (r.value === "fixed") {
|
|
2575
|
+
const b = Et(L(a));
|
|
2576
|
+
f(b != null ? `${b}px` : L(a)), i.focus(), i.select();
|
|
2419
2577
|
return;
|
|
2420
2578
|
}
|
|
2421
|
-
const x = `var(--twd-space-${
|
|
2422
|
-
|
|
2579
|
+
const x = `var(--twd-space-${r.value})`;
|
|
2580
|
+
f(v(x) ? null : x);
|
|
2423
2581
|
});
|
|
2424
|
-
const
|
|
2582
|
+
const u = () => {
|
|
2425
2583
|
const x = i.value.trim();
|
|
2426
|
-
if (!x) return
|
|
2427
|
-
|
|
2584
|
+
if (!x) return f(null);
|
|
2585
|
+
f(/^-?\d*\.?\d+$/.test(x) ? `${x}px` : x);
|
|
2428
2586
|
};
|
|
2429
|
-
i.addEventListener("change",
|
|
2587
|
+
i.addEventListener("change", u), i.addEventListener("blur", () => m()), i.addEventListener("keydown", (x) => {
|
|
2430
2588
|
x.key === "Enter" && (x.preventDefault(), i.blur()), x.key === "Escape" && (x.stopPropagation(), m(), i.blur());
|
|
2431
|
-
}), l.addEventListener("click", () =>
|
|
2589
|
+
}), l.addEventListener("click", () => f(null));
|
|
2432
2590
|
function m() {
|
|
2433
2591
|
const x = p();
|
|
2434
|
-
if (
|
|
2435
|
-
const h =
|
|
2592
|
+
if (r.innerHTML = Ho.map((h) => `<option value="${h}">space-${h} · ${Et(L(`--twd-space-${h}`)) ?? "?"}</option>`).join("") + '<option value="fixed">Fixed</option>', r.value = x.step ? String(x.step) : "fixed", s.hidden = !x.fixed, o.classList.toggle("is-fixed", !!x.fixed), x.fixed && o.getRootNode().activeElement !== i) {
|
|
2593
|
+
const h = Et(x.fixed);
|
|
2436
2594
|
i.value = h != null ? String(h) : x.fixed;
|
|
2437
2595
|
}
|
|
2438
2596
|
const b = d() != null;
|
|
@@ -2440,18 +2598,18 @@ function Co(e, t, n) {
|
|
|
2440
2598
|
}
|
|
2441
2599
|
return m(), e.onChange(m), o;
|
|
2442
2600
|
}
|
|
2443
|
-
const
|
|
2601
|
+
const Wo = {
|
|
2444
2602
|
spacing(e, t, { label: n = "Roles" } = {}) {
|
|
2445
|
-
const a =
|
|
2603
|
+
const a = co(), o = D(n);
|
|
2446
2604
|
o.el.classList.add("token-field");
|
|
2447
|
-
const
|
|
2448
|
-
o.setValue(
|
|
2449
|
-
const
|
|
2450
|
-
|
|
2605
|
+
const r = $t.filter((l) => !a.has(`--twd-${l.key}`));
|
|
2606
|
+
o.setValue(r.length === $t.length ? "not used on this page" : "");
|
|
2607
|
+
const s = document.createElement("div");
|
|
2608
|
+
s.className = "token-rows", $t.forEach((l) => s.append(Ko(t, l, a.has(`--twd-${l.key}`)))), o.append(s), e.append(o.el);
|
|
2451
2609
|
const i = document.createElement("p");
|
|
2452
|
-
i.className = "role-note", i.textContent =
|
|
2610
|
+
i.className = "role-note", i.textContent = r.length ? `Only elements styled with these tokens change. Not used on this page: ${r.map((l) => l.label).join(", ")}.` : "Each role points at a spacing step, so Density still scales it. Pick another step, or a fixed value.", e.append(i);
|
|
2453
2611
|
}
|
|
2454
|
-
},
|
|
2612
|
+
}, te = [
|
|
2455
2613
|
{ id: "overview", title: "Overview", icon: "tabOverview", parts: [["simple", "character"], ["simple", "summary"]] },
|
|
2456
2614
|
{ id: "colour", title: "Colour", icon: "tabColour", parts: [
|
|
2457
2615
|
["colour", "themeTiles"],
|
|
@@ -2467,6 +2625,7 @@ const Mo = {
|
|
|
2467
2625
|
["typography", "fontTiles"],
|
|
2468
2626
|
["typography", "base"],
|
|
2469
2627
|
["typography", "scaleTiles"],
|
|
2628
|
+
["typography", "weights"],
|
|
2470
2629
|
{ more: "Type tokens", parts: [["custom", "fonts"], ["custom", "type"], ["custom", "leading"], ["custom", "weights"]] }
|
|
2471
2630
|
] },
|
|
2472
2631
|
{ id: "spacing", title: "Spacing", icon: "tabSpacing", parts: [
|
|
@@ -2475,15 +2634,21 @@ const Mo = {
|
|
|
2475
2634
|
{ more: "Spacing tokens", parts: [["custom", "spacing"]] }
|
|
2476
2635
|
] },
|
|
2477
2636
|
{ id: "shape", title: "Shape", icon: "tabShape", parts: [
|
|
2478
|
-
//
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2637
|
+
// Shape pass (Ravi, 2026-10-16; Figma 281:1158 / 281:1421): one group at a
|
|
2638
|
+
// time, so the tab fits without scrolling. Buttons & Inputs: corners and size
|
|
2639
|
+
// also change inputs and chips (shared control tokens).
|
|
2640
|
+
{ tabs: [
|
|
2641
|
+
{ label: "Buttons & Inputs", parts: [
|
|
2642
|
+
["buttons", "styleTiles", { label: "Style" }],
|
|
2643
|
+
["buttons", "cornerTiles", { label: "Corners" }],
|
|
2644
|
+
["buttons", "sizeTiles", { label: "Size" }]
|
|
2645
|
+
] },
|
|
2646
|
+
{ label: "Cards", parts: [
|
|
2647
|
+
["cards", "styleTiles", { label: "Style" }],
|
|
2648
|
+
["cards", "cornerTiles", { label: "Corners" }],
|
|
2649
|
+
["cards", "borderTiles", { label: "Borders" }]
|
|
2650
|
+
] }
|
|
2651
|
+
] },
|
|
2487
2652
|
{ more: "Shape tokens", parts: [["custom", "buttonStyle"], ["custom", "cardStyle"], ["custom", "cardShadow"], ["custom", "corners"], ["custom", "controls"], ["custom", "borders"]] }
|
|
2488
2653
|
] },
|
|
2489
2654
|
{ id: "shadows", title: "Shadows", icon: "tabShadows", parts: [
|
|
@@ -2491,62 +2656,59 @@ const Mo = {
|
|
|
2491
2656
|
["depth", "intensity"],
|
|
2492
2657
|
{ more: "Shadow tokens", parts: [["custom", "shadows"]] }
|
|
2493
2658
|
] }
|
|
2494
|
-
],
|
|
2495
|
-
function
|
|
2659
|
+
], F = (e) => String(e).replace(/[&<>"]/g, (t) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[t]);
|
|
2660
|
+
function Go({ sections: e, store: t, view: n }) {
|
|
2496
2661
|
const a = document.createElement("div");
|
|
2497
2662
|
a.id = "twiddle-root", document.body.appendChild(a);
|
|
2498
|
-
const o = a.attachShadow({ mode: "open" })
|
|
2499
|
-
<details data-section="${g.id}">
|
|
2500
|
-
<summary>${g.title}<span class="chev">${A.chevron}</span></summary>
|
|
2501
|
-
<div class="section-body"><div class="placeholder">${g.hint}</div></div>
|
|
2502
|
-
</details>
|
|
2503
|
-
`).join("");
|
|
2663
|
+
const o = a.attachShadow({ mode: "open" });
|
|
2504
2664
|
o.innerHTML = `
|
|
2505
|
-
<style>${
|
|
2665
|
+
<style>${Da}</style>
|
|
2506
2666
|
<div class="pick-box" hidden><span class="pick-tag"></span></div>
|
|
2507
2667
|
<div class="wrap">
|
|
2508
2668
|
<div class="toast" role="status"></div>
|
|
2669
|
+
<section class="side" aria-label="Changes" hidden>
|
|
2670
|
+
<div class="side-head"><span>Changes</span><button class="icon-btn" data-action="changes-close" title="Close Changes" aria-label="Close Changes">${P.close}</button></div>
|
|
2671
|
+
<div class="body" data-view="changes"></div>
|
|
2672
|
+
</section>
|
|
2509
2673
|
<div class="panel" role="dialog" aria-label="Panel" hidden>
|
|
2510
|
-
<
|
|
2511
|
-
|
|
2512
|
-
<button class="icon-btn" data-action="
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
<button class="icon-btn" data-action="close" title="Close (Alt+T)" aria-label="Close">${A.close}</button>
|
|
2674
|
+
<nav class="tabbar" aria-label="Areas">
|
|
2675
|
+
${te.map((c) => `<button class="tab" data-tab="${c.id}" title="${c.title}" aria-pressed="false">${P[c.icon]}<span>${c.title}</span></button>`).join("")}
|
|
2676
|
+
<button class="icon-btn tab-min" data-action="minimise" title="Minimise to the tabs" aria-label="Minimise to the tabs">${P.minimise}</button>
|
|
2677
|
+
</nav>
|
|
2678
|
+
<div class="original-pill" hidden>Showing original</div>
|
|
2679
|
+
<div class="body" data-view="global"><div class="setup-hint" role="status" hidden></div>
|
|
2680
|
+
${te.map((c) => `<div class="page page-fields" data-page="${c.id}" hidden></div>`).join("")}
|
|
2518
2681
|
</div>
|
|
2682
|
+
<div class="body" data-view="local" hidden></div>
|
|
2683
|
+
<!-- One footer row with every action (Ravi, 2026-10-16); the launcher closes the panel. -->
|
|
2519
2684
|
<div class="actions">
|
|
2520
|
-
<button class="icon-btn" data-action="undo" title="Undo (⌘Z)" aria-label="Undo">${
|
|
2521
|
-
<button class="icon-btn" data-action="redo" title="Redo (⇧⌘Z)" aria-label="Redo">${
|
|
2522
|
-
<
|
|
2685
|
+
<button class="icon-btn" data-action="undo" title="Undo (⌘Z)" aria-label="Undo">${P.undo}</button>
|
|
2686
|
+
<button class="icon-btn" data-action="redo" title="Redo (⇧⌘Z)" aria-label="Redo">${P.redo}</button>
|
|
2687
|
+
<span class="sep" aria-hidden="true"></span>
|
|
2688
|
+
<button class="icon-btn" data-action="pick" title="Pick an element to tweak on its own" aria-label="Pick an element" aria-pressed="false">${P.pick}</button>
|
|
2689
|
+
<button class="icon-btn" data-action="compare" title="Hold to see the original (Alt+C toggles)" aria-label="Compare with original" aria-pressed="false">${P.compare}</button>
|
|
2690
|
+
<button class="icon-btn" data-action="copy" title="Copy link to this version" aria-label="Copy link">${P.link}</button>
|
|
2691
|
+
<button class="icon-btn" data-action="reset" title="Reset to defaults" aria-label="Reset">${P.reset}</button>
|
|
2692
|
+
<span class="sep" aria-hidden="true"></span>
|
|
2693
|
+
<button class="changes-btn" data-action="changes" title="Changes" aria-label="Changes" aria-pressed="false"><span class="badge">0</span></button>
|
|
2523
2694
|
<span class="spacer"></span>
|
|
2524
2695
|
<div class="apply">
|
|
2525
|
-
<button class="apply-btn" data-action="apply" title="Copy these tweaks as a prompt for your coding agent">Copy prompt</button>
|
|
2526
|
-
<button class="apply-menu-btn" data-action="apply-menu" aria-label="Copy prompt options" aria-haspopup="true" aria-expanded="false">${
|
|
2696
|
+
<button class="apply-btn" data-action="apply" title="Copy these tweaks as a prompt for your coding agent">Copy<span class="wide"> prompt</span></button>
|
|
2697
|
+
<button class="apply-menu-btn" data-action="apply-menu" aria-label="Copy prompt options" aria-haspopup="true" aria-expanded="false">${P.down}</button>
|
|
2527
2698
|
<div class="menu apply-menu" role="menu" hidden>
|
|
2528
2699
|
<button role="menuitemradio" data-apply-mode="preview">Preview before copying</button>
|
|
2529
2700
|
<button role="menuitemradio" data-apply-mode="copy">Copy without preview</button>
|
|
2530
2701
|
</div>
|
|
2531
2702
|
</div>
|
|
2532
2703
|
</div>
|
|
2533
|
-
<div class="original-pill" hidden>Showing original</div>
|
|
2534
|
-
<div class="body" data-view="global"><div class="setup-hint" role="status" hidden></div>
|
|
2535
|
-
${ye.map((c) => `<div class="page page-fields" data-page="${c.id}" hidden></div>`).join("")}
|
|
2536
|
-
</div>
|
|
2537
|
-
<div class="body" data-view="local" hidden></div>
|
|
2538
|
-
<div class="body" data-view="changes" hidden></div>
|
|
2539
|
-
<nav class="tabbar" aria-label="Areas">
|
|
2540
|
-
${ye.map((c) => `<button class="tab" data-tab="${c.id}" aria-pressed="false">${A[c.icon]}<span>${c.title}</span></button>`).join("")}
|
|
2541
|
-
</nav>
|
|
2542
2704
|
</div>
|
|
2543
|
-
<button class="launcher" aria-label="Open Panel" aria-expanded="false" title="Panel · drag to move, double-click to reset">${
|
|
2705
|
+
<button class="launcher" aria-label="Open Panel" aria-expanded="false" title="Panel · drag to move, double-click to reset">${P.panelMark}</button>
|
|
2544
2706
|
</div>
|
|
2545
2707
|
<div class="dialog-backdrop" hidden>
|
|
2546
2708
|
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="twd-dialog-title">
|
|
2547
2709
|
<div class="dialog-head">
|
|
2548
2710
|
<span id="twd-dialog-title">Prompt for your agent</span>
|
|
2549
|
-
<button class="icon-btn" data-action="dialog-close" aria-label="Close">${
|
|
2711
|
+
<button class="icon-btn" data-action="dialog-close" aria-label="Close">${P.close}</button>
|
|
2550
2712
|
</div>
|
|
2551
2713
|
<textarea class="prompt-text" readonly spellcheck="false" aria-label="Generated prompt"></textarea>
|
|
2552
2714
|
<div class="dialog-foot">
|
|
@@ -2556,432 +2718,452 @@ function To({ sections: e, store: t, view: n }) {
|
|
|
2556
2718
|
</div>
|
|
2557
2719
|
</div>
|
|
2558
2720
|
`;
|
|
2559
|
-
const r = (c) => o.querySelector(c),
|
|
2560
|
-
function m(c, g,
|
|
2721
|
+
const r = (c) => o.querySelector(c), s = r(".panel"), i = r(".launcher"), l = r(".toast"), d = { global: r('[data-view="global"]'), local: r('[data-view="local"]'), changes: r('[data-view="changes"]') }, p = r(".pick-box"), f = r(".pick-tag"), v = { getState: t.getState, setSection: t.setSection, setSections: t.setSections, onChange: t.onChange }, u = Object.fromEntries(e.map((c) => [c.id, c]));
|
|
2722
|
+
function m(c, g, y = v) {
|
|
2723
|
+
const C = y !== v;
|
|
2561
2724
|
g.forEach((k) => {
|
|
2562
|
-
if (
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
const $ = [...c.querySelectorAll("details")];
|
|
2567
|
-
return $.forEach((k) => k.addEventListener("toggle", () => {
|
|
2568
|
-
k.open && $.forEach((C) => {
|
|
2569
|
-
C !== k && (C.open = !1);
|
|
2570
|
-
});
|
|
2571
|
-
})), $;
|
|
2572
|
-
}
|
|
2573
|
-
const x = Object.fromEntries(e.map((c) => [c.id, c]));
|
|
2574
|
-
function b(c, g) {
|
|
2575
|
-
g.forEach((w) => {
|
|
2576
|
-
if (w === "warnings") {
|
|
2577
|
-
const S = document.createElement("div");
|
|
2578
|
-
S.className = "warnings page-warnings", c.append(S);
|
|
2725
|
+
if (k === "warnings") {
|
|
2726
|
+
if (C) return;
|
|
2727
|
+
const j = document.createElement("div");
|
|
2728
|
+
j.className = "warnings page-warnings", c.append(j);
|
|
2579
2729
|
return;
|
|
2580
2730
|
}
|
|
2581
|
-
if (
|
|
2582
|
-
const
|
|
2583
|
-
|
|
2731
|
+
if (k.heading) {
|
|
2732
|
+
const j = document.createElement("div");
|
|
2733
|
+
j.className = "page-heading", j.innerHTML = `<span></span>${k.note ? '<span class="muted"></span>' : ""}`, j.firstChild.textContent = k.heading, k.note && (j.lastChild.textContent = k.note), c.append(j);
|
|
2584
2734
|
return;
|
|
2585
2735
|
}
|
|
2586
|
-
if (
|
|
2587
|
-
const
|
|
2588
|
-
|
|
2736
|
+
if (k.more) {
|
|
2737
|
+
const j = document.createElement("details");
|
|
2738
|
+
j.className = "more", j.innerHTML = `<summary><span class="chev" aria-hidden="true">${P.chevron}</span><span></span></summary><div class="more-body"></div>`, j.querySelector("summary span:last-child").textContent = k.more, m(j.querySelector(".more-body"), k.parts, y), c.append(j);
|
|
2589
2739
|
return;
|
|
2590
2740
|
}
|
|
2591
|
-
if (
|
|
2592
|
-
const
|
|
2593
|
-
const
|
|
2594
|
-
return
|
|
2595
|
-
}),
|
|
2741
|
+
if (k.tabs) {
|
|
2742
|
+
const j = k.tabs.map((W) => {
|
|
2743
|
+
const q = document.createElement("div");
|
|
2744
|
+
return q.className = "tab-pane", m(q, W.parts, y), q;
|
|
2745
|
+
}), dt = ge({
|
|
2596
2746
|
label: "View",
|
|
2597
|
-
options:
|
|
2598
|
-
onSelect: (
|
|
2599
|
-
|
|
2600
|
-
|
|
2747
|
+
options: k.tabs.map((W, q) => ({ value: String(q), label: W.label })),
|
|
2748
|
+
onSelect: (W) => {
|
|
2749
|
+
dt.setValue(W), j.forEach((q, va) => {
|
|
2750
|
+
q.hidden = String(va) !== W;
|
|
2601
2751
|
});
|
|
2602
2752
|
}
|
|
2603
2753
|
});
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
}), c.append(
|
|
2754
|
+
dt.setValue("0"), j.forEach((W, q) => {
|
|
2755
|
+
W.hidden = q !== 0;
|
|
2756
|
+
}), c.append(dt.el, ...j);
|
|
2607
2757
|
return;
|
|
2608
2758
|
}
|
|
2609
|
-
const [
|
|
2610
|
-
|
|
2611
|
-
|
|
2759
|
+
const [T, U, O = {}] = k, ye = T === "simple" ? qo[U] : T === "roles" ? Wo[U] : u[T]?.parts?.[U];
|
|
2760
|
+
ye && ye(c, y, { ...O, buttons: u.buttons, cards: u.cards, goTab: (j) => {
|
|
2761
|
+
Ae(Ut()), w(j);
|
|
2612
2762
|
} });
|
|
2613
2763
|
});
|
|
2614
2764
|
}
|
|
2615
|
-
|
|
2616
|
-
const
|
|
2617
|
-
let
|
|
2618
|
-
function
|
|
2619
|
-
|
|
2620
|
-
}
|
|
2621
|
-
function
|
|
2622
|
-
|
|
2623
|
-
g.
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
}
|
|
2633
|
-
|
|
2634
|
-
const
|
|
2635
|
-
function
|
|
2636
|
-
|
|
2637
|
-
|
|
2765
|
+
te.forEach((c) => m(d.global.querySelector(`[data-page="${c.id}"]`), c.parts));
|
|
2766
|
+
const x = [...o.querySelectorAll(".tab")];
|
|
2767
|
+
let b = te.some((c) => c.id === ne("tab")) ? ne("tab") : te[0].id;
|
|
2768
|
+
function h() {
|
|
2769
|
+
x.forEach((c) => c.setAttribute("aria-pressed", String(c.dataset.tab === b)));
|
|
2770
|
+
}
|
|
2771
|
+
function w(c) {
|
|
2772
|
+
b = c, ce("tab", c), [d.global, d.local].forEach((g) => {
|
|
2773
|
+
g.querySelectorAll(".page").forEach((y) => {
|
|
2774
|
+
y.hidden = y.dataset.page !== c;
|
|
2775
|
+
}), g.scrollTop = 0;
|
|
2776
|
+
}), h();
|
|
2777
|
+
}
|
|
2778
|
+
const S = (c) => c.map((g) => `<div class="warning">${P.warn}<span>${F(g.text)}</span></div>`).join(""), $ = r(".side");
|
|
2779
|
+
let K = !1;
|
|
2780
|
+
function Z(c) {
|
|
2781
|
+
K = c && !s.hidden, $.hidden = !K, r('[data-action="changes"]').setAttribute("aria-pressed", String(K)), K && rn();
|
|
2782
|
+
}
|
|
2783
|
+
let Le = "global";
|
|
2784
|
+
const Ut = () => E ? "local" : "global";
|
|
2785
|
+
function Ae(c) {
|
|
2786
|
+
Le = c, Object.entries(d).forEach(([g, y]) => {
|
|
2787
|
+
y.hidden = g !== c;
|
|
2788
|
+
}), h();
|
|
2789
|
+
}
|
|
2790
|
+
w(b);
|
|
2791
|
+
const et = "cubic-bezier(0.3, 0.7, 0.2, 1)", tt = "cubic-bezier(0.2, 0.8, 0.2, 1)", Pe = 640, me = 100, nt = 50, da = () => i.querySelector(".mark-knob"), ca = () => [i.querySelector(".mark-t1"), i.querySelector(".mark-t2")];
|
|
2792
|
+
function Vt(c) {
|
|
2793
|
+
if (matchMedia("(prefers-reduced-motion: reduce)").matches || !i.animate) return;
|
|
2794
|
+
const g = da(), [y, C] = ca(), k = (T) => `translateY(${T}px)`;
|
|
2638
2795
|
if (c) {
|
|
2639
|
-
g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration:
|
|
2640
|
-
const
|
|
2641
|
-
|
|
2796
|
+
g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration: Pe, easing: et });
|
|
2797
|
+
const T = [{ transform: "scale(0.4)", opacity: 0 }, { transform: "scale(1)", opacity: 1 }];
|
|
2798
|
+
C.animate(T, { duration: me * 2, delay: Pe * 0.12, easing: et, fill: "backwards" }), y.animate(T, { duration: me * 2, delay: Pe * 0.32 + nt, easing: et, fill: "backwards" });
|
|
2642
2799
|
} else {
|
|
2643
|
-
const
|
|
2644
|
-
|
|
2800
|
+
const T = [{ opacity: 1 }, { opacity: 0 }];
|
|
2801
|
+
y.animate(T, { duration: me, easing: tt }), C.animate(T, { duration: me, delay: nt / 2, easing: tt, fill: "backwards" }), g.animate([{ transform: k(0) }, { transform: k(34), offset: 0.8 }, { transform: k(32) }], { duration: Pe * 0.7, delay: me + nt / 2, easing: tt, fill: "backwards" });
|
|
2645
2802
|
}
|
|
2646
2803
|
}
|
|
2647
|
-
const
|
|
2648
|
-
let
|
|
2649
|
-
function
|
|
2650
|
-
|
|
2804
|
+
const Oe = r(".wrap"), qt = Va({ wrapEl: Oe, launcherEl: i });
|
|
2805
|
+
let at = !1;
|
|
2806
|
+
function be(c) {
|
|
2807
|
+
at = c, s.classList.toggle("minimised", c), c && Z(!1), Oe.classList.toggle("is-min", c);
|
|
2808
|
+
const g = r('[data-action="minimise"]');
|
|
2809
|
+
g.title = c ? "Expand the panel" : "Minimise to the tabs", g.setAttribute("aria-label", g.title), s.hidden || ce("panel", c ? "tabs" : "open");
|
|
2651
2810
|
}
|
|
2652
|
-
function
|
|
2653
|
-
|
|
2811
|
+
function J({ animate: c = !0 } = {}) {
|
|
2812
|
+
s.hidden && c && Vt(!0), s.hidden = !1, Oe.classList.add("is-open"), i.setAttribute("aria-expanded", "true"), i.setAttribute("aria-label", "Close Panel"), be(!1);
|
|
2654
2813
|
}
|
|
2655
|
-
function
|
|
2656
|
-
|
|
2814
|
+
function pa() {
|
|
2815
|
+
s.hidden && J(), be(!0);
|
|
2657
2816
|
}
|
|
2658
|
-
function
|
|
2659
|
-
|
|
2817
|
+
function ot() {
|
|
2818
|
+
s.hidden || Vt(!1), s.hidden = !0, Oe.classList.remove("is-open", "is-min"), Z(!1), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-label", "Open Panel"), ce("panel", "closed"), Re();
|
|
2660
2819
|
}
|
|
2661
|
-
function
|
|
2662
|
-
|
|
2820
|
+
function je() {
|
|
2821
|
+
s.hidden ? J() : ot();
|
|
2663
2822
|
}
|
|
2664
|
-
let
|
|
2665
|
-
function
|
|
2666
|
-
|
|
2823
|
+
let Ht;
|
|
2824
|
+
function V(c) {
|
|
2825
|
+
l.textContent = c, l.classList.add("show"), clearTimeout(Ht), Ht = setTimeout(() => l.classList.remove("show"), 1600);
|
|
2667
2826
|
}
|
|
2668
|
-
function
|
|
2669
|
-
navigator.clipboard.writeText(c).then(() =>
|
|
2827
|
+
function xe(c, g) {
|
|
2828
|
+
navigator.clipboard.writeText(c).then(() => V(g)).catch(() => V("Couldn’t copy — select the text and copy it instead"));
|
|
2670
2829
|
}
|
|
2671
|
-
let
|
|
2672
|
-
function
|
|
2673
|
-
if (
|
|
2674
|
-
|
|
2830
|
+
let rt = null;
|
|
2831
|
+
function se(c, g) {
|
|
2832
|
+
if (rt = c, !c) {
|
|
2833
|
+
p.hidden = !0;
|
|
2675
2834
|
return;
|
|
2676
2835
|
}
|
|
2677
|
-
const
|
|
2678
|
-
Object.assign(
|
|
2836
|
+
const y = c.getBoundingClientRect();
|
|
2837
|
+
Object.assign(p.style, { top: `${y.top}px`, left: `${y.left}px`, width: `${y.width}px`, height: `${y.height}px` }), f.textContent = g || Ot(c), p.hidden = !1;
|
|
2679
2838
|
}
|
|
2680
|
-
const
|
|
2681
|
-
|
|
2839
|
+
const _t = () => {
|
|
2840
|
+
rt && !p.hidden && se(rt, f.textContent);
|
|
2682
2841
|
};
|
|
2683
|
-
window.addEventListener("scroll",
|
|
2684
|
-
let
|
|
2685
|
-
const
|
|
2686
|
-
function
|
|
2687
|
-
if (
|
|
2842
|
+
window.addEventListener("scroll", _t, !0), window.addEventListener("resize", _t);
|
|
2843
|
+
let ie = !1;
|
|
2844
|
+
const Fe = r('[data-action="pick"]'), Kt = (c) => c.composedPath().includes(a);
|
|
2845
|
+
function Wt(c) {
|
|
2846
|
+
if (Kt(c)) return;
|
|
2688
2847
|
const g = c.target;
|
|
2689
|
-
g && g.nodeType === 1 && g !== document.documentElement && g !== document.body &&
|
|
2848
|
+
g && g.nodeType === 1 && g !== document.documentElement && g !== document.body && se(g);
|
|
2690
2849
|
}
|
|
2691
|
-
function
|
|
2692
|
-
if (
|
|
2850
|
+
function Gt(c) {
|
|
2851
|
+
if (Kt(c) || (c.preventDefault(), c.stopPropagation(), c.type !== "click")) return;
|
|
2693
2852
|
const g = c.target;
|
|
2694
|
-
!g || g === document.documentElement || g === document.body || (
|
|
2853
|
+
!g || g === document.documentElement || g === document.body || (Re(), Jt(g));
|
|
2695
2854
|
}
|
|
2696
|
-
function
|
|
2697
|
-
c.key === "Escape" && (c.preventDefault(),
|
|
2855
|
+
function Yt(c) {
|
|
2856
|
+
c.key === "Escape" && (c.preventDefault(), Re(), E && se(E.el), Ne());
|
|
2698
2857
|
}
|
|
2699
|
-
const
|
|
2700
|
-
function
|
|
2701
|
-
if (!
|
|
2702
|
-
if (
|
|
2858
|
+
const Xt = ["pointerdown", "mousedown", "mouseup", "click"];
|
|
2859
|
+
function Zt() {
|
|
2860
|
+
if (!ie) {
|
|
2861
|
+
if (ie = !0, !document.getElementById("twiddle-picking")) {
|
|
2703
2862
|
const c = document.createElement("style");
|
|
2704
2863
|
c.id = "twiddle-picking", c.textContent = "html.twd-picking, html.twd-picking * { cursor: crosshair !important; }", document.head.appendChild(c);
|
|
2705
2864
|
}
|
|
2706
|
-
|
|
2865
|
+
J(), Fe.setAttribute("aria-pressed", "true"), document.documentElement.classList.add("twd-picking"), document.addEventListener("pointermove", Wt, !0), Xt.forEach((c) => document.addEventListener(c, Gt, !0)), document.addEventListener("keydown", Yt, !0), V("Click an element · Esc to cancel");
|
|
2707
2866
|
}
|
|
2708
2867
|
}
|
|
2709
|
-
function
|
|
2710
|
-
|
|
2868
|
+
function Ne() {
|
|
2869
|
+
const c = ie || !!E;
|
|
2870
|
+
Fe.setAttribute("aria-pressed", String(c));
|
|
2871
|
+
const g = c ? "Leave selection mode" : "Pick an element to tweak on its own";
|
|
2872
|
+
Fe.title = g, Fe.setAttribute("aria-label", c ? "Leave selection mode" : "Pick an element");
|
|
2873
|
+
}
|
|
2874
|
+
function Re() {
|
|
2875
|
+
ie && (ie = !1, Ne(), document.documentElement.classList.remove("twd-picking"), document.removeEventListener("pointermove", Wt, !0), Xt.forEach((c) => document.removeEventListener(c, Gt, !0)), document.removeEventListener("keydown", Yt, !0), se(E ? E.el : null));
|
|
2711
2876
|
}
|
|
2712
|
-
let
|
|
2713
|
-
function
|
|
2714
|
-
const
|
|
2877
|
+
let E = null;
|
|
2878
|
+
function ua(c, g) {
|
|
2879
|
+
const y = [];
|
|
2715
2880
|
return {
|
|
2716
2881
|
ctx: {
|
|
2717
2882
|
getState: () => (t.getState().scopes || {})[c] || {},
|
|
2718
|
-
setSection: (
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2883
|
+
setSection: (C, k) => t.setScope(c, C, k),
|
|
2884
|
+
setSections: (C, k) => t.setScopeSections(c, C, k),
|
|
2885
|
+
onChange: (C) => {
|
|
2886
|
+
const k = t.onChange(C);
|
|
2887
|
+
return y.push(k), k;
|
|
2722
2888
|
},
|
|
2723
|
-
readToken: (
|
|
2889
|
+
readToken: (C) => getComputedStyle(g).getPropertyValue(C).trim()
|
|
2724
2890
|
},
|
|
2725
|
-
dispose: () =>
|
|
2891
|
+
dispose: () => y.forEach((C) => C())
|
|
2726
2892
|
};
|
|
2727
2893
|
}
|
|
2728
|
-
function
|
|
2729
|
-
const
|
|
2730
|
-
g || (g =
|
|
2731
|
-
} },
|
|
2894
|
+
function Jt(c, g) {
|
|
2895
|
+
const y = Ga(c), C = t.getState().scopes || {};
|
|
2896
|
+
g || (g = !C[y.elementSelector] && y.classSelector && C[y.classSelector] ? "class" : "element"), E && E.dispose(), E = { el: c, info: y, target: g, dispose: () => {
|
|
2897
|
+
} }, st(), Ae("local"), J(), se(c), Ne(), lt();
|
|
2732
2898
|
}
|
|
2733
|
-
const
|
|
2734
|
-
function
|
|
2735
|
-
|
|
2736
|
-
const { info: c } =
|
|
2737
|
-
if (
|
|
2899
|
+
const Qt = () => E.target === "class" ? E.info.classSelector : E.info.elementSelector;
|
|
2900
|
+
function st() {
|
|
2901
|
+
E.dispose();
|
|
2902
|
+
const { info: c } = E, g = d.local;
|
|
2903
|
+
if (g.innerHTML = `
|
|
2738
2904
|
<div class="scope-bar">
|
|
2739
2905
|
<div class="scope-head">
|
|
2740
|
-
<button class="icon-btn" data-action="back" title="
|
|
2906
|
+
<button class="icon-btn" data-action="back" title="Leave selection mode" aria-label="Leave selection mode">${P.back}</button>
|
|
2741
2907
|
<div class="scope-name">
|
|
2742
|
-
<code>${
|
|
2743
|
-
${c.text ? `<span class="muted">“${
|
|
2908
|
+
<code>${F(Ot(c.el))}</code>
|
|
2909
|
+
${c.text ? `<span class="muted">“${F(c.text)}”</span>` : ""}
|
|
2744
2910
|
</div>
|
|
2745
2911
|
</div>
|
|
2746
2912
|
<div class="scope-target"></div>
|
|
2913
|
+
${c.tokens.size ? "" : '<div class="scope-note">This element doesn’t use Panel tokens yet, so changes here won’t show. Style it with <code>var(--twd-…)</code> to make it tweakable.</div>'}
|
|
2747
2914
|
<div class="warnings"></div>
|
|
2748
2915
|
</div>
|
|
2749
|
-
${
|
|
2916
|
+
${te.map((O) => `<div class="page page-fields" data-page="${O.id}"${O.id === b ? "" : " hidden"}></div>`).join("")}
|
|
2750
2917
|
`, c.classSelector) {
|
|
2751
|
-
const
|
|
2918
|
+
const O = ge({
|
|
2752
2919
|
label: "Apply to",
|
|
2753
2920
|
options: [
|
|
2754
2921
|
{ value: "element", label: "This element" },
|
|
2755
2922
|
{ value: "class", label: `All ${c.classSelector} (${c.classCount})`, title: `Every element matching ${c.classSelector}` }
|
|
2756
2923
|
],
|
|
2757
|
-
onSelect: (
|
|
2758
|
-
|
|
2924
|
+
onSelect: (ye) => {
|
|
2925
|
+
ye === "class" && t.moveScope(c.elementSelector, c.classSelector), E.target = ye, st();
|
|
2759
2926
|
}
|
|
2760
2927
|
});
|
|
2761
|
-
|
|
2928
|
+
O.setValue(E.target), g.querySelector(".scope-target").append(O.el);
|
|
2762
2929
|
}
|
|
2763
|
-
const { ctx:
|
|
2764
|
-
|
|
2765
|
-
const
|
|
2766
|
-
const
|
|
2767
|
-
|
|
2930
|
+
const { ctx: y, dispose: C } = ua(Qt(), c.el);
|
|
2931
|
+
te.forEach((O) => m(g.querySelector(`[data-page="${O.id}"]`), O.parts, y));
|
|
2932
|
+
const k = g.querySelector(".warnings"), T = () => {
|
|
2933
|
+
const O = c.tokens.size ? ao(c.el) : null;
|
|
2934
|
+
k.innerHTML = O ? S([O]) : "";
|
|
2768
2935
|
};
|
|
2769
|
-
|
|
2770
|
-
const
|
|
2771
|
-
|
|
2772
|
-
|
|
2936
|
+
T();
|
|
2937
|
+
const U = t.onChange(T);
|
|
2938
|
+
E.dispose = () => {
|
|
2939
|
+
C(), U();
|
|
2773
2940
|
};
|
|
2774
2941
|
}
|
|
2775
|
-
function
|
|
2776
|
-
if (!
|
|
2777
|
-
const c = t.getState().scopes || {}, g =
|
|
2778
|
-
!c[
|
|
2942
|
+
function en() {
|
|
2943
|
+
if (!E || !E.info.classSelector) return;
|
|
2944
|
+
const c = t.getState().scopes || {}, g = E.target === "class" ? "element" : "class", y = g === "class" ? E.info.classSelector : E.info.elementSelector;
|
|
2945
|
+
!c[Qt()] && c[y] && (E.target = g, st());
|
|
2779
2946
|
}
|
|
2780
|
-
const
|
|
2781
|
-
t.undo(),
|
|
2782
|
-
},
|
|
2783
|
-
t.redo(),
|
|
2947
|
+
const tn = () => {
|
|
2948
|
+
t.undo(), en();
|
|
2949
|
+
}, nn = () => {
|
|
2950
|
+
t.redo(), en();
|
|
2784
2951
|
};
|
|
2785
|
-
function
|
|
2786
|
-
|
|
2952
|
+
function an() {
|
|
2953
|
+
E && (E.dispose(), E = null, d.local.innerHTML = "", se(null), Ne(), Le === "local" && Ae("global"));
|
|
2787
2954
|
}
|
|
2788
|
-
const
|
|
2789
|
-
function
|
|
2790
|
-
const c =
|
|
2955
|
+
const on = (c) => c.kind === "global" ? "Whole page" : c.kind === "class" ? `All ${c.selector}` : "One element";
|
|
2956
|
+
function rn() {
|
|
2957
|
+
const c = wn(t.getState(), e), g = ut(), y = d.changes;
|
|
2791
2958
|
if (!c.length) {
|
|
2792
|
-
|
|
2959
|
+
y.innerHTML = `<div class="empty">No changes yet. Tweak a section, or pick an element to change it on its own.</div>${S(g)}`;
|
|
2793
2960
|
return;
|
|
2794
2961
|
}
|
|
2795
|
-
const
|
|
2962
|
+
const C = /* @__PURE__ */ new Map();
|
|
2796
2963
|
c.forEach((k) => {
|
|
2797
|
-
const
|
|
2798
|
-
|
|
2799
|
-
}),
|
|
2964
|
+
const T = k.selector || "";
|
|
2965
|
+
C.has(T) || C.set(T, []), C.get(T).push(k);
|
|
2966
|
+
}), y.innerHTML = (g.length ? `<div class="warnings">${S(g)}</div>` : "") + [...C].map(([k, T]) => `
|
|
2800
2967
|
<div class="change-group">
|
|
2801
2968
|
<div class="change-scope">
|
|
2802
|
-
${k ? `<button class="link-btn" data-select="${
|
|
2969
|
+
${k ? `<button class="link-btn" data-select="${F(k)}" title="Select it on the page">${F(on(T[0]))}</button><code>${F(k)}</code>` : `<span>${on(T[0])}</span>`}
|
|
2803
2970
|
</div>
|
|
2804
|
-
${
|
|
2971
|
+
${T.map((U) => `
|
|
2805
2972
|
<div class="change-row">
|
|
2806
2973
|
<div class="change-main">
|
|
2807
|
-
<div class="change-title">${
|
|
2808
|
-
${
|
|
2974
|
+
<div class="change-title">${F(U.title)} <span class="muted">›</span> ${U.settings.map((O) => `${F(O.label)}: <b>${F(O.value)}</b>`).join(", ")}</div>
|
|
2975
|
+
${U.tokens.map((O) => `<div class="change-token"><code>${F(O.name)}</code> ${O.from ? `${F(O.from)} → ` : ""}${F(O.to)}</div>`).join("")}
|
|
2809
2976
|
</div>
|
|
2810
|
-
<button class="icon-btn" data-remove="${
|
|
2977
|
+
<button class="icon-btn" data-remove="${F(U.sectionId)}" data-remove-scope="${F(k)}" aria-label="Remove ${F(U.title)} change">${P.close}</button>
|
|
2811
2978
|
</div>`).join("")}
|
|
2812
2979
|
</div>`).join("");
|
|
2813
2980
|
}
|
|
2814
|
-
function
|
|
2981
|
+
function ha(c, g) {
|
|
2815
2982
|
if (g) return t.removeScope(g, c);
|
|
2816
|
-
const
|
|
2817
|
-
t.setSection(c, Object.fromEntries(Object.keys(
|
|
2983
|
+
const y = t.getState()[c] || {};
|
|
2984
|
+
t.setSection(c, Object.fromEntries(Object.keys(y).map((C) => [C, null])));
|
|
2818
2985
|
}
|
|
2819
|
-
const
|
|
2820
|
-
function
|
|
2821
|
-
|
|
2986
|
+
const we = r(".apply-menu"), sn = r('[data-action="apply-menu"]'), ln = () => ne("apply-mode") === "copy" ? "copy" : "preview";
|
|
2987
|
+
function fa() {
|
|
2988
|
+
we.querySelectorAll("[data-apply-mode]").forEach((c) => c.setAttribute("aria-checked", String(c.dataset.applyMode === ln())));
|
|
2822
2989
|
}
|
|
2823
|
-
function
|
|
2824
|
-
|
|
2990
|
+
function ze(c) {
|
|
2991
|
+
we.hidden = !c, sn.setAttribute("aria-expanded", String(c)), c && fa();
|
|
2825
2992
|
}
|
|
2826
|
-
function
|
|
2827
|
-
return
|
|
2993
|
+
function dn() {
|
|
2994
|
+
return Qa(wn(t.getState(), e), { warnings: ut() });
|
|
2828
2995
|
}
|
|
2829
|
-
const
|
|
2830
|
-
function
|
|
2831
|
-
const c =
|
|
2832
|
-
if (!c) return
|
|
2833
|
-
if (
|
|
2834
|
-
|
|
2996
|
+
const Ie = r(".dialog-backdrop"), cn = r(".prompt-text");
|
|
2997
|
+
function ga() {
|
|
2998
|
+
const c = dn();
|
|
2999
|
+
if (!c) return V("No changes to copy yet");
|
|
3000
|
+
if (ln() === "copy") return xe(c, "Prompt copied · paste it into your agent");
|
|
3001
|
+
cn.value = c, Ie.hidden = !1, r('[data-action="dialog-copy"]').focus();
|
|
2835
3002
|
}
|
|
2836
|
-
function
|
|
2837
|
-
|
|
3003
|
+
function it() {
|
|
3004
|
+
Ie.hidden = !0, r('[data-action="apply"]').focus();
|
|
2838
3005
|
}
|
|
2839
|
-
const
|
|
2840
|
-
function
|
|
2841
|
-
c ? n.suspend() : n.resume(),
|
|
3006
|
+
const le = r('[data-action="compare"]'), ma = r(".original-pill");
|
|
3007
|
+
function De(c) {
|
|
3008
|
+
c ? n.suspend() : n.resume(), le.setAttribute("aria-pressed", String(c)), ma.hidden = !c;
|
|
2842
3009
|
}
|
|
2843
|
-
let
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
3010
|
+
let pn = null, ve = !1;
|
|
3011
|
+
le.addEventListener("pointerdown", () => {
|
|
3012
|
+
ve = !1, pn = setTimeout(() => {
|
|
3013
|
+
ve = !0, De(!0);
|
|
2847
3014
|
}, 180);
|
|
2848
3015
|
});
|
|
2849
|
-
const
|
|
2850
|
-
clearTimeout(
|
|
3016
|
+
const un = () => {
|
|
3017
|
+
clearTimeout(pn), ve && (De(!1), ve = !1);
|
|
2851
3018
|
};
|
|
2852
|
-
|
|
2853
|
-
const
|
|
3019
|
+
le.addEventListener("pointerup", un), le.addEventListener("pointerleave", un);
|
|
3020
|
+
const Be = r(".setup-hint"), hn = `setup-hint-dismissed:${location.pathname}`, ba = {
|
|
2854
3021
|
"no-tokens": {
|
|
2855
3022
|
title: "The Panel tokens aren’t loaded",
|
|
2856
3023
|
text: "The knobs change the --twd-* variables, but this page doesn’t load them. Add the token stylesheet before your own CSS:",
|
|
2857
|
-
snippets:
|
|
2858
|
-
link: `${
|
|
3024
|
+
snippets: En,
|
|
3025
|
+
link: `${$n}/#install`
|
|
2859
3026
|
},
|
|
2860
3027
|
unused: {
|
|
2861
3028
|
title: "Nothing here uses the Panel tokens yet",
|
|
2862
3029
|
text: "The tokens are loaded, but no style on this page uses var(--twd-…), so the knobs won’t change anything. Ask your coding agent to convert the styles:",
|
|
2863
3030
|
copy: "Copy prompt for your agent",
|
|
2864
3031
|
copied: "Prompt copied",
|
|
2865
|
-
text2copy:
|
|
2866
|
-
link: `${
|
|
3032
|
+
text2copy: po,
|
|
3033
|
+
link: `${$n}/#tokens`
|
|
2867
3034
|
}
|
|
2868
3035
|
};
|
|
2869
|
-
let
|
|
2870
|
-
function
|
|
2871
|
-
const c =
|
|
2872
|
-
|
|
2873
|
-
<div class="setup-head">${
|
|
2874
|
-
<button class="icon-btn" data-action="setup-dismiss" title="Hide this message" aria-label="Hide this message">${
|
|
3036
|
+
let B = null;
|
|
3037
|
+
function Ue() {
|
|
3038
|
+
const c = lo();
|
|
3039
|
+
B = ne(hn) === c ? null : ba[c] || null, i.toggleAttribute("data-hint", !!B), Be.hidden = !B, B && (Be.innerHTML = `
|
|
3040
|
+
<div class="setup-head">${P.warn}<b>${F(B.title)}</b>
|
|
3041
|
+
<button class="icon-btn" data-action="setup-dismiss" title="Hide this message" aria-label="Hide this message">${P.close}</button>
|
|
2875
3042
|
</div>
|
|
2876
|
-
<p>${
|
|
2877
|
-
${(
|
|
3043
|
+
<p>${F(B.text)}</p>
|
|
3044
|
+
${(B.snippets || []).map((g, y) => `
|
|
2878
3045
|
<div class="setup-snippet">
|
|
2879
|
-
<span>${
|
|
2880
|
-
<div class="setup-code"><code>${
|
|
3046
|
+
<span>${F(g.label)}</span>
|
|
3047
|
+
<div class="setup-code"><code>${F(g.code)}</code><button class="text-btn" data-setup-snippet="${y}">Copy</button></div>
|
|
2881
3048
|
</div>`).join("")}
|
|
2882
3049
|
<div class="setup-actions">
|
|
2883
|
-
${
|
|
2884
|
-
<a href="${
|
|
2885
|
-
</div>`,
|
|
3050
|
+
${B.copy ? `<button class="apply-btn" data-action="setup-copy">${F(B.copy)}</button>` : ""}
|
|
3051
|
+
<a href="${B.link}" target="_blank" rel="noopener">Setup guide</a>
|
|
3052
|
+
</div>`, Be.dataset.status = c);
|
|
3053
|
+
}
|
|
3054
|
+
Ue(), document.readyState !== "complete" && window.addEventListener("load", Ue, { once: !0 }), setTimeout(Ue, 1500);
|
|
3055
|
+
const xa = { "--pv-primary": "--twd-primary", "--pv-on-primary": "--twd-on-primary", "--pv-radius-control": "--twd-radius-control", "--pv-radius-container": "--twd-radius-container" };
|
|
3056
|
+
function lt() {
|
|
3057
|
+
[[d.global, document.documentElement], [d.local, E && E.info.el]].forEach(([c, g]) => {
|
|
3058
|
+
if (!g) return;
|
|
3059
|
+
const y = getComputedStyle(g);
|
|
3060
|
+
Object.entries(xa).forEach(([C, k]) => {
|
|
3061
|
+
const T = y.getPropertyValue(k).trim();
|
|
3062
|
+
T ? c.style.setProperty(C, T) : c.style.removeProperty(C);
|
|
3063
|
+
});
|
|
3064
|
+
});
|
|
2886
3065
|
}
|
|
2887
|
-
|
|
2888
|
-
const
|
|
2889
|
-
function
|
|
3066
|
+
t.onChange(() => requestAnimationFrame(lt)), lt();
|
|
3067
|
+
const wa = r(".badge");
|
|
3068
|
+
function fn() {
|
|
2890
3069
|
r('[data-action="undo"]').disabled = !t.canUndo(), r('[data-action="redo"]').disabled = !t.canRedo();
|
|
2891
|
-
const c =
|
|
2892
|
-
|
|
2893
|
-
const g =
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
}), K
|
|
2897
|
-
}
|
|
2898
|
-
return t.onChange(
|
|
2899
|
-
if (c.target ===
|
|
3070
|
+
const c = Za(t.getState());
|
|
3071
|
+
wa.textContent = c, r('[data-action="changes"]').classList.toggle("has-changes", c > 0), r('[data-action="changes"]').setAttribute("aria-label", `Changes (${c})`);
|
|
3072
|
+
const g = S(ut());
|
|
3073
|
+
d.global.querySelectorAll(".page-warnings").forEach((y) => {
|
|
3074
|
+
y.innerHTML = g;
|
|
3075
|
+
}), K && rn();
|
|
3076
|
+
}
|
|
3077
|
+
return t.onChange(fn), fn(), o.addEventListener("click", (c) => {
|
|
3078
|
+
if (c.target === Ie) return it();
|
|
2900
3079
|
const g = c.target.closest("button");
|
|
2901
3080
|
if (g) {
|
|
2902
|
-
if (g !==
|
|
2903
|
-
return
|
|
3081
|
+
if (g !== sn && !we.contains(g) && ze(!1), g === i)
|
|
3082
|
+
return qt.consumeClick() ? void 0 : c.detail === 2 ? (je(), qt.reset(), V("Panel moved back to the corner")) : je();
|
|
2904
3083
|
if (g.dataset.select) {
|
|
2905
|
-
const
|
|
2906
|
-
return
|
|
3084
|
+
const y = document.querySelector(g.dataset.select);
|
|
3085
|
+
return y ? Jt(y) : V("That element isn’t on this page");
|
|
2907
3086
|
}
|
|
2908
|
-
if (g.dataset.setupSnippet) return
|
|
3087
|
+
if (g.dataset.setupSnippet) return xe(En[g.dataset.setupSnippet].code, "Copied");
|
|
2909
3088
|
if (g.dataset.tab)
|
|
2910
|
-
return
|
|
2911
|
-
if (g.dataset.remove) return
|
|
3089
|
+
return Ae(Ut()), at && be(!1), w(g.dataset.tab);
|
|
3090
|
+
if (g.dataset.remove) return ha(g.dataset.remove, g.dataset.removeScope);
|
|
2912
3091
|
if (g.dataset.applyMode)
|
|
2913
|
-
return
|
|
3092
|
+
return ce("apply-mode", g.dataset.applyMode), ze(!1), V(g.dataset.applyMode === "copy" ? "Copy prompt copies straight away" : "Copy prompt shows a preview first");
|
|
2914
3093
|
switch (g.dataset.action) {
|
|
2915
3094
|
case "close":
|
|
2916
|
-
return
|
|
3095
|
+
return ot();
|
|
2917
3096
|
case "minimise":
|
|
2918
|
-
return
|
|
3097
|
+
return at ? be(!1) : (pa(), (o.querySelector('.tab[aria-pressed="true"]') || x[0]).focus());
|
|
2919
3098
|
case "reset":
|
|
2920
|
-
return t.resetState(),
|
|
3099
|
+
return t.resetState(), V("Reset to defaults · ⌘Z to undo");
|
|
2921
3100
|
case "copy":
|
|
2922
|
-
return
|
|
3101
|
+
return xe(location.href, "Link copied");
|
|
3102
|
+
// Pressed again (picking, or an element selected): out of selection mode.
|
|
2923
3103
|
case "pick":
|
|
2924
|
-
return
|
|
3104
|
+
return ie || E ? (Re(), an()) : Zt();
|
|
2925
3105
|
case "compare":
|
|
2926
|
-
|
|
3106
|
+
ve || De(le.getAttribute("aria-pressed") !== "true");
|
|
2927
3107
|
return;
|
|
2928
3108
|
case "undo":
|
|
2929
|
-
return
|
|
3109
|
+
return tn();
|
|
2930
3110
|
case "redo":
|
|
2931
|
-
return
|
|
3111
|
+
return nn();
|
|
2932
3112
|
case "back":
|
|
2933
|
-
return
|
|
3113
|
+
return an();
|
|
2934
3114
|
case "changes":
|
|
2935
|
-
return
|
|
3115
|
+
return Z(!K);
|
|
3116
|
+
case "changes-close":
|
|
3117
|
+
return Z(!1);
|
|
2936
3118
|
case "apply":
|
|
2937
|
-
return
|
|
3119
|
+
return ga();
|
|
2938
3120
|
case "apply-menu":
|
|
2939
|
-
return
|
|
3121
|
+
return ze(we.hidden);
|
|
2940
3122
|
case "dialog-close":
|
|
2941
|
-
return
|
|
3123
|
+
return it();
|
|
2942
3124
|
case "dialog-copy":
|
|
2943
|
-
return
|
|
3125
|
+
return xe(cn.value, "Prompt copied · paste it into your agent");
|
|
2944
3126
|
case "setup-copy":
|
|
2945
|
-
return
|
|
3127
|
+
return B && xe(B.text2copy, B.copied);
|
|
2946
3128
|
case "setup-dismiss":
|
|
2947
|
-
return
|
|
3129
|
+
return ce(hn, Be.dataset.status), Ue();
|
|
2948
3130
|
}
|
|
2949
3131
|
}
|
|
2950
3132
|
}), o.addEventListener("keydown", (c) => {
|
|
2951
3133
|
if (c.key === "Escape") {
|
|
2952
|
-
if (!
|
|
2953
|
-
if (!
|
|
3134
|
+
if (!Ie.hidden) return it();
|
|
3135
|
+
if (!we.hidden) return ze(!1);
|
|
2954
3136
|
}
|
|
2955
|
-
!(c.metaKey || c.ctrlKey) || c.key.toLowerCase() !== "z" || c.target.matches?.('input[type="text"], textarea') || (c.preventDefault(), c.shiftKey ?
|
|
3137
|
+
!(c.metaKey || c.ctrlKey) || c.key.toLowerCase() !== "z" || c.target.matches?.('input[type="text"], textarea') || (c.preventDefault(), c.shiftKey ? nn() : tn());
|
|
2956
3138
|
}), document.addEventListener("keydown", (c) => {
|
|
2957
|
-
c.altKey && (c.code === "KeyT" && (c.preventDefault(),
|
|
2958
|
-
}),
|
|
3139
|
+
c.altKey && (c.code === "KeyT" && (c.preventDefault(), je()), c.code === "KeyC" && (c.preventDefault(), De(le.getAttribute("aria-pressed") !== "true")));
|
|
3140
|
+
}), ne("panel") === "open" ? J({ animate: !1 }) : ne("panel") === "tabs" && (J({ animate: !1 }), be(!0)), { open: J, close: ot, toggle: je, toast: V, pick: Zt, prompt: dn };
|
|
2959
3141
|
}
|
|
2960
|
-
const
|
|
2961
|
-
function
|
|
3142
|
+
const zn = [Bo, Je, ia, Ft, yo, Nt, la, vo];
|
|
3143
|
+
function Yo() {
|
|
2962
3144
|
if (getComputedStyle(document.documentElement).getPropertyValue("--twd-radius-control").trim() || document.getElementById("twiddle-tokens")) return;
|
|
2963
3145
|
const t = document.createElement("style");
|
|
2964
|
-
t.id = "twiddle-tokens", t.textContent =
|
|
3146
|
+
t.id = "twiddle-tokens", t.textContent = qn, document.head.prepend(t);
|
|
2965
3147
|
}
|
|
2966
3148
|
if (!window.Twiddle) {
|
|
2967
|
-
|
|
3149
|
+
Yo(), zn.forEach((n) => ja(n.tokens)), Vn(At);
|
|
2968
3150
|
let e;
|
|
2969
3151
|
const t = () => {
|
|
2970
|
-
|
|
3152
|
+
At(Mt()), e = Go({ sections: zn, store: Aa, view: { suspend: za, resume: Ia } });
|
|
2971
3153
|
};
|
|
2972
3154
|
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", t) : t(), window.Twiddle = {
|
|
2973
3155
|
open: () => e && e.open(),
|
|
2974
3156
|
close: () => e && e.close(),
|
|
2975
3157
|
toggle: () => e && e.toggle(),
|
|
2976
|
-
reset: () =>
|
|
2977
|
-
undo: () =>
|
|
2978
|
-
redo: () =>
|
|
3158
|
+
reset: () => Dn(),
|
|
3159
|
+
undo: () => Bn(),
|
|
3160
|
+
redo: () => Un(),
|
|
2979
3161
|
pick: () => e && e.pick(),
|
|
2980
3162
|
prompt: () => e ? e.prompt() : "",
|
|
2981
|
-
getState:
|
|
3163
|
+
getState: Mt
|
|
2982
3164
|
};
|
|
2983
3165
|
}
|
|
2984
|
-
const
|
|
3166
|
+
const Xo = window.Twiddle;
|
|
2985
3167
|
export {
|
|
2986
|
-
|
|
3168
|
+
Xo as Twiddle
|
|
2987
3169
|
};
|