@kitlangton/knobs 0.4.0-next.11 → 0.4.0-next.13

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/dist/index.js CHANGED
@@ -473,6 +473,16 @@ function pickMeta(source, skip = []) {
473
473
  return out;
474
474
  }
475
475
  var READOUT_KINDS = /* @__PURE__ */ new Set(["watch", "sparkline", "graph", "meter", "table", "log"]), isReadout = (field) => READOUT_KINDS.has(field.kind), hasValue = (field) => field.kind !== "action" && field.kind !== "toolbar" && !READOUT_KINDS.has(field.kind);
476
+ function variantsOf(schema, isPinned = () => !1) {
477
+ let found;
478
+ for (let [key, field] of Object.entries(schema))
479
+ if (field.kind === "choice" && field.variants && !isPinned(key)) {
480
+ if (found)
481
+ throw Error(`Only one choice can hold the variants (${found.key}, ${key}).`);
482
+ found = { key, field };
483
+ }
484
+ return found;
485
+ }
476
486
  function pinnedKeys(schema, groups = []) {
477
487
  let byGroup = new Map(groups.map((g) => [g, !0]));
478
488
  for (let f of Object.values(schema))
@@ -515,8 +525,9 @@ function choice(value, options, meta) {
515
525
  value,
516
526
  options: Object.freeze([...options]),
517
527
  ...labels && Object.keys(labels).length ? { labels } : {},
518
- ...meta?.display ? { display: meta.display } : {}
519
- }, meta);
528
+ ...meta?.display ? { display: meta.display } : {},
529
+ ...meta?.variants ? { variants: !0 } : {}
530
+ }, meta?.variants ? { ...meta, random: meta.random ?? !1 } : meta);
520
531
  }
