@takazudo/zdtp 0.4.5 → 0.4.7

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.
@@ -1,4 +1,4 @@
1
- import { C as s, c as i, i as l, a as r, l as t, s as o, v as d } from "../load-routing-LJ72261l.js";
1
+ import { C as s, c as i, i as l, a as r, l as t, s as o, v as d } from "../load-routing-D4H2VOl5.js";
2
2
  export {
3
3
  s as CSS_VAR_NAME_RE,
4
4
  i as createApplyHandler,
@@ -0,0 +1,25 @@
1
+ import type { TabConfig } from '../tokens/tier-model';
2
+ interface NotesTabProps {
3
+ tab: TabConfig;
4
+ }
5
+ /**
6
+ * Notes tab — host-configurable "token notes" landing view (#515).
7
+ *
8
+ * Renders `tab.notesExtras.title` as a section heading
9
+ * (`div[role="heading"][aria-level=3]`, NEVER an hN tag — see
10
+ * packages/zdtp/CLAUDE.md "Panel DOM hygiene") and `tab.notesExtras.html` as
11
+ * sanitized HTML via `dangerouslySetInnerHTML` — the package's first and only
12
+ * use of it. `sanitizeHtml` (utils/sanitize-html.ts) is the security
13
+ * boundary, an allowlist parser, not this component and not the CSS below.
14
+ *
15
+ * The rendered `.tokenpanel-notes-body` subtree is HOST-AUTHORED PROSE
16
+ * CONTENT, not panel chrome: the DOM-hygiene ban on h1-h6/p/ul/a/table/etc.
17
+ * applies to the panel's OWN JSX, and does not apply inside sanitized note
18
+ * content — that's exactly the markup a "notes" feature needs to render.
19
+ *
20
+ * `assertValidPanelConfig` requires `notesExtras` (non-empty title + html)
21
+ * on every tab with `id: 'notes'`, so this component can assume it's
22
+ * present without a runtime fallback UI.
23
+ */
24
+ export default function NotesTab({ tab }: NotesTabProps): import("preact").JSX.Element | null;
25
+ export {};
package/dist/testing.js CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as r, c as i, h as _, i as n, j as g, b as R, a as p } from "./panel-config-CipeKMTz.js";
2
- import { a as y, b as O, l as P } from "./tweak-state-xt-tSQ_t.js";
1
+ import { _ as r, c as _, h as i, j as n, k as g, b as R, a as p } from "./panel-config-Bw7D-25C.js";
2
+ import { a as y, b as O, l as P } from "./tweak-state-CD1y56zR.js";
3
3
  import { F as K, G as E, a as S, S as u } from "./manifest-DCReQE0k.js";
