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