521
532
  function action(label, meta) {
522
533
  let { shortcut, global, rebind, run, ...rest } = meta ?? {}, key = shortcutOf({ ...shortcut ? { shortcut } : {}, ...global ? { global } : {} }), combo = key ? normalizeCombo(key.key) : "";
@@ -661,6 +672,17 @@ function keybind(value = "", meta) {
661
672
  let field = { kind: "keybind", value: normalizeCombo(value) };
662
673
  return withMeta(field, { random: !1, ...meta });
663
674
  }
675
+ function text(value = "", options) {
676
+ let { multiline, placeholder, ...meta } = options ?? {}, field = {
677
+ kind: "text",
678
+ value: "",
679
+ ...multiline ? { multiline: !0 } : {},
680
+ ...placeholder ? { placeholder } : {}
681
+ };
682
+ return withMeta({ ...field, value: normalizeText(field, value) }, { random: !1, ...meta });
683
+ }
684
+ var normalizeText = (field, input) => field.multiline ? input.replace(/\r\n?/g, `
685
+ `) : input.replace(/\r\n?|\n/g, " ");
664
686
  function gradient(stops, options = {}) {
665
687
  let value = normalizeStops(stops);
666
688
  if (!value)
@@ -715,7 +737,8 @@ function toolbar(items, meta) {
715
737
  throw Error("A toolbar needs at least one item.");
716
738
  return withMeta({
717
739
  kind: "toolbar",
718
- items: Object.freeze(items.map((item) => Object.freeze({ ...item })))
740
+ items: Object.freeze(items.map((item) => Object.freeze({ ...item }))),
741
+ ...meta?.labels !== void 0 ? { labels: meta.labels } : {}
719
742
  }, meta);
720
743
  }
721
744
  var readoutMeta = (o) => {
@@ -859,6 +882,8 @@ function normalizeField(field, input) {
859
882
  return typeof input === "number" && Number.isFinite(input) ? Math.round(Math.min(field.max, Math.max(field.min, input))) : field.value;
860
883
  case "keybind":
861
884
  return typeof input === "string" ? normalizeCombo(input) : field.value;
885
+ case "text":
886
+ return typeof input === "string" ? normalizeText(field, input) : field.value;
862
887
  case "gradient":
863
888
  return normalizeStops(input) ?? field.value;
864
889
  case "curve":
@@ -1009,6 +1034,7 @@ function pick(field, random) {
1009
1034
  case "seed":
1010
1035
  return lattice(field.min, field.max, 1, r());
1011
1036
  case "keybind":
1037
+ case "text":
1012
1038
  return field.value;
1013
1039
  case "gradient":
1014
1040
  return randomStops(field.value.length, r);
@@ -1094,6 +1120,7 @@ function around(field, base, random) {
1094
1120
  ]);
1095
1121
  case "seed":
1096
1122
  case "keybind":
1123
+ case "text":
1097
1124
  return pick(field, random);
1098
1125
  }
1099
1126
  }
@@ -1193,6 +1220,7 @@ function shift(field, value, random) {
1193
1220
  case "seed":
1194
1221
  return nudge(value, field.min, field.max, 1, random);
1195
1222
  case "keybind":
1223
+ case "text":
1196
1224
  return value;
1197
1225
  case "gradient":
1198
1226
  return mutateStops(value, random, MUTATE.hue, MUTATE.lightness);
@@ -1509,7 +1537,7 @@ function unlock(options) {
1509
1537
  }
1510
1538
 
1511
1539
  // src/core/variants.ts
1512
- function createVariants(schema, id, key, storage, get, set, notify, enabled) {
1540
+ function createVariants(schema, id, key, storage, get, set, notify, enabled, seed) {
1513
1541
  let ready = !1, list = Object.freeze([]);
1514
1542
  function parse(json) {
1515
1543
  let data = JSON.parse(json);
@@ -1538,7 +1566,7 @@ function createVariants(schema, id, key, storage, get, set, notify, enabled) {
1538
1566
  if (ready || !enabled())
1539
1567
  return;
1540
1568
  let json = storage()?.getItem(key);
1541
- list = json ? parse(json) : Object.freeze([]), ready = !0;
1569
+ list = json ? parse(json) : Object.freeze([...seed?.() ?? []]), ready = !0;
1542
1570
  }
1543
1571
  function persist(next) {
1544
1572
  if (!enabled())
@@ -1625,32 +1653,69 @@ function defineControls(id, input, options = {}) {
1625
1653
  let schema = Object.freeze({ ...input }), locked = normalize(schema, {}), actions = Object.freeze([...options.actions ?? []]), { theme, appearance } = options, density = options.density ?? "default", renderer = options.renderer ?? "auto", enabled = () => (Boolean(options.ship) || isUnlocked()) && (typeof options.enabled === "function" ? options.enabled() : options.enabled ?? (Boolean(options.ship) || isUnlocked() || typeof location < "u" && ["localhost", "127.0.0.1", "[::1]"].includes(location.hostname))), storage = () => options.storage === void 0 ? browserStorage() : options.storage, persistMode = options.persist ?? "all", valueStorage = () => persistMode === "all" ? storage() : null, variantStorage = () => persistMode === "none" ? null : storage(), draw = options.random ?? Math.random, hostHistory = options.history, unsubscribeHistory, unsubscribeUnlock, listeners = /* @__PURE__ */ new Set, refreshes = 0, notify = () => {
1626
1654
  for (let cb of [...listeners])
1627
1655
  cb();
1628
- }, mixed = /* @__PURE__ */ new Set, current = locked, read = !1, error = null, locks = /* @__PURE__ */ new Set, bindings = /* @__PURE__ */ new Map, comparing = null, landing = !1, echo = Math.max(0, options.echo ?? 0), grabbed = /* @__PURE__ */ new Set, changedAt = /* @__PURE__ */ new Map, sent = /* @__PURE__ */ new Map, pending = /* @__PURE__ */ new Map, echoTimer, now = () => typeof performance < "u" ? performance.now() : Date.now(), isHeld = (key) => grabbed.has(key) || echo > 0 && now() - (changedAt.get(key) ?? -1 / 0) < echo, NO_BASE = Object.freeze({ preset: null, groups: Object.freeze({}) }), base = NO_BASE, past = [], future = [], HISTORY = 100, stateKey = storageKey(id, "state"), pinnedOf, pinned = () => {
1656
+ }, mixed = /* @__PURE__ */ new Set, current = locked, read = !1, error = null, locks = /* @__PURE__ */ new Set, bindings = /* @__PURE__ */ new Map, comparing = null, landing = !1, echo = Math.max(0, options.echo ?? 0), grabbed = /* @__PURE__ */ new Set, changedAt = /* @__PURE__ */ new Map, sent = /* @__PURE__ */ new Map, pending = /* @__PURE__ */ new Map, echoTimer, now = () => typeof performance < "u" ? performance.now() : Date.now(), isHeld = (key) => grabbed.has(key) || echo > 0 && now() - (changedAt.get(key) ?? -1 / 0) < echo, NO_BASE = Object.freeze({
1657
+ preset: null,
1658
+ groups: Object.freeze({}),
1659
+ scopes: Object.freeze({})
1660
+ }), base = NO_BASE, past = [], future = [], HISTORY = 100, stateKey = storageKey(id, "state"), pinnedOf, pinned = () => {
1629
1661
  if (pinnedOf?.schema !== schema)
1630
1662
  pinnedOf = { schema, keys: pinnedKeys(schema, options.pinned) };
1631
1663
  return pinnedOf.keys;
1632
- }, unpinned = (values) => {
1633
- let keys = pinned();
1634
- if (keys.size === 0)
1635
- return values;
1636
- let out = { ...values };
1637
- for (let k of keys)
1638
- if (Object.hasOwn(locked, k))
1639
- out[k] = locked[k];
1640
- return Object.freeze(out);
1641
- }, presetPatch = (values) => {
1642
- let keys = pinned();
1643
- if (keys.size === 0)
1644
- return values;
1645
- return Object.fromEntries(Object.entries(values).filter(([k]) => !keys.has(k)));
1646
- }, buildPresets = () => Object.freeze(Object.fromEntries([
1647
- ["default", locked],
1648
- ...Object.entries(options.presets ?? {}).map(([id, patch]) => {
1649
- if (id === "default" || id === "custom")
1650
- throw Error(`Reserved preset id: ${id}`);
1651
- return [id, unpinned(normalize(schema, { ...locked, ...patch }))];
1652
- })
1653
- ])), presets = buildPresets(), buildGroupPresets = () => Object.freeze(Object.fromEntries(Object.entries(options.groupPresets ?? {}).map(([group, list]) => {
1664
+ }, variantsChoice = () => variantsOf(schema, (key) => pinned().has(key)), scopingOf, scoping = () => {
1665
+ if (scopingOf?.schema === schema)
1666
+ return scopingOf;
1667
+ let fields = schema, groupTab = /* @__PURE__ */ new Map;
1668
+ for (let f of Object.values(fields))
1669
+ if (f.group !== void 0 && f.tab !== void 0 && !groupTab.has(f.group))
1670
+ groupTab.set(f.group, f.tab);
1671
+ let tabOf = (f) => f.tab ?? (f.group !== void 0 ? groupTab.get(f.group) : void 0), named = new Set(Object.keys(options.scopedPresets ?? {}));
1672
+ if (named.has(""))
1673
+ throw Error("A scope needs a nonempty tab or group id.");
1674
+ let v = variantsChoice(), group = v?.field.group, variants = v ? tabOf(v.field) ?? (group !== void 0 && named.has(group) ? group : "") : void 0;
1675
+ if (variants)
1676
+ named.add(variants);
1677
+ let of = /* @__PURE__ */ new Map;
1678
+ for (let k of valueKeys(schema)) {
1679
+ if (pinned().has(k))
1680
+ continue;
1681
+ let f = fields[k], tab = tabOf(f);
1682
+ of.set(k, tab !== void 0 && named.has(tab) ? tab : f.group !== void 0 && named.has(f.group) ? f.group : "");
1683
+ }
1684
+ return scopingOf = { schema, of, named: [...named], variants }, scopingOf;
1685
+ }, within = (values, scope) => {
1686
+ let of = scoping().of, out;
1687
+ for (let k of Object.keys(locked))
1688
+ if (of.get(k) !== scope && !same(values[k], locked[k]))
1689
+ out ??= { ...values }, out[k] = locked[k];
1690
+ return out ? Object.freeze(out) : values;
1691
+ }, unpinned = (values) => within(values, ""), patchIn = (values, scope) => {
1692
+ let of = scoping().of;
1693
+ return Object.fromEntries(Object.entries(values).filter(([k]) => of.get(k) === scope));
1694
+ }, buildPresets = (scope) => {
1695
+ let own = (scope === "" ? options.presets : options.scopedPresets?.[scope]) ?? {}, v = scoping().variants === scope ? variantsChoice() : void 0;
1696
+ if (v && Object.hasOwn(own, v.field.value))
1697
+ throw Error(`The default variant (${v.field.value}) is the schema's defaults; set its values there.`);
1698
+ let ids = [
1699
+ ...v ? v.field.options.filter((o) => o !== v.field.value) : [],
1700
+ ...Object.keys(own).filter((id) => !v?.field.options.includes(id))
1701
+ ];
1702
+ return Object.freeze(Object.fromEntries([
1703
+ ["default", locked],
1704
+ ...ids.map((id) => {
1705
+ if (id === "default" || id === "custom")
1706
+ throw Error(`Reserved preset id: ${id}`);
1707
+ let variant = v?.field.options.includes(id) ? { [v.key]: id } : {};
1708
+ return [id, within(normalize(schema, { ...locked, ...variant, ...own[id] }), scope)];
1709
+ })
1710
+ ]));
1711
+ }, buildScopePresets = () => new Map(scoping().named.map((scope) => [scope, buildPresets(scope)])), presets = buildPresets(""), scopePresets = buildScopePresets(), presetsIn = (scope) => {
1712
+ if (scope === "")
1713
+ return presets;
1714
+ let found = scopePresets.get(scope);
1715
+ if (!found)
1716
+ throw Error(`Unknown preset scope: ${scope}`);
1717
+ return found;
1718
+ }, buildGroupPresets = () => Object.freeze(Object.fromEntries(Object.entries(options.groupPresets ?? {}).map(([group, list]) => {
1654
1719
  let named = list.flatMap((p) => Object.keys(p.values).filter((k) => Object.hasOwn(schema, k))), covered = new Set((named.length ? named : Object.keys(schema).filter((k) => schema[k].group === group)).filter((k) => hasValue(schema[k])));
1655
1720
  return [
1656
1721
  group,
@@ -1715,22 +1780,54 @@ function defineControls(id, input, options = {}) {
1715
1780
  function commit(next) {
1716
1781
  current = next, persist(), notify();
1717
1782
  }
1718
- let makeBase = (preset, groups) => preset === null && Object.keys(groups).length === 0 ? NO_BASE : Object.freeze({ preset, groups: Object.freeze({ ...groups }) }), sameBase = (a, b) => a === b || a.preset?.kind === b.preset?.kind && a.preset?.id === b.preset?.id && Object.keys(a.groups).length === Object.keys(b.groups).length && Object.entries(a.groups).every(([g, i]) => b.groups[g] === i);
1783
+ let makeBase = (preset, groups, scopes = {}) => preset === null && Object.keys(groups).length === 0 && Object.keys(scopes).length === 0 ? NO_BASE : Object.freeze({
1784
+ preset,
1785
+ groups: Object.freeze({ ...groups }),
1786
+ scopes: Object.freeze({ ...scopes })
1787
+ }), withScope = (scopes, scope, ref) => {
1788
+ let { [scope]: _, ...others } = scopes;
1789
+ return ref ? { ...others, [scope]: ref } : others;
1790
+ }, sameRef = (a, b) => a?.kind === b?.kind && a?.id === b?.id, sameBase = (a, b) => a === b || sameRef(a.preset, b.preset) && Object.keys(a.groups).length === Object.keys(b.groups).length && Object.entries(a.groups).every(([g, i]) => b.groups[g] === i) && Object.keys(a.scopes).length === Object.keys(b.scopes).length && Object.entries(a.scopes).every(([s, ref]) => sameRef(ref, b.scopes[s])), seedsScopes = () => scopePresets.size > 0 && Object.keys(options.presets ?? {}).length === 0, groupsOutside = (groups, scope) => {
1791
+ let of = scoping().of;
1792
+ return Object.fromEntries(Object.entries(groups).filter(([g]) => !(groupPresets[g] ?? []).some((p) => Object.keys(p.values).some((k) => of.get(k) === scope))));
1793
+ }, known = (ref, scope) => ref.kind === "variant" || ref.id !== "default" && Object.hasOwn(scopePresets.get(scope) ?? {}, ref.id);
1719
1794
  function validBase(b) {
1720
- let preset = b.preset?.kind === "preset" && !(b.preset.id !== "default" && Object.hasOwn(presets, b.preset.id)) ? null : b.preset, groups = Object.fromEntries(Object.entries(b.groups).filter(([g, i]) => groupPresets[g]?.[i] !== void 0)), next = makeBase(preset, groups);
1795
+ let preset = b.preset?.kind === "preset" && !(b.preset.id !== "default" && Object.hasOwn(presets, b.preset.id)) ? null : b.preset, groups = Object.fromEntries(Object.entries(b.groups).filter(([g, i]) => groupPresets[g]?.[i] !== void 0)), scopes = Object.fromEntries(Object.entries(b.scopes).filter(([s, ref]) => scopePresets.has(s) && known(ref, s))), next = makeBase(preset, groups, scopes);
1721
1796
  return sameBase(next, b) ? b : next;
1722
1797
  }
1798
+ let readRef = (raw) => raw && typeof raw === "object" && (raw.kind === "preset" || raw.kind === "variant") && typeof raw.id === "string" ? Object.freeze({ kind: raw.kind, id: raw.id }) : null;
1723
1799
  function readBase(raw) {
1724
1800
  if (!raw || typeof raw !== "object")
1725
1801
  return NO_BASE;
1726
- let { preset, groups } = raw, ref = preset && typeof preset === "object" && (preset.kind === "preset" || preset.kind === "variant") && typeof preset.id === "string" ? Object.freeze({ kind: preset.kind, id: preset.id }) : null, byGroup = groups && typeof groups === "object" ? Object.fromEntries(Object.entries(groups).filter((e) => Number.isInteger(e[1]))) : {};
1727
- return validBase(makeBase(ref, byGroup));
1802
+ let { preset, groups, scopes } = raw, ref = readRef(preset), byGroup = groups && typeof groups === "object" ? Object.fromEntries(Object.entries(groups).filter((e) => Number.isInteger(e[1]))) : {}, byScope = {};
1803
+ if (scopes && typeof scopes === "object")
1804
+ for (let [s, r] of Object.entries(scopes)) {
1805
+ let found = readRef(r);
1806
+ if (found)
1807
+ byScope[s] = found;
1808
+ }
1809
+ if (ref?.kind === "preset" && !Object.hasOwn(presets, ref.id)) {
1810
+ for (let [s, list] of scopePresets)
1811
+ if (!byScope[s] && ref.id !== "default" && Object.hasOwn(list, ref.id))
1812
+ byScope[s] = ref;
1813
+ ref = null;
1814
+ } else if (ref?.kind === "variant" && seedsScopes()) {
1815
+ let id = ref.id;
1816
+ for (let s of scopePresets.keys())
1817
+ if (!byScope[s] && scopeVariants(s).list().some((v) => v.id === id))
1818
+ byScope[s] = ref;
1819
+ ref = null;
1820
+ }
1821
+ return validBase(makeBase(ref, byGroup, byScope));
1728
1822
  }
1729
1823
  function activeBase() {
1730
1824
  load();
1731
- let ref = base.preset;
1732
- if (ref?.kind === "variant" && !variants.list().some((v) => v.id === ref.id))
1733
- base = makeBase(null, base.groups);
1825
+ let gone = (ref, list) => ref?.kind === "variant" && !list().some((v) => v.id === ref.id), preset = gone(base.preset, () => variants.list()) ? null : base.preset, scopes = base.scopes;
1826
+ for (let [s, ref] of Object.entries(base.scopes))
1827
+ if (gone(ref, () => scopeVariants(s).list()))
1828
+ scopes = withScope(scopes, s, void 0);
1829
+ if (preset !== base.preset || scopes !== base.scopes)
1830
+ base = makeBase(preset, base.groups, scopes);
1734
1831
  return base;
1735
1832
  }
1736
1833
  let hasBase = () => activeBase() !== NO_BASE, baseCache;
@@ -1740,17 +1837,23 @@ function defineControls(id, input, options = {}) {
1740
1837
  let b = activeBase();
1741
1838
  if (b === NO_BASE)
1742
1839
  return locked;
1743
- let list = b.preset?.kind === "variant" ? variants.list() : null, c = baseCache;
1744
- if (c && c.base === b && c.schema === schema && c.presets === presets && c.groupPresets === groupPresets && c.variants === list)
1840
+ let lists = [
1841
+ b.preset?.kind === "variant" ? variants.list() : null,
1842
+ ...Object.entries(b.scopes).map(([s, ref]) => ref.kind === "variant" ? scopeVariants(s).list() : null)
1843
+ ], c = baseCache;
1844
+ if (c && c.base === b && c.schema === schema && c.presets === presets && c.scopePresets === scopePresets && c.groupPresets === groupPresets && c.variants.length === lists.length && c.variants.every((l, i) => l === lists[i]))
1745
1845
  return c.values;
1746
- let out = locked, ref = b.preset;
1747
- if (ref?.kind === "preset" && Object.hasOwn(presets, ref.id))
1748
- out = presets[ref.id];
1749
- else if (ref?.kind === "variant") {
1846
+ let valuesOf = (ref, scope, list) => {
1847
+ if (ref.kind === "preset")
1848
+ return presetsIn(scope)[ref.id];
1750
1849
  let found = list?.find((v) => v.id === ref.id);
1751
- if (found)
1752
- out = unpinned(normalize(schema, found.values));
1753
- }
1850
+ return found ? within(normalize(schema, found.values), scope) : void 0;
1851
+ }, out = locked;
1852
+ if (b.preset)
1853
+ out = valuesOf(b.preset, "", lists[0]) ?? locked;
1854
+ let scoped = Object.entries(b.scopes);
1855
+ if (scoped.length)
1856
+ out = { ...out }, scoped.forEach(([s, ref], i) => Object.assign(out, patchIn(valuesOf(ref, s, lists[i + 1]) ?? locked, s)));
1754
1857
  let groups = Object.entries(b.groups);
1755
1858
  if (groups.length) {
1756
1859
  out = { ...out };
@@ -1758,13 +1861,16 @@ function defineControls(id, input, options = {}) {
1758
1861
  Object.assign(out, groupPresets[g]?.[i]?.values);
1759
1862
  }
1760
1863
  let values = Object.freeze(out);
1761
- return baseCache = { base: b, schema, presets, groupPresets, variants: list, values }, values;
1864
+ return baseCache = { base: b, schema, presets, scopePresets, groupPresets, variants: lists, values }, values;
1762
1865
  }
1763
1866
  let covered = (key) => {
1764
1867
  let b = activeBase(), group = schema[key]?.group;
1765
1868
  if (group !== void 0 && Object.hasOwn(b.groups, group))
1766
1869
  return !0;
1767
- return b.preset !== null && !pinned().has(key);
1870
+ let scope = scoping().of.get(key);
1871
+ if (scope === void 0)
1872
+ return !1;
1873
+ return scope === "" ? b.preset !== null : Object.hasOwn(b.scopes, scope);
1768
1874
  };
1769
1875
  function declaredShortcut(id) {
1770
1876
  let slash = id.indexOf("/"), key = slash < 0 ? id : id.slice(0, slash), field = Object.hasOwn(schema, key) ? schema[key] : void 0;
@@ -1999,7 +2105,7 @@ function defineControls(id, input, options = {}) {
1999
2105
  schema = Object.freeze({ ...next });
2000
2106
  if (changed) {
2001
2107
  let previous = load();
2002
- schema = Object.freeze({ ...next }), locked = normalize(schema, {}), presets = buildPresets(), groupPresets = buildGroupPresets(), base = validBase(base);
2108
+ schema = Object.freeze({ ...next }), locked = normalize(schema, {}), presets = buildPresets(""), scopePresets = buildScopePresets(), groupPresets = buildGroupPresets(), base = validBase(base);
2003
2109
  for (let k of [...locks])
2004
2110
  if (!valueField(k))
2005
2111
  locks.delete(k);
@@ -2034,7 +2140,33 @@ function defineControls(id, input, options = {}) {
2034
2140
  listen(cb) {
2035
2141
  return readoutListeners.add(cb), () => readoutListeners.delete(cb);
2036
2142
  }
2037
- }, variants = createVariants(() => schema, id, storageKey(id, "variants"), variantStorage, () => unpinned(get()), (v, variant, coalesce) => write(presetPatch(v), { source: "variant", coalesce }, makeBase(Object.freeze({ kind: "variant", id: variant }), {})), notify, enabled), resettable = (keys) => (keys ?? fieldKeys()).filter((k) => valueField(k) && !blocked(k));
2143
+ }, variants = createVariants(() => schema, id, storageKey(id, "variants"), variantStorage, () => unpinned(get()), (v, variant, coalesce) => write(patchIn(v, ""), { source: "variant", coalesce }, makeBase(Object.freeze({ kind: "variant", id: variant }), {}, activeBase().scopes)), notify, enabled), scopeVariantsMade = /* @__PURE__ */ new Map;
2144
+ function scopeVariants(scope) {
2145
+ let made = scopeVariantsMade.get(scope);
2146
+ if (made)
2147
+ return made;
2148
+ return made = createVariants(() => schema, id, storageKey(id, `variants:${scope}`), variantStorage, () => within(get(), scope), (v, variant, coalesce) => {
2149
+ let b = activeBase();
2150
+ write(patchIn(v, scope), { source: "variant", coalesce }, makeBase(b.preset, groupsOutside(b.groups, scope), withScope(b.scopes, scope, Object.freeze({ kind: "variant", id: variant }))));
2151
+ }, notify, enabled, () => !seedsScopes() ? [] : variants.list().flatMap((v) => {
2152
+ let values = within(normalize(schema, v.values), scope);
2153
+ return equal(values, locked) ? [] : [{ ...v, values }];
2154
+ })), scopeVariantsMade.set(scope, made), made;
2155
+ }
2156
+ let scopesOf, scopes = () => {
2157
+ if (scopesOf?.presets === scopePresets)
2158
+ return scopesOf.value;
2159
+ let of = scoping().of, value = Object.freeze(Object.fromEntries([...scopePresets].map(([scope, list]) => [
2160
+ scope,
2161
+ Object.freeze({
2162
+ id: scope,
2163
+ keys: Object.freeze([...of].flatMap(([k, s]) => s === scope ? [k] : [])),
2164
+ presets: list,
2165
+ variants: scopeVariants(scope)
2166
+ })
2167
+ ])));
2168
+ return scopesOf = { presets: scopePresets, value }, value;
2169
+ }, resettable = (keys) => (keys ?? fieldKeys()).filter((k) => valueField(k) && !blocked(k));
2038
2170
  function offBase(keys) {
2039
2171
  if (!hasBase())
2040
2172
  return !1;
@@ -2098,6 +2230,10 @@ function defineControls(id, input, options = {}) {
2098
2230
  get presets() {
2099
2231
  return presets;
2100
2232
  },
2233
+ get scopes() {
2234
+ return scopes();
2235
+ },
2236
+ scopeOf: (key) => scoping().of.get(key),
2101
2237
  actions,
2102
2238
  theme,
2103
2239
  appearance,
@@ -2118,7 +2254,10 @@ function defineControls(id, input, options = {}) {
2118
2254
  sectionDescription: (section) => options.sectionDescriptions?.[section] ?? fieldLabels().sectionText.get(section),
2119
2255
  groupFolded: options.groupFolded,
2120
2256
  sectionFolded: options.sectionFolded,
2121
- presetLabel: (presetId) => options.presetLabels?.[presetId] ?? (presetId === "default" ? options.defaultLabel : void 0) ?? presetId,
2257
+ presetLabel: (presetId, scope = "") => {
2258
+ let v = scoping().variants === scope ? variantsChoice() : void 0, option = presetId === "default" ? v?.field.value : presetId;
2259
+ return options.presetLabels?.[presetId] ?? (presetId === "default" ? options.defaultLabel : void 0) ?? (v && option !== void 0 && v.field.options.includes(option) ? v.field.labels?.[option] ?? option : presetId);
2260
+ },
2122
2261
  isPinned: (key) => pinned().has(key),
2123
2262
  get,
2124
2263
  set,
@@ -2247,7 +2386,7 @@ function defineControls(id, input, options = {}) {
2247
2386
  options.onPreset?.({ group, index, name: preset.name });
2248
2387
  return;
2249
2388
  }
2250
- let { [group]: _, ...others } = b.groups, forget = index === void 0 ? void 0 : makeBase(b.preset, others);
2389
+ let { [group]: _, ...others } = b.groups, forget = index === void 0 ? void 0 : makeBase(b.preset, others, b.scopes);
2251
2390
  if (open.length < keys.length)
2252
2391
  return toDefaults(open, forget);
2253
2392
  let patch = {};
@@ -2392,13 +2531,15 @@ function defineControls(id, input, options = {}) {
2392
2531
  comparing = before, options.onChange?.(changes(now, before), before, { source: "compare" }), notify();
2393
2532
  },
2394
2533
  preset(id, presetOptions) {
2395
- if (!Object.hasOwn(presets, id))
2534
+ let scope = presetOptions?.scope ?? "", list = presetsIn(scope);
2535
+ if (!Object.hasOwn(list, id))
2396
2536
  throw Error(`Unknown preset: ${id}`);
2397
- write(presetPatch(presets[id]), { source: "preset", coalesce: presetOptions?.coalesce }, id === "default" ? NO_BASE : makeBase(Object.freeze({ kind: "preset", id }), {}));
2537
+ let b = activeBase(), ref = id === "default" ? void 0 : Object.freeze({ kind: "preset", id });
2538
+ write(patchIn(list[id], scope), { source: "preset", coalesce: presetOptions?.coalesce }, scope === "" ? makeBase(ref ?? null, {}, b.scopes) : makeBase(b.preset, groupsOutside(b.groups, scope), withScope(b.scopes, scope, ref)));
2398
2539
  },
2399
- matchingPreset: () => {
2400
- let cur = unpinned(get());
2401
- return Object.keys(presets).find((id) => equal(cur, presets[id])) ?? "custom";
2540
+ matchingPreset: (scope = "") => {
2541
+ let list = presetsIn(scope), cur = within(get(), scope);
2542
+ return Object.keys(list).find((id) => equal(cur, list[id])) ?? "custom";
2402
2543
  },
2403
2544
  get groupPresets() {
2404
2545
  return groupPresets;
@@ -2410,12 +2551,13 @@ function defineControls(id, input, options = {}) {
2410
2551
  if (!enabled())
2411
2552
  return;
2412
2553
  let b = activeBase();
2413
- write(preset.values, { source: "preset", group, coalesce: presetOptions?.coalesce }, makeBase(b.preset, { ...b.groups, [group]: index })), options.onPreset?.({ group, index, name: preset.name });
2554
+ write(preset.values, { source: "preset", group, coalesce: presetOptions?.coalesce }, makeBase(b.preset, { ...b.groups, [group]: index }, b.scopes)), options.onPreset?.({ group, index, name: preset.name });
2414
2555
  },
2415
- activePreset() {
2556
+ activePreset(scope) {
2416
2557
  if (!enabled())
2417
2558
  return null;
2418
- return activeBase().preset;
2559
+ let b = activeBase();
2560
+ return scope ? b.scopes[scope] ?? null : b.preset;
2419
2561
  },
2420
2562
  activeGroupPreset(group) {
2421
2563
  if (!enabled())
@@ -2471,7 +2613,7 @@ function descriptorToField(d) {
2471
2613
  if (readoutDescriptor(d))
2472
2614
  return readoutField(d);
2473
2615
  if (d.kind === "toolbar")
2474
- return toolbar(d.items.map(({ key, ...item }) => ({ ...item, id: key })), meta);
2616
+ return toolbar(d.items.map(({ key, ...item }) => ({ ...item, id: key })), d.labels !== void 0 ? { ...meta, labels: d.labels } : meta);
2475
2617
  if (d.bind)
2476
2618
  return flatSpring({}, { ...meta, id: d.key }).duration;
2477
2619
  if (d.kind === "knob" || d.kind === "number") {
@@ -2546,6 +2688,12 @@ function descriptorToField(d) {
2546
2688
  });
2547
2689
  if (d.kind === "keybind")
2548
2690
  return keybind(typeof d.default === "string" ? d.default : "", meta);
2691
+ if (d.kind === "text")
2692
+ return text(typeof d.default === "string" ? d.default : "", {
2693
+ ...d.multiline ? { multiline: !0 } : {},
2694
+ ...d.placeholder ? { placeholder: d.placeholder } : {},
2695
+ ...meta
2696
+ });
2549
2697
  if (d.kind === "gradient") {
2550
2698
  let fallback = [
2551
2699
  { at: 0, color: "#000000" },
@@ -2624,7 +2772,7 @@ var decimal = (n) => String(Math.round(n * 1e4) / 1e4), swiftLiteral = (d, v) =>
2624
2772
  let [a, b] = v;
2625
2773
  return `${decimal(a)}...${decimal(b)}`;
2626
2774
  }
2627
- if (d.kind === "keybind")
2775
+ if (d.kind === "keybind" || d.kind === "text")
2628
2776
  return JSON.stringify(v);
2629
2777
  if (d.kind === "gradient")
2630
2778
  return `Gradient(stops: [${(Array.isArray(v) ? v : []).map((s) => {
@@ -2653,6 +2801,7 @@ var decimal = (n) => String(Math.round(n * 1e4) / 1e4), swiftLiteral = (d, v) =>
2653
2801
  range: "ClosedRange<Double>",
2654
2802
  seed: "Int",
2655
2803
  keybind: "String",
2804
+ text: "String",
2656
2805
  gradient: "Gradient",
2657
2806
  curve: "[CGPoint]"
2658
2807
  };
@@ -2726,6 +2875,7 @@ function createRemoteControls(id, descriptors, options = {}) {
2726
2875
  tunable: options.tunable,
2727
2876
  history: options.history,
2728
2877
  presets: options.presets,
2878
+ scopedPresets: options.scopedPresets,
2729
2879
  pinned: options.pinned,
2730
2880
  groupPresets: options.groupPresets,
2731
2881
  onPreset: options.onPreset,
@@ -2862,6 +3012,7 @@ export {
2862
3012
  springCss,
2863
3013
  springMotion,
2864
3014
  table,
3015
+ text,
2865
3016
  toggle,
2866
3017
  toolbar,
2867
3018
  toolbarShortcut,