4
4
  async function s(a, e) {
5
5
  await a.fill(e), await a.dispatchEvent("input");
@@ -10,12 +10,12 @@ export {
10
10
  S as GROUP_TITLES,
11
11
  u as SIZE_GROUP_ORDER,
12
12
  r as __resetPanelConfigForTests,
13
- i as configurePanel,
13
+ _ as configurePanel,
14
14
  y as getStorageKeyV1,
15
15
  O as getStorageKeyV2,
16
16
  P as loadPersistedState,
17
17
  s as setPanelInputValue,
18
- _ as storageKey_open,
18
+ i as storageKey_open,
19
19
  n as storageKey_position,
20
20
  g as storageKey_stateV1,
21
21
  R as storageKey_stateV2,
@@ -60,6 +60,14 @@ export interface TokenDef {
60
60
  step: number;
61
61
  /** Unit suffix (e.g. `rem`, `px`). Read-only rows may use an empty string. */
62
62
  unit: string;
63
+ /**
64
+ * Optional list of unit suffixes this row may cycle through by clicking
65
+ * the unit label (#519). 2+ entries opts in; omitted/single-entry keeps
66
+ * today's static, non-interactive unit suffix. Mirrors `TierValueKind`'s
67
+ * `length.units` — the default-slider mapping in a host's
68
+ * `toTierItem()`-style helper should pass it straight through.
69
+ */
70
+ units?: readonly string[];
63
71
  /** Read-only tokens are displayed but not editable (e.g. `clamp()` expressions). */
64
72
  readonly?: true;
65
73
  /** Which control renders this token. Defaults to `"slider"` when absent. */
@@ -4,6 +4,16 @@ export type TierValueKind = {
4
4
  kind: 'length';
5
5
  step: number;
6
6
  unit: string;
7
+ /**
8
+ * Optional list of unit suffixes this row may cycle through by
9
+ * clicking the unit label (#519). 2+ entries opts the row into an
10
+ * interactive, click/Enter/Space-cycling unit suffix (wrapping through
11
+ * the list); omitted or a single entry keeps today's static,
12
+ * non-interactive unit span, pixel-identical. This kind is reused for
13
+ * ms durations and unitless values too — only declare `units` when
14
+ * cycling between those suffixes is valid CSS for the token.
15
+ */
16
+ units?: readonly string[];
7
17
  } | {
8
18
  kind: 'number';
9
19
  step: number;
@@ -145,9 +155,23 @@ export interface ColorClusterExtras {
145
155
  */
146
156
  semanticDefaults?: Record<string, SemanticValue>;
147
157
  }
158
+ /**
159
+ * Host-configurable "token notes" content — ONLY valid on the reserved
160
+ * `id: 'notes'` pseudo-tab (#515). That tab renders `title` as a section
161
+ * heading and `html` (sanitized by `utils/sanitize-html.ts`) as its body,
162
+ * instead of a tier-driven token editor. `assertValidPanelConfig` enforces
163
+ * the full contract: required + non-empty on `id: 'notes'`, forbidden on
164
+ * every other tab id, and `id: 'notes'` tabs must ship `tiers: []` and no
165
+ * `colorExtras`.
166
+ */
167
+ export interface NotesExtras {
168
+ title: string;
169
+ html: string;
170
+ }
148
171
  export interface TabConfig {
149
172
  id: string;
150
173
  label: string;
151
174
  tiers: readonly TierConfig[];
152
175
  colorExtras?: ColorClusterExtras;
176
+ notesExtras?: NotesExtras;
153
177
  }
@@ -1,4 +1,4 @@
1
- import { E as gt, g, e as mt, d as yt, b as bt, h as xt, F as U, t as Z, p as Ne, q as Oe, G as Mt, i as kt, H as vt, r as B, j as St, I as Ie, J as wt, m as $t } from "./panel-config-CipeKMTz.js";
1
+ import { r as gt, g, e as mt, d as yt, b as bt, h as xt, m as U, n as Z, o as Ne, p as Oe, q as Mt, j as kt, t as vt, u as B, k as St, v as Te, w as wt, x as $t } from "./panel-config-Bw7D-25C.js";
2
2
  const Re = (e, t) => {
3
3
  if (typeof e == "number") {
4
4
  if (t === 3)
@@ -183,13 +183,13 @@ const Re = (e, t) => {
183
183
  whitesmoke: 16119285,
184
184
  yellow: 16776960,
185
185
  yellowgreen: 10145074
186
- }, Ot = (e) => Re(Nt[e.toLowerCase()], 6), It = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i, Tt = (e) => {
186
+ }, Ot = (e) => Re(Nt[e.toLowerCase()], 6), Tt = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i, Ct = (e) => {
187
187
  let t;
188
- return (t = e.match(It)) ? Re(parseInt(t[1], 16), t[1].length) : void 0;
189
- }, w = "([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)", H = `${w}%`, be = `(?:${w}%|${w})`, Ct = `(?:${w}(deg|grad|rad|turn)|${w})`, T = "\\s*,\\s*", At = new RegExp(
190
- `^rgba?\\(\\s*${w}${T}${w}${T}${w}\\s*(?:,\\s*${be}\\s*)?\\)$`
188
+ return (t = e.match(Tt)) ? Re(parseInt(t[1], 16), t[1].length) : void 0;
189
+ }, w = "([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)", H = `${w}%`, be = `(?:${w}%|${w})`, It = `(?:${w}(deg|grad|rad|turn)|${w})`, C = "\\s*,\\s*", At = new RegExp(
190
+ `^rgba?\\(\\s*${w}${C}${w}${C}${w}\\s*(?:,\\s*${be}\\s*)?\\)$`
191
191
  ), Pt = new RegExp(
192
- `^rgba?\\(\\s*${H}${T}${H}${T}${H}\\s*(?:,\\s*${be}\\s*)?\\)$`
192
+ `^rgba?\\(\\s*${H}${C}${H}${C}${H}\\s*(?:,\\s*${be}\\s*)?\\)$`
193
193
  ), _t = (e) => {
194
194
  let t = { mode: "rgb" }, n;
195
195
  if (n = e.match(At))
@@ -279,7 +279,7 @@ const Rt = {
279
279
  function P(e) {
280
280
  let t = "";
281
281
  if ((e[c] === "-" || e[c] === "+") && (t += e[c++]), t += F(e), e[c] === "." && /\d/.test(e[c + 1]) && (t += e[c++] + F(e)), (e[c] === "e" || e[c] === "E") && ((e[c + 1] === "-" || e[c + 1] === "+") && /\d/.test(e[c + 2]) ? t += e[c++] + e[c++] + F(e) : /\d/.test(e[c + 1]) && (t += e[c++] + F(e))), le(e)) {
282
- let n = J(e);
282
+ let n = W(e);
283
283
  return n === "deg" || n === "rad" || n === "turn" || n === "grad" ? { type: l.Hue, value: t * Rt[n] } : void 0;
284
284
  }
285
285
  return e[c] === "%" ? (c++, { type: l.Percentage, value: +t }) : { type: l.Number, value: +t };
@@ -290,14 +290,14 @@ function F(e) {
290
290
  t += e[c++];
291
291
  return t;
292
292
  }
293
- function J(e) {
293
+ function W(e) {
294
294
  let t = "";
295
295
  for (; c < e.length && Et.test(e[c]); )
296
296
  t += e[c++];
297
297
  return t;
298
298
  }
299
299
  function jt(e) {
300
- let t = J(e);
300
+ let t = W(e);
301
301
  return e[c] === "(" ? (c++, { type: l.Function, value: t }) : t === "none" ? { type: l.None, value: void 0 } : { type: l.Ident, value: t };
302
302
  }
303
303
  function zt(e = "") {
@@ -329,7 +329,7 @@ function zt(e = "") {
329
329
  continue;
330
330
  }
331
331
  if (le(t)) {
332
- n.push({ type: l.Ident, value: J(t) });
332
+ n.push({ type: l.Ident, value: W(t) });
333
333
  continue;
334
334
  }
335
335
  return;
@@ -350,7 +350,7 @@ function zt(e = "") {
350
350
  n.push({ type: l.Alpha, value: o });
351
351
  continue;
352
352
  }
353
- if (le(t) && J(t) === "none") {
353
+ if (le(t) && W(t) === "none") {
354
354
  n.push({
355
355
  type: l.Alpha,
356
356
  value: { type: l.None, value: void 0 }
@@ -459,12 +459,12 @@ const Kt = (e) => e === "transparent" ? { mode: "rgb", r: 0, g: 0, b: 0, alpha:
459
459
  }, y = qt(Bt), Me = (e) => {
460
460
  let t = !1, n = e.map((r) => r !== void 0 ? (t = !0, r) : 1);
461
461
  return t ? n : e;
462
- }, Jt = {
462
+ }, Wt = {
463
463
  mode: "rgb",
464
464
  channels: ["r", "g", "b", "alpha"],
465
465
  parse: [
466
466
  Dt,
467
- Tt,
467
+ Ct,
468
468
  _t,
469
469
  Ot,
470
470
  Kt,
@@ -502,12 +502,12 @@ const Kt = (e) => e === "transparent" ? { mode: "rgb", r: 0, g: 0, b: 0, alpha:
502
502
  b: re(n)
503
503
  };
504
504
  return r !== void 0 && (i.alpha = r), i;
505
- }, $ = (e) => (e = e % 360) < 0 ? e + 360 : e, Wt = (e, t) => e.map((n, r, o) => {
505
+ }, $ = (e) => (e = e % 360) < 0 ? e + 360 : e, Jt = (e, t) => e.map((n, r, o) => {
506
506
  if (n === void 0)
507
507
  return n;
508
508
  let i = $(n);
509
509
  return r === 0 || e[r - 1] === void 0 ? i : t(i - $(o[r - 1]));
510
- }).reduce((n, r) => !n.length || r === void 0 || n[n.length - 1] === void 0 ? (n.push(r), n) : (n.push(r + n[n.length - 1]), n), []), Be = (e) => Wt(e, (t) => Math.abs(t) <= 180 ? t : t - 360 * Math.sign(t)), Xt = (e, t) => {
510
+ }).reduce((n, r) => !n.length || r === void 0 || n[n.length - 1] === void 0 ? (n.push(r), n) : (n.push(r + n[n.length - 1]), n), []), Be = (e) => Jt(e, (t) => Math.abs(t) <= 180 ? t : t - 360 * Math.sign(t)), Xt = (e, t) => {
511
511
  if (e.h === void 0 || t.h === void 0 || !e.s || !t.s)
512
512
  return 0;
513
513
  let n = $(e.h), r = $(t.h), o = Math.sin((r - n + 360) / 2 * Math.PI / 180);
@@ -529,7 +529,7 @@ const Kt = (e) => e === "transparent" ? { mode: "rgb", r: 0, g: 0, b: 0, alpha:
529
529
  { sin: 0, cos: 0 }
530
530
  ), n = Math.atan2(t.sin, t.cos) * 180 / Math.PI;
531
531
  return n < 0 ? 360 + n : n;
532
- }, W = ({ l: e, a: t, b: n, alpha: r }, o = "lch") => {
532
+ }, J = ({ l: e, a: t, b: n, alpha: r }, o = "lch") => {
533
533
  t === void 0 && (t = 0), n === void 0 && (n = 0);
534
534
  let i = Math.sqrt(t * t + n * n), a = { mode: o, l: e, c: i };
535
535
  return i && (a.h = $(Math.atan2(n, t) * 180 / Math.PI)), r !== void 0 && (a.alpha = r), a;
@@ -542,7 +542,7 @@ const Kt = (e) => e === "transparent" ? { mode: "rgb", r: 0, g: 0, b: 0, alpha:
542
542
  b: t ? t * Math.sin(n / 180 * Math.PI) : 0
543
543
  };
544
544
  return r !== void 0 && (i.alpha = r), i;
545
- }, I = {
545
+ }, T = {
546
546
  X: 0.3457 / 0.3585,
547
547
  Y: 1,
548
548
  Z: (1 - 0.3457 - 0.3585) / 0.3585
@@ -595,7 +595,7 @@ const Qt = (e, t) => {
595
595
  return e * 360;
596
596
  }
597
597
  }, en = new RegExp(
598
- `^hsla?\\(\\s*${Ct}${T}${H}${T}${H}\\s*(?:,\\s*${be}\\s*)?\\)$`
598
+ `^hsla?\\(\\s*${It}${C}${H}${C}${H}\\s*(?:,\\s*${be}\\s*)?\\)$`
599
599
  ), tn = (e) => {
600
600
  let t = e.match(en);
601
601
  if (!t) return;
@@ -656,15 +656,15 @@ const rn = {
656
656
  average: {
657
657
  h: Ge
658
658
  }
659
- }, qe = Math.pow(29, 3) / Math.pow(3, 3), Je = Math.pow(6, 3) / Math.pow(29, 3);
660
- let oe = (e) => Math.pow(e, 3) > Je ? Math.pow(e, 3) : (116 * e - 16) / qe;
659
+ }, qe = Math.pow(29, 3) / Math.pow(3, 3), We = Math.pow(6, 3) / Math.pow(29, 3);
660
+ let oe = (e) => Math.pow(e, 3) > We ? Math.pow(e, 3) : (116 * e - 16) / qe;
661
661
  const on = ({ l: e, a: t, b: n, alpha: r }) => {
662
662
  e === void 0 && (e = 0), t === void 0 && (t = 0), n === void 0 && (n = 0);
663
663
  let o = (e + 16) / 116, i = t / 500 + o, a = o - n / 200, s = {
664
664
  mode: "xyz50",
665
- x: oe(i) * I.X,
666
- y: oe(o) * I.Y,
667
- z: oe(a) * I.Z
665
+ x: oe(i) * T.X,
666
+ y: oe(o) * T.Y,
667
+ z: oe(a) * T.Z
668
668
  };
669
669
  return r !== void 0 && (s.alpha = r), s;
670
670
  }, an = ({ x: e, y: t, z: n, alpha: r }) => {
@@ -683,9 +683,9 @@ const on = ({ l: e, a: t, b: n, alpha: r }) => {
683
683
  z: 0.013923904500943465 * t + 0.09708128566574634 * n + 0.7140993584005155 * r
684
684
  };
685
685
  return o !== void 0 && (i.alpha = o), i;
686
- }, ie = (e) => e > Je ? Math.cbrt(e) : (qe * e + 16) / 116, ln = ({ x: e, y: t, z: n, alpha: r }) => {
686
+ }, ie = (e) => e > We ? Math.cbrt(e) : (qe * e + 16) / 116, ln = ({ x: e, y: t, z: n, alpha: r }) => {
687
687
  e === void 0 && (e = 0), t === void 0 && (t = 0), n === void 0 && (n = 0);
688
- let o = ie(e / I.X), i = ie(t / I.Y), a = ie(n / I.Z), s = {
688
+ let o = ie(e / T.X), i = ie(t / T.Y), a = ie(n / T.Z), s = {
689
689
  mode: "lab",
690
690
  l: 116 * i - 16,
691
691
  a: 500 * (o - i),
@@ -728,8 +728,8 @@ const dn = {
728
728
  rgb: (e) => sn(X(e))
729
729
  },
730
730
  fromMode: {
731
- rgb: (e) => W(un(e)),
732
- lab: W
731
+ rgb: (e) => J(un(e)),
732
+ lab: J
733
733
  },
734
734
  channels: ["l", "c", "h", "alpha"],
735
735
  ranges: {
@@ -815,8 +815,8 @@ const bn = {
815
815
  rgb: (e) => mn(X(e, "oklab"))
816
816
  },
817
817
  fromMode: {
818
- rgb: (e) => W(pn(e), "oklch"),
819
- oklab: (e) => W(e, "oklch")
818
+ rgb: (e) => J(pn(e), "oklch"),
819
+ oklab: (e) => J(e, "oklch")
820
820
  },
821
821
  parse: [yn],
822
822
  serialize: (e) => `oklch(${e.l !== void 0 ? e.l : "none"} ${e.c !== void 0 ? e.c : "none"} ${e.h !== void 0 ? e.h : "none"}${e.alpha < 1 ? ` / ${e.alpha}` : ""})`,
@@ -825,7 +825,7 @@ const bn = {
825
825
  c: [0, 0.4],
826
826
  h: [0, 360]
827
827
  }
828
- }, xn = (e) => Math.max(0, Math.min(1, e || 0)), D = (e) => Math.round(xn(e) * 255), We = x("rgb"), Xe = (e) => {
828
+ }, xn = (e) => Math.max(0, Math.min(1, e || 0)), D = (e) => Math.round(xn(e) * 255), Je = x("rgb"), Xe = (e) => {
829
829
  if (e === void 0)
830
830
  return;
831
831
  let t = D(e.r), n = D(e.g), r = D(e.b);
@@ -835,7 +835,7 @@ const bn = {
835
835
  return;
836
836
  let t = D(e.alpha !== void 0 ? e.alpha : 1);
837
837
  return Xe(e) + (256 | t).toString(16).slice(1);
838
- }, kn = (e) => Xe(We(e)), vn = (e) => Mn(We(e)), Ye = x("rgb"), Ue = (e) => {
838
+ }, kn = (e) => Xe(Je(e)), vn = (e) => Mn(Je(e)), Ye = x("rgb"), Ue = (e) => {
839
839
  const t = {
840
840
  mode: e.mode,
841
841
  r: Math.max(0, Math.min(e.r !== void 0 ? e.r : 0, 1)),
@@ -887,9 +887,9 @@ function Nn(e, t = "lch", n = "rgb") {
887
887
  );
888
888
  }
889
889
  xe(bn);
890
- xe(Jt);
890
+ xe(Wt);
891
891
  xe(rn);
892
- const sr = 0.37, Qe = x("oklch"), et = x("rgb"), On = x("hsl"), In = ke("rgb");
892
+ const sr = 0.37, Qe = x("oklch"), et = x("rgb"), On = x("hsl"), Tn = ke("rgb");
893
893
  function ee(e) {
894
894
  return {
895
895
  mode: "oklch",
@@ -899,7 +899,7 @@ function ee(e) {
899
899
  alpha: e.a / 100
900
900
  };
901
901
  }
902
- function Tn(e) {
902
+ function Cn(e) {
903
903
  return {
904
904
  l: (e.l ?? 0) * 100,
905
905
  c: e.c ?? 0,
@@ -953,7 +953,7 @@ function dr(e) {
953
953
  l: e.l / 100,
954
954
  alpha: e.a / 100
955
955
  }, n = Qe(t);
956
- return n ? Tn(n) : { l: 0, c: 0, h: 0, a: e.a };
956
+ return n ? Cn(n) : { l: 0, c: 0, h: 0, a: e.a };
957
957
  }
958
958
  function hr(e) {
959
959
  const t = ee(e), n = Nn(t, "oklch", "rgb");
@@ -966,9 +966,9 @@ function hr(e) {
966
966
  }
967
967
  function pr(e) {
968
968
  const t = et(ee(e));
969
- return t ? In(t) : !1;
969
+ return t ? Tn(t) : !1;
970
970
  }
971
- function Cn(e, t) {
971
+ function In(e, t) {
972
972
  const n = parseFloat(e);
973
973
  let r;
974
974
  switch (t.toLowerCase()) {
@@ -1011,19 +1011,19 @@ function ue(e) {
1011
1011
  const m = k.match(/^([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)(deg|rad|grad|turn)?$/i);
1012
1012
  if (!m) return null;
1013
1013
  const A = m[1], j = m[2] ?? "deg";
1014
- v = Cn(A, j);
1014
+ v = In(A, j);
1015
1015
  }
1016
- let C = 100;
1016
+ let I = 100;
1017
1017
  if (a !== null) {
1018
1018
  const m = f(a.trim());
1019
1019
  if (!m) return null;
1020
- C = m.isPercent ? m.value : m.value * 100;
1020
+ I = m.isPercent ? m.value : m.value * 100;
1021
1021
  }
1022
1022
  return {
1023
1023
  l: d,
1024
1024
  c: p,
1025
1025
  h: v,
1026
- a: C
1026
+ a: I
1027
1027
  };
1028
1028
  }
1029
1029
  function tt(e = g()) {
@@ -1074,7 +1074,7 @@ function yr(e, t) {
1074
1074
  } catch {
1075
1075
  }
1076
1076
  }
1077
- const ae = 60, Te = 320, Ce = 240, Ae = 1600, Pe = 1200, _n = { width: 1024 * 0.8, height: 768 * 0.8 };
1077
+ const ae = 60, Ce = 320, Ie = 240, Ae = 1600, Pe = 1200, _n = { width: 1024 * 0.8, height: 768 * 0.8 };
1078
1078
  function Hn() {
1079
1079
  return typeof window > "u" ? _n : st(
1080
1080
  Math.min(1200, 0.8 * window.innerWidth),
@@ -1083,10 +1083,10 @@ function Hn() {
1083
1083
  }
1084
1084
  const _e = 32;
1085
1085
  function st(e, t) {
1086
- const n = typeof window < "u" ? window.innerWidth : Ae, r = typeof window < "u" ? window.innerHeight : Pe, o = Math.max(Te, Math.min(Ae, n - _e)), i = Math.max(Ce, Math.min(Pe, r - _e));
1086
+ const n = typeof window < "u" ? window.innerWidth : Ae, r = typeof window < "u" ? window.innerHeight : Pe, o = Math.max(Ce, Math.min(Ae, n - _e)), i = Math.max(Ie, Math.min(Pe, r - _e));
1087
1087
  return {
1088
- width: Math.max(Te, Math.min(e, o)),
1089
- height: Math.max(Ce, Math.min(t, i))
1088
+ width: Math.max(Ce, Math.min(e, o)),
1089
+ height: Math.max(Ie, Math.min(t, i))
1090
1090
  };
1091
1091
  }
1092
1092
  function br(e) {
@@ -1337,7 +1337,7 @@ function qn(e, t = b()) {
1337
1337
  shikiTheme: String(e.shikiTheme ?? t.defaultShikiTheme)
1338
1338
  };
1339
1339
  }
1340
- function Jn(e, t, n, r) {
1340
+ function Wn(e, t, n, r) {
1341
1341
  const o = t.length;
1342
1342
  return e === "bg" ? t[V(n, o)] ?? "#000000" : e === "fg" ? t[V(r, o)] ?? "#ffffff" : t[V(e, o)] ?? "#000000";
1343
1343
  }
@@ -1345,7 +1345,7 @@ function V(e, t) {
1345
1345
  return e >= 0 && e < t ? e : 0;
1346
1346
  }
1347
1347
  function pe(e, t, n, r, o, i, a) {
1348
- if (En(e)) return Jn(e, t, n, r);
1348
+ if (En(e)) return Wn(e, t, n, r);
1349
1349
  if (ce(e) && de(e))
1350
1350
  return a ? e.literal[a] : `light-dark(${e.literal.light}, ${e.literal.dark})`;
1351
1351
  if (ce(e) && !de(e)) return e.literal;
@@ -1357,7 +1357,7 @@ function pe(e, t, n, r, o, i, a) {
1357
1357
  }
1358
1358
  return null;
1359
1359
  }
1360
- function Wn(e) {
1360
+ function Jn(e) {
1361
1361
  const t = e.panelSettings.colorMode;
1362
1362
  return t ? t.defaultMode : "light";
1363
1363
  }
@@ -1372,7 +1372,7 @@ function Or(e, t, n = b(), r, o) {
1372
1372
  t.foreground,
1373
1373
  r,
1374
1374
  o,
1375
- Wn(n)
1375
+ Jn(n)
1376
1376
  ) ?? "#000000";
1377
1377
  }
1378
1378
  const N = "color-scheme", ge = "light dark";
@@ -1405,7 +1405,7 @@ function Ve(e, t = b(), n, r, o, i) {
1405
1405
  );
1406
1406
  h !== null && s.push([u, h]);
1407
1407
  }
1408
- Y(e, t) && (s.push([N, ge]), i && Ie(i));
1408
+ Y(e, t) && (s.push([N, ge]), i && Te(i));
1409
1409
  try {
1410
1410
  n.apply(s);
1411
1411
  } catch (f) {
@@ -1431,7 +1431,7 @@ function Ve(e, t = b(), n, r, o, i) {
1431
1431
  );
1432
1432
  d !== null && _(f, d);
1433
1433
  }
1434
- Y(e, t) && (_(N, ge), i && Ie(i));
1434
+ Y(e, t) && (_(N, ge), i && Te(i));
1435
1435
  }
1436
1436
  function Xn(e, t, n) {
1437
1437
  const r = n ?? g(), o = r.applySink, i = r.tabs.find((u) => u.id === "color"), a = b(r);
@@ -1460,7 +1460,7 @@ function Xn(e, t, n) {
1460
1460
  } else
1461
1461
  ct(r.storagePrefix);
1462
1462
  }
1463
- function Ir(e, t) {
1463
+ function Tr(e, t) {
1464
1464
  const n = t ?? g();
1465
1465
  Xn(e.color, e.secondary, n), Yn(e, n);
1466
1466
  }
@@ -1580,7 +1580,7 @@ function Qn(e, t, n) {
1580
1580
  }
1581
1581
  e.length > 0 && n && ct(n);
1582
1582
  }
1583
- function Tr(e, t) {
1583
+ function Cr(e, t) {
1584
1584
  const n = t ?? g(), r = n.applySink, o = lt(n);
1585
1585
  if (r) {
1586
1586
  const a = [];
@@ -1669,7 +1669,7 @@ function me(e, t, n) {
1669
1669
  shikiTheme: typeof e.shikiTheme == "string" && e.shikiTheme.length > 0 ? e.shikiTheme : t.shikiTheme
1670
1670
  };
1671
1671
  }
1672
- const Cr = {
1672
+ const Ir = {
1673
1673
  "text-caption": "text-xs",
1674
1674
  "text-small": "text-sm",
1675
1675
  "text-body": "text-base",
@@ -1744,7 +1744,7 @@ function or(e = localStorage, t, n = b(), r = g()) {
1744
1744
  if (d && typeof d == "object") {
1745
1745
  const h = d, p = ye(h, n, t, r);
1746
1746
  if (p !== null) {
1747
- const k = h.typography !== void 0 ? h.typography : h.font, v = JSON.stringify(k ?? {}) !== JSON.stringify(p.typography), C = h.color, m = Array.isArray(C?.palette) && C.palette.length !== n.paletteSize;
1747
+ const k = h.typography !== void 0 ? h.typography : h.font, v = JSON.stringify(k ?? {}) !== JSON.stringify(p.typography), I = h.color, m = Array.isArray(I?.palette) && I.palette.length !== n.paletteSize;
1748
1748
  if (v || h.typography === void 0 || m)
1749
1749
  try {
1750
1750
  e.setItem(a, JSON.stringify(p));
@@ -1915,7 +1915,7 @@ export {
1915
1915
  Yn as I,
1916
1916
  Zn as J,
1917
1917
  Hr as K,
1918
- Tr as L,
1918
+ Cr as L,
1919
1919
  sr as M,
1920
1920
  $r as N,
1921
1921
  Mr as O,
@@ -1928,7 +1928,7 @@ export {
1928
1928
  R as V,
1929
1929
  rt as W,
1930
1930
  Or as X,
1931
- Cr as Z,
1931
+ Ir as Z,
1932
1932
  tt as a,
1933
1933
  nt as b,
1934
1934
  b as c,
@@ -1950,9 +1950,9 @@ export {
1950
1950
  V as s,
1951
1951
  ue as t,
1952
1952
  Nr as u,
1953
- Wn as v,
1953
+ Jn as v,
1954
1954
  wr as w,
1955
- Ir as x,
1955
+ Tr as x,
1956
1956
  _r as y,
1957
1957
  Hn as z
1958
1958
  };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Allowlist HTML sanitizer for host-supplied "token notes" content (#515).
3
+ *
4
+ * Security boundary: a strict ALLOWLIST of tags and attributes, not a
5
+ * blacklist — anything not explicitly permitted is stripped, including
6
+ * tags/attributes a blacklist would never have anticipated (custom elements,
7
+ * future HTML, namespaced foreign-content attributes). This is the package's
8
+ * only HTML sanitizer (see `controls/sanitize-css-value.ts` for the separate
9
+ * CSS-value sanitizer used elsewhere) and it is the thing standing between a
10
+ * host's `notesExtras.html` string and `dangerouslySetInnerHTML` in
11
+ * `tabs/notes-tab.tsx`.
12
+ *
13
+ * Implementation: parse with the browser's own `DOMParser` (dependency-free —
14
+ * no external sanitizer library), then rebuild a FRESH tree by walking the
15
+ * parsed result and copying across only allowlisted tags/attributes. Using
16
+ * the real HTML parser means malformed markup (mismatched tags, unclosed
17
+ * elements, HTML entities) is resolved into a well-formed DOM tree by the
18
+ * browser itself before the allowlist walk ever runs — the walk only ever
19
+ * sees valid nodes, never raw markup text it would have to re-parse or guess
20
+ * at (the classic weakness of a regex/string-replace "sanitizer").
21
+ */
22
+ /**
23
+ * Sanitize host-supplied HTML (`TabConfig.notesExtras.html`) against the
24
+ * strict allowlist above. Returns an HTML string safe to pass to
25
+ * `dangerouslySetInnerHTML`.
26
+ */
27
+ export declare function sanitizeHtml(html: string): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Helpers for the opt-in click-to-cycle unit suffix on `length`-kind value
3
+ * rows (#519). Pure, DOM-free — safe to import from any editor component.
4
+ *
5
+ * `deriveCyclableUnit` is the single source of the opt-in/divergence policy;
6
+ * both editor call sites (`tabs/_generic-item-editor.tsx` and
7
+ * `tabs/generic-tab.tsx`) call it instead of re-deriving the same state.
8
+ */
9
+ /**
10
+ * Split a stored length value (e.g. `"1.5rem"`, `".5rem"`) into its numeric
11
+ * magnitude and unit suffix. Accepts the same leading-number shapes as the
12
+ * platform's own `parseFloat` (leading-dot decimals included, e.g. `.5`) so
13
+ * this parser and `parseFloat(value)` never disagree on the same input.
14
+ * Mirrors (and slightly widens) the leading-number regex in
15
+ * `tokens/manifest.ts#parseNumericValue`; also returns the trailing suffix
16
+ * text so the interactive unit control can display the value's ACTUAL
17
+ * stored suffix rather than assuming the item's declared `type.unit` (a
18
+ * stored value can diverge from the declared unit — imported data, a
19
+ * manifest change, etc.).
20
+ *
21
+ * Returns `null` when the value has no leading number (e.g. a `clamp()`
22
+ * expression on a readonly row).
23
+ */
24
+ export declare function splitLengthValue(value: string): {
25
+ magnitude: number;
26
+ suffix: string;
27
+ } | null;
28
+ /**
29
+ * Resolve the next unit when cycling through a declared `units` list
30
+ * (wrapping). When `currentUnit` is not present in `units` — a stored value
31
+ * whose suffix diverged from the declared list — the first activation lands
32
+ * on `units[0]` rather than guessing or throwing.
33
+ */
34
+ export declare function nextCyclableUnit(units: readonly string[], currentUnit: string): string;
35
+ export interface CyclableUnitState {
36
+ /** Declared cyclable unit list — defined only when 2+ entries were declared. */
37
+ cyclableUnits: readonly string[] | undefined;
38
+ /** True iff `cyclableUnits` is defined (2+ entries) — the row is interactive.
39
+ * A present-but-single-entry (or omitted) `units` list is a DELIBERATE
40
+ * "not opted in yet" signal, not a config error — it keeps today's static,
41
+ * non-interactive unit span pixel-identical. */
42
+ isCyclableUnit: boolean;
43
+ /** Unit suffix to display, and to cycle FROM on the next activation. */
44
+ effectiveUnit: string;
45
+ /**
46
+ * Magnitude parsed by the SAME pass that produced `effectiveUnit` — reuse
47
+ * this (not a separately-computed `parseFloat(value)`) when emitting a
48
+ * post-cycle value, so the suffix-detection parse and the magnitude the
49
+ * cycle emits can never disagree. `null` when the stored value did not
50
+ * parse as a length at all; callers should fall back to their own
51
+ * last-known-good numeric state (e.g. the number input's draft) in that
52
+ * case.
53
+ */
54
+ parsedMagnitude: number | null;
55
+ }
56
+ /**
57
+ * Derive the full opt-in/display/divergence state for a `length`-kind row's
58
+ * unit suffix from its declared `unit`/`units` and its current stored
59
+ * `value`. Single source of truth for both editor call sites.
60
+ */
61
+ export declare function deriveCyclableUnit(unit: string, units: readonly string[] | undefined, value: string): CyclableUnitState;