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

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.
@@ -46,7 +46,7 @@ import {
46
46
  pushReadout,
47
47
  READOUT,
48
48
  storageKey
49
- } from "./chunk-7kns3qe6.js";
49
+ } from "./chunk-v2g91mrm.js";
50
50
  import {
51
51
  resolveLook,
52
52
  themeOf,
@@ -119,7 +119,7 @@ import {
119
119
  createHintController,
120
120
  pageHints,
121
121
  toggleLayer
122
- } from "./chunk-pdf3zy95.js";
122
+ } from "./chunk-bejz4qcg.js";
123
123
 
124
124
  // src/panel/die.ts
125
125
  var ROUND = 16 * Math.sqrt(5), DIE = { half: 64 + ROUND, round: ROUND, pip: 12 }, PIPS = {
@@ -1029,7 +1029,7 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
1029
1029
  saveButton.type = "button", saveButton.tabIndex = -1, saveButton.setAttribute("aria-label", "Save as variant"), saveButton.setAttribute("aria-keyshortcuts", "Shift+S"), saveButton.dataset.hint = "Save", saveButton.onclick = () => run(save);
1030
1030
  if (track.append(pill), root.append(track), saveButton)
1031
1031
  root.append(saveButton);
1032
- let pillEdges = new Edges, pillY = new Spring(0, 28, 0.86), pillAlpha = new Spring(1, 26, 0.9), entries = [], buttons = [], signature = "", lastVariantsRef = null, lastAppliedId = null, activeState = { activeId: "p:default", activeIndex: 0, isCustom: !1 }, prevActiveId = "", prevActiveIndex = -2, prevIsCustom = !1, prevDrifted = !1, initializedPill = !1, slideUntil = 0, display = "auto", chipMin = 0, scope = null, at = () => scope ?? "", source = () => scope === null ? void 0 : scope === "" ? { presets: controls.presets, variants: controls.variants } : controls.scopes[scope], unpinned = (values) => {
1032
+ let pillEdges = new Edges, pillY = new Spring(0, 28, 0.86), pillAlpha = new Spring(1, 26, 0.9), entries = [], buttons = [], signature = "", lastVariantsRef = null, lastAppliedId = null, activeState = { activeId: "p:default", activeIndex: 0, isCustom: !1 }, prevActiveId = "", prevActiveIndex = -2, prevIsCustom = !1, prevDrifted = !1, initializedPill = !1, slideUntil = 0, display = "auto", digits = !1, chipMin = 0, scope = null, at = () => scope ?? "", source = () => scope === null ? void 0 : scope === "" ? { presets: controls.presets, variants: controls.variants } : controls.scopes[scope], unpinned = (values) => {
1033
1033
  let out;
1034
1034
  for (let k of Object.keys(values))
1035
1035
  if (controls.scopeOf(k) !== at() && !same(values[k], controls.locked[k]))
@@ -1142,10 +1142,15 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
1142
1142
  return;
1143
1143
  }
1144
1144
  lastVariantsRef = variantsRef;
1145
- let nextEntries = buildEntries(), nextSig = JSON.stringify([scope, nextEntries.map((e) => [e.id, e.label])]);
1145
+ let nextEntries = buildEntries(), nextSig = JSON.stringify([scope, digits, nextEntries.map((e) => [e.id, e.label])]);
1146
1146
  if (entries = nextEntries, syncShown(), nextSig !== signature)
1147
1147
  signature = nextSig, entries = nextEntries, buttons = entries.map((entry, idx) => {
1148
- let b = el("button", "knobs-preset-chip", entry.label);
1148
+ let b = el("button", "knobs-preset-chip");
1149
+ if (digits && idx < 9) {
1150
+ let key = el("span", "knobs-preset-key", String(idx + 1));
1151
+ key.setAttribute("aria-hidden", "true"), b.append(key, el("span", "knobs-preset-label", entry.label)), b.setAttribute("aria-keyshortcuts", String(idx + 1));
1152
+ } else
1153
+ b.textContent = entry.label;
1149
1154
  return b.type = "button", b.tabIndex = -1, b.dataset.presetId = entry.id, b.dataset.presetKind = entry.kind, b.dataset.presetName = entry.label, b.setAttribute("role", "radio"), b.setAttribute("aria-label", entry.label), b.onclick = (e) => {
1150
1155
  if (e.preventDefault(), focusQuietly(), idx !== activeState.activeIndex || activeState.drifted)
1151
1156
  selectAt(idx);
@@ -1339,6 +1344,13 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
1339
1344
  if (display = mode, placeDisplay())
1340
1345
  measurePill(!0);
1341
1346
  },
1347
+ digits(on) {
1348
+ if (digits === on)
1349
+ return;
1350
+ if (digits = on, root.toggleAttribute("data-digits", on), signature === "")
1351
+ return;
1352
+ signature = "", update();
1353
+ },
1342
1354
  save: () => {
1343
1355
  let message = "";
1344
1356
  return run(() => message = save()), message;
@@ -4022,6 +4034,111 @@ class GpuOverlayManager {
4022
4034
  let more = o.more;
4023
4035
  d[b + 40] = more ? more[0] : 0, d[b + 41] = more ? more[1] : 0, d[b + 42] = more ? more[2] : 0, d[b + 43] = more ? more[3] : 0;
4024
4036
  }
4037
+ drawPresetStrip(ps, tokens, clip, hinting, reducedMotion, dt, tray) {
4038
+ let moving = !1, psHovered = this.curPanelHovered && ps.element.matches(":hover");
4039
+ this.curSubHovered = psHovered;
4040
+ let psRect = this.rectOf(ps.element), trackRect = this.rectOf(ps.track);
4041
+ if (tray && !tokens.boxed)
4042
+ this.pushPrim({
4043
+ x: psRect.left,
4044
+ y: psRect.top,
4045
+ w: psRect.width,
4046
+ h: psRect.height,
4047
+ color: tokens.segBg,
4048
+ color2: tokens.accent,
4049
+ radius: tokens.radius,
4050
+ glowOrBlur: tokens.glowStrength * 8,
4051
+ kind: 1,
4052
+ p0: psHovered || this.isFocused(ps.element) ? 0.4 : 0
4053
+ });
4054
+ let stripClip = intersectClip(clip, [psRect.left, psRect.top, psRect.right, psRect.bottom]), activeState = ps.getActiveState(), chipButtons = ps.getButtons();
4055
+ if (tokens.boxed)
4056
+ for (let btn of chipButtons) {
4057
+ let br = this.rectOf(btn);
4058
+ if (br.right < stripClip[0] || br.left > stripClip[2])
4059
+ continue;
4060
+ this.drawButtonBox(btn, br, this.isHovered(btn), this.missed(btn, hinting), stripClip);
4061
+ }
4062
+ let activeBtn = activeState.activeIndex >= 0 ? chipButtons[activeState.activeIndex] : void 0, pe = ps.pillEdges, activeBtnRect = activeBtn ? this.rectOf(activeBtn) : void 0, panned = livePill(ps.track);
4063
+ if (activeBtn && activeBtnRect && !activeState.isCustom) {
4064
+ let left = activeBtnRect.left - trackRect.left, right = left + Math.max(20, activeBtnRect.width), top = activeBtnRect.top - trackRect.top;
4065
+ if (this.carry(pe, activeBtn, left, right, ps.pillY, top), panned)
4066
+ pe.snap(panned.left, panned.right), ps.pillY.snap(panned.top);
4067
+ else if (reducedMotion || pe.end.x - pe.start.x <= 1)
4068
+ pe.snap(left, right), ps.pillY.snap(top);
4069
+ else
4070
+ pe.to(left, right, 0, trailScale(tokens.blurStrength)), ps.pillY.set(top);
4071
+ ps.pillAlpha.set(activeState.drifted ? DRIFTED_PILL : 1);
4072
+ } else
4073
+ ps.pillAlpha.set(0);
4074
+ for (let sp of [pe.start, pe.end, ps.pillY, ps.pillAlpha])
4075
+ if (reducedMotion)
4076
+ sp.snap(sp.target);
4077
+ else if (sp.step(dt), !sp.resting)
4078
+ moving = !0;
4079
+ let pillAlpha = clamp(ps.pillAlpha.x, 0, 1);
4080
+ if (pillAlpha > 0.01)
4081
+ this.pushPrim({
4082
+ x: trackRect.left + pe.start.x,
4083
+ y: trackRect.top + ps.pillY.x,
4084
+ w: Math.max(18, pe.end.x - pe.start.x),
4085
+ h: Math.max(16, activeBtnRect?.height ?? trackRect.height),
4086
+ clip: stripClip,
4087
+ color: withAlpha(tokens.pillBg, pillAlpha),
4088
+ color2: withAlpha(tokens.pillBorder, pillAlpha),
4089
+ strokeWidth: this.hairline(tokens),
4090
+ aux2: withAlpha(tokens.rim, tokens.rim[3] * pillAlpha),
4091
+ radius: tokens.buttonRadius ?? tokens.radiusMd,
4092
+ glowOrBlur: tokens.glowStrength,
4093
+ kind: 3,
4094
+ blurX: reducedMotion ? 0 : clamp(pe.velocity * 0.006 * tokens.motionBlur, -8, 8)
4095
+ });
4096
+ for (let i = 0;i < chipButtons.length; i++) {
4097
+ let btn = chipButtons[i], br = this.rectOf(btn);
4098
+ if (br.right < stripClip[0] || br.left > stripClip[2])
4099
+ continue;
4100
+ let isActive = i === activeState.activeIndex, chipHovered = this.isHovered(btn);
4101
+ if (chipHovered)
4102
+ this.curSubHovered = !1;
4103
+ let mark = this.ease(btn, "dot", btn.hasAttribute("data-drifted") ? 1 : 0);
4104
+ if (mark > 0.01) {
4105
+ let size = 4 * (0.4 + 0.6 * mark);
4106
+ this.pushPrim({
4107
+ x: br.right - 6 - size / 2,
4108
+ y: br.top + 6 - size / 2,
4109
+ w: size,
4110
+ h: size,
4111
+ clip: stripClip,
4112
+ color: withAlpha(tokens.modified, 0.95 * mark),
4113
+ glowOrBlur: 0.5,
4114
+ kind: 7
4115
+ });
4116
+ }
4117
+ if (btn.hasAttribute("data-naming"))
4118
+ continue;
4119
+ let key = btn.querySelector(".knobs-preset-key"), name = key && btn.querySelector(".knobs-preset-label");
4120
+ if (key && name) {
4121
+ let kr = this.rectOf(key);
4122
+ this.drawText(key.textContent ?? "", kr.left, br.top + br.height * 0.5, withAlpha(tokens.dim, this.missed(btn, hinting)), { px: tokens.textMeta, weight: tokens.labelWeight, clip: stripClip });
4123
+ }
4124
+ let nr = name ? this.rectOf(name) : void 0;
4125
+ this.drawText(name ? name.textContent ?? "" : btn.textContent ?? "", nr ? nr.left : br.left + br.width * 0.5, br.top + br.height * 0.5, withAlpha(this.hot(btn, isActive || chipHovered, tokens.buttonColor ?? tokens.label), this.missed(btn, hinting)), {
4126
+ px: tokens.buttonFontSize ?? tokens.textControl,
4127
+ weight: isActive ? tokens.titleWeight : tokens.labelWeight,
4128
+ align: nr ? "left" : "center",
4129
+ clip: stripClip
4130
+ });
4131
+ }
4132
+ if (ps.saveButton && !ps.saveButton.hidden) {
4133
+ let sr = this.rectOf(ps.saveButton), hot = this.isHovered(ps.saveButton);
4134
+ this.drawText("+", sr.left + sr.width * 0.5, sr.top + sr.height * 0.5, this.hot(ps.saveButton, hot, tokens.dim), {
4135
+ px: tokens.textTitle,
4136
+ weight: 400,
4137
+ align: "center"
4138
+ });
4139
+ }
4140
+ return moving;
4141
+ }
4025
4142
  hairline(tokens) {
4026
4143
  return Math.max(1, Math.round(tokens.rimWidth * this.curScale)) / this.curScale;
4027
4144
  }
@@ -5768,7 +5885,6 @@ class GpuOverlayManager {
5768
5885
  this.placeBackdrop(panel);
5769
5886
  continue;
5770
5887
  }
5771
- let pillFill = tokens.pillBg;
5772
5888
  if (pageB)
5773
5889
  this.placeBackdrop(panel);
5774
5890
  let covers = drawn.some((r) => r.left < card.right && r.right > card.left && r.top < card.bottom && r.bottom > card.top);
@@ -5900,111 +6016,8 @@ class GpuOverlayManager {
5900
6016
  }
5901
6017
  }
5902
6018
  let ps = panel.presetStrip, hinting = panel.root.classList.contains("knobs-hinting"), stripAlpha = this.curAlpha;
5903
- if (!ps.element.hidden) {
5904
- this.curAlpha = stripAlpha * this.presence(ps.element);
5905
- let psHovered = this.curPanelHovered && ps.element.matches(":hover");
5906
- this.curSubHovered = psHovered;
5907
- let psRect = this.rectOf(ps.element), trackRect = this.rectOf(ps.track);
5908
- if (!tokens.boxed)
5909
- this.pushPrim({
5910
- x: psRect.left,
5911
- y: psRect.top,
5912
- w: psRect.width,
5913
- h: psRect.height,
5914
- color: tokens.segBg,
5915
- color2: tokens.accent,
5916
- radius: tokens.radius,
5917
- glowOrBlur: tokens.glowStrength * 8,
5918
- kind: 1,
5919
- p0: psHovered || this.isFocused(ps.element) ? 0.4 : 0
5920
- });
5921
- let stripClip = intersectClip(bodyClip, [
5922
- psRect.left,
5923
- psRect.top,
5924
- psRect.right,
5925
- psRect.bottom
5926
- ]), activeState = ps.getActiveState(), chipButtons = ps.getButtons();
5927
- if (tokens.boxed)
5928
- for (let btn of chipButtons) {
5929
- let br = this.rectOf(btn);
5930
- if (br.right < stripClip[0] || br.left > stripClip[2])
5931
- continue;
5932
- this.drawButtonBox(btn, br, this.isHovered(btn), this.missed(btn, hinting), stripClip);
5933
- }
5934
- let activeBtn = activeState.activeIndex >= 0 ? chipButtons[activeState.activeIndex] : void 0, pe = ps.pillEdges, activeBtnRect = activeBtn ? this.rectOf(activeBtn) : void 0, panned = livePill(ps.track);
5935
- if (activeBtn && activeBtnRect && !activeState.isCustom) {
5936
- let left = activeBtnRect.left - trackRect.left, right = left + Math.max(20, activeBtnRect.width), top = activeBtnRect.top - trackRect.top;
5937
- if (this.carry(pe, activeBtn, left, right, ps.pillY, top), panned)
5938
- pe.snap(panned.left, panned.right), ps.pillY.snap(panned.top);
5939
- else if (reducedMotion || pe.end.x - pe.start.x <= 1)
5940
- pe.snap(left, right), ps.pillY.snap(top);
5941
- else
5942
- pe.to(left, right, 0, trailScale(tokens.blurStrength)), ps.pillY.set(top);
5943
- ps.pillAlpha.set(activeState.drifted ? DRIFTED_PILL : 1);
5944
- } else
5945
- ps.pillAlpha.set(0);
5946
- for (let sp of [pe.start, pe.end, ps.pillY, ps.pillAlpha])
5947
- if (reducedMotion)
5948
- sp.snap(sp.target);
5949
- else if (sp.step(dt), !sp.resting)
5950
- anyMoving = !0;
5951
- let pillAlpha = clamp(ps.pillAlpha.x, 0, 1);
5952
- if (pillAlpha > 0.01)
5953
- this.pushPrim({
5954
- x: trackRect.left + pe.start.x,
5955
- y: trackRect.top + ps.pillY.x,
5956
- w: Math.max(18, pe.end.x - pe.start.x),
5957
- h: Math.max(16, activeBtnRect?.height ?? trackRect.height),
5958
- clip: stripClip,
5959
- color: withAlpha(pillFill, pillAlpha),
5960
- color2: withAlpha(tokens.pillBorder, pillAlpha),
5961
- strokeWidth: this.hairline(tokens),
5962
- aux2: withAlpha(tokens.rim, tokens.rim[3] * pillAlpha),
5963
- radius: tokens.buttonRadius ?? tokens.radiusMd,
5964
- glowOrBlur: tokens.glowStrength,
5965
- kind: 3,
5966
- blurX: reducedMotion ? 0 : clamp(pe.velocity * 0.006 * tokens.motionBlur, -8, 8)
5967
- });
5968
- for (let i = 0;i < chipButtons.length; i++) {
5969
- let btn = chipButtons[i], br = this.rectOf(btn);
5970
- if (br.right < stripClip[0] || br.left > stripClip[2])
5971
- continue;
5972
- let isActive = i === activeState.activeIndex, chipHovered = this.isHovered(btn);
5973
- if (chipHovered)
5974
- this.curSubHovered = !1;
5975
- let mark = this.ease(btn, "dot", btn.hasAttribute("data-drifted") ? 1 : 0);
5976
- if (mark > 0.01) {
5977
- let size = 4 * (0.4 + 0.6 * mark);
5978
- this.pushPrim({
5979
- x: br.right - 6 - size / 2,
5980
- y: br.top + 6 - size / 2,
5981
- w: size,
5982
- h: size,
5983
- clip: stripClip,
5984
- color: withAlpha(tokens.modified, 0.95 * mark),
5985
- glowOrBlur: 0.5,
5986
- kind: 7
5987
- });
5988
- }
5989
- if (btn.hasAttribute("data-naming"))
5990
- continue;
5991
- this.drawText(btn.textContent ?? "", br.left + br.width * 0.5, br.top + br.height * 0.5, withAlpha(this.hot(btn, isActive || chipHovered, tokens.buttonColor ?? tokens.label), this.missed(btn, hinting)), {
5992
- px: tokens.buttonFontSize ?? tokens.textControl,
5993
- weight: isActive ? tokens.titleWeight : tokens.labelWeight,
5994
- align: "center",
5995
- clip: stripClip
5996
- });
5997
- }
5998
- if (ps.saveButton && !ps.saveButton.hidden) {
5999
- let sr = this.rectOf(ps.saveButton), hot = this.isHovered(ps.saveButton);
6000
- this.drawText("+", sr.left + sr.width * 0.5, sr.top + sr.height * 0.5, this.hot(ps.saveButton, hot, tokens.dim), {
6001
- px: tokens.textTitle,
6002
- weight: 400,
6003
- align: "center"
6004
- });
6005
- }
6006
- this.curAlpha = stripAlpha;
6007
- }
6019
+ if (!ps.element.hidden)
6020
+ this.curAlpha = stripAlpha * this.presence(ps.element), anyMoving = this.drawPresetStrip(ps, tokens, bodyClip, hinting, reducedMotion, dt, !0) || anyMoving, this.curAlpha = stripAlpha;
6008
6021
  let tabs = panel.tabs;
6009
6022
  if (tabs && !tabs.element.hidden)
6010
6023
  anyMoving = this.drawTabStrip(tabs, hinting, reducedMotion, dt, {
@@ -6225,6 +6238,13 @@ class GpuOverlayManager {
6225
6238
  this.drawScope(card, cr, tokens.radius, 1);
6226
6239
  }
6227
6240
  }
6241
+ for (let gs of panel.groupStrips?.() ?? []) {
6242
+ let card = gs.element.parentElement;
6243
+ if (!card || gs.element.closest("[hidden]"))
6244
+ continue;
6245
+ let cr = cardRects.get(card) ?? this.rectOf(card);
6246
+ this.curClip = clipToTile(card, intersectClip(bodyClip, [cr.left, cr.top, cr.right, cr.bottom])), this.curAlpha = panelAlpha * this.presence(card) * fadeOf(card), anyMoving = this.drawPresetStrip(gs, tokens, this.curClip, hinting, reducedMotion, dt, !1) || anyMoving;
6247
+ }
6228
6248
  this.curClip = bodyClip, this.curAlpha = panelAlpha;
6229
6249
  let washed = /* @__PURE__ */ new Set, rowsAlpha = this.curAlpha;
6230
6250
  for (let row of panel.rows) {
@@ -11311,7 +11331,7 @@ function presence(el, o = {}) {
11311
11331
  return !1;
11312
11332
  }), tune = (seconds, zeta) => {
11313
11333
  let reduced = reducedMotion(win);
11314
- spring.omega = 2 * Math.PI / (reduced ? FADE.duration : seconds), spring.zeta = reduced ? 1 : zeta;
11334
+ spring.omega = 2 * Math.PI / (reduced ? Math.min(FADE.duration, seconds) : seconds), spring.zeta = reduced ? 1 : zeta;
11315
11335
  };
11316
11336
  return render(), {
11317
11337
  show() {
@@ -15156,7 +15176,7 @@ function buildPanel(controls, host, options) {
15156
15176
  if (helpButton?.hidden)
15157
15177
  titleEl.title = `${titleEl.textContent ? `${titleEl.textContent}
15158
15178
  ` : ""}Press ${show("help")} for keyboard shortcuts`;
15159
- let presetLabel = (id, scope = "") => options.presetLabels?.[id] ?? (id === "default" ? options.defaultLabel : void 0) ?? controls.presetLabel(id, scope), presetStrip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel, (entries) => presetsTouchTab(entries), () => stripScope()), tabStrip = createTabStrip(doc, (id, animate) => selectTab(id, animate)), activeTab, searchOptions = typeof options.search === "object" ? options.search : {}, chipTests = /* @__PURE__ */ new Map, search = options.search && !page ? createSearch(doc, {
15179
+ let presetLabel = (id, scope = "") => options.presetLabels?.[id] ?? (id === "default" ? typeof options.defaultLabel === "object" ? options.defaultLabel[scope] : options.defaultLabel : void 0) ?? controls.presetLabel(id, scope), hintReach = (typeof options.hints === "object" ? options.hints.reach : options.hints) ?? "page", stripDigitsShown = typeof options.hints === "object" && options.hints.digits === "always", presetStrip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel, (entries) => presetsTouchTab(entries), () => stripScope()), tabStrip = createTabStrip(doc, (id, animate) => selectTab(id, animate)), activeTab, searchOptions = typeof options.search === "object" ? options.search : {}, chipTests = /* @__PURE__ */ new Map, search = options.search && !page ? createSearch(doc, {
15160
15180
  placeholder: searchOptions.placeholder ?? "Search",
15161
15181
  chips: (searchOptions.chips ?? ["modified"]).map((chip) => {
15162
15182
  if (chip === "modified")
@@ -15187,8 +15207,19 @@ function buildPanel(controls, host, options) {
15187
15207
  else if ((!header.hidden && header.dataset.leaving !== "true") !== any)
15188
15208
  reveal(header, any, () => wakeGpu());
15189
15209
  };
15190
- presetStrip.display(page ? "chips" : options.presetDisplay ?? "auto");
15191
- let allActions = [
15210
+ presetStrip.display(page ? "chips" : options.presetDisplay ?? "auto"), presetStrip.digits(stripDigitsShown);
15211
+ let groupStrips = /* @__PURE__ */ new Map, tabNamed = (id) => (options.tabs ?? []).some((t) => (typeof t === "string" ? t : t.id) === id) || Object.values(controls.schema).some((f) => f.tab === id), groupStripFor = (group) => {
15212
+ if (page || !Object.hasOwn(controls.scopes, group) || tabNamed(group))
15213
+ return;
15214
+ let strip = groupStrips.get(group);
15215
+ if (!strip)
15216
+ strip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel, void 0, () => stripScope() === group ? null : group), strip.element.setAttribute("aria-label", `${groupTitle(group)} presets`), strip.display(options.presetDisplay ?? "auto"), strip.digits(stripDigitsShown), groupStrips.set(group, strip);
15217
+ return strip;
15218
+ }, updateStrips = () => {
15219
+ presetStrip.update();
15220
+ for (let strip of groupStrips.values())
15221
+ strip.update();
15222
+ }, allActions = [
15192
15223
  ...controls.actions ?? [],
15193
15224
  ...options.actions ?? []
15194
15225
  ], body = el("div"), tabMotion = tabTransition(body, root, () => wakeGpu()), pinnedBody = el("div");
@@ -15480,7 +15511,7 @@ function buildPanel(controls, host, options) {
15480
15511
  }
15481
15512
  let specs = [], placed = /* @__PURE__ */ new Set;
15482
15513
  for (let [key, field] of entries) {
15483
- if (field.kind === "choice" && field.variants && !controls.isPinned(key))
15514
+ if (field.kind === "choice" && field.variants && controls.inPresets(key))
15484
15515
  continue;
15485
15516
  let pair = field.kind === "knob" && field.spring ? halves.get(field.spring.id) : void 0;
15486
15517
  if (pair?.duration && pair.bounce && field.kind === "knob" && field.spring) {
@@ -16049,7 +16080,7 @@ function buildPanel(controls, host, options) {
16049
16080
  else
16050
16081
  runs.push({ group: r.group, pinned, rows: [r] });
16051
16082
  }
16052
- let free = blocks.filter((b) => !b.leaving), chosen = [], pinnedBlocks = /* @__PURE__ */ new Set, usedSubheads = /* @__PURE__ */ new Set;
16083
+ let free = blocks.filter((b) => !b.leaving), chosen = [], pinnedBlocks = /* @__PURE__ */ new Set, usedSubheads = /* @__PURE__ */ new Set, stripped = /* @__PURE__ */ new Set;
16053
16084
  for (let run of runs) {
16054
16085
  let at = free.findIndex((b) => b.group === run.group), block = at >= 0 ? free.splice(at, 1)[0] : newBlock(run.group);
16055
16086
  if (at < 0) {
@@ -16076,7 +16107,10 @@ function buildPanel(controls, host, options) {
16076
16107
  }
16077
16108
  under = name, nodes.push(r.row);
16078
16109
  }
16079
- if (arrange(block.card, nodes), animate && at >= 0) {
16110
+ let strip = run.group !== void 0 && !run.pinned && !stripped.has(run.group) ? groupStripFor(run.group) : void 0;
16111
+ if (strip)
16112
+ stripped.add(run.group);
16113
+ if (arrange(block.card, strip ? [strip.element, ...nodes] : nodes), animate && at >= 0) {
16080
16114
  for (let r of run.rows)
16081
16115
  if (entering.has(r))
16082
16116
  motion.push({ node: r.row, show: !0 });
@@ -16212,7 +16246,7 @@ function buildPanel(controls, host, options) {
16212
16246
  let keys = rows.flatMap((r) => {
16213
16247
  let tab = tabOf(r);
16214
16248
  return !pinnedRow(r) && (tab === void 0 || tab === activeTab) ? r.keys.filter((k) => controls.scopeOf(k) === "") : [];
16215
- }), v = variantsOf(controls.schema, controls.isPinned);
16249
+ }), v = variantsOf(controls.schema, (key) => !controls.inPresets(key));
16216
16250
  if (v) {
16217
16251
  let tab = v.field.tab ?? (v.field.group !== void 0 ? groupTab(v.field.group) : void 0);
16218
16252
  if (tab === void 0 || tab === activeTab)
@@ -16247,7 +16281,7 @@ function buildPanel(controls, host, options) {
16247
16281
  writeSession(controls.id, { tab: id });
16248
16282
  tabStrip.select(id, animate);
16249
16283
  let focused = doc.activeElement;
16250
- if (presetStrip.update(), applyVisibility(animate ? "tab" : "instant"), focused && root.contains(focused) && focused.closest("[hidden],[data-leaving]"))
16284
+ if (updateStrips(), applyVisibility(animate ? "tab" : "instant"), focused && root.contains(focused) && focused.closest("[hidden],[data-leaving]"))
16251
16285
  tabStrip.focus({ preventScroll: !0, focusVisible: !1 });
16252
16286
  options.onTab?.(id), wakeGpu();
16253
16287
  }
@@ -16575,7 +16609,7 @@ function buildPanel(controls, host, options) {
16575
16609
  changedCount++;
16576
16610
  }
16577
16611
  let stagger = initialized ? cascade(changedCount) : 0, moving = initialized ? rows.filter(changed) : rows;
16578
- if (presetStrip.update(), batchMeasures(() => {
16612
+ if (updateStrips(), batchMeasures(() => {
16579
16613
  for (let row of moving)
16580
16614
  if (row.slider?.prime(), row.editor?.prime?.(), row.choice)
16581
16615
  primeSegPill(row.choice.seg, row.choice.buttons);
@@ -16707,13 +16741,14 @@ function buildPanel(controls, host, options) {
16707
16741
  if (s.label?.hasAttribute("data-foldable"))
16708
16742
  add(s.label, sectionTitle(s.name), () => s.label.focus(), s.label.firstElementChild);
16709
16743
  }
16710
- presetStrip.getEntries().forEach((entry, i) => {
16711
- let chip = presetStrip.getButtons()[i];
16712
- if (chip)
16713
- add(chip, entry.label, () => {
16714
- presetStrip.selectById(entry.id), presetStrip.element.focus({ preventScroll: !0 });
16715
- });
16716
- });
16744
+ for (let strip of [presetStrip, ...groupStrips.values()])
16745
+ strip.getEntries().forEach((entry, i) => {
16746
+ let chip = strip.getButtons()[i];
16747
+ if (chip)
16748
+ add(chip, entry.label, () => {
16749
+ strip.selectById(entry.id), strip.element.focus({ preventScroll: !0 });
16750
+ });
16751
+ });
16717
16752
  for (let b of root.querySelectorAll(page ? ".knobs-head-cmd, .knobs-group-head button:not(.knobs-group-die,.knobs-group-reset,.knobs-chips button), .knobs-custom-actions button, .knobs-preset-save" : ".knobs-head-cmd, .knobs-group-head button, .knobs-custom-actions button, .knobs-preset-save, .knobs-popover button"))
16718
16753
  add(b, b.dataset.hint ?? b.getAttribute("aria-label") ?? b.textContent ?? "", () => b.focus());
16719
16754
  let focusedHead = page ? doc.activeElement?.closest(".knobs-tile")?.querySelector(":scope > .knobs-group-head") : null, presetUi = focusedHead ? groupChips.get(focusedHead.dataset.group ?? "") : void 0;
@@ -16729,7 +16764,7 @@ function buildPanel(controls, host, options) {
16729
16764
  add(stop, label, () => stop.focus());
16730
16765
  }
16731
16766
  return targets;
16732
- }, unregisterHints = (options.hints ?? "page") === "page" && bound("hint") ? pageHints(doc).register(root, () => collectHintTargets().map((t) => ({
16767
+ }, unregisterHints = hintReach === "page" && bound("hint") ? pageHints(doc).register(root, () => collectHintTargets().map((t) => ({
16733
16768
  el: t.el,
16734
16769
  label: t.display,
16735
16770
  ...t.labelEl ? { labelEl: t.labelEl } : {},
@@ -16786,6 +16821,9 @@ function buildPanel(controls, host, options) {
16786
16821
  runs.push({ at: b.head, stops: shown([b.head]) });
16787
16822
  for (let sh of subheads.values())
16788
16823
  runs.push({ at: sh.el, stops: shown([sh.el]) });
16824
+ for (let gs of groupStrips.values())
16825
+ if (visible(gs.element))
16826
+ runs.push({ at: gs.element, stops: shown([gs.element]) });
16789
16827
  runs.push(...strips), runs.sort((a, b) => a.at.compareDocumentPosition(b.at) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
16790
16828
  for (let run of runs)
16791
16829
  list.push(...run.stops);
@@ -17164,6 +17202,9 @@ function buildPanel(controls, host, options) {
17164
17202
  return { el: sub, level: "row" };
17165
17203
  return { el: r.row.closest(".knobs-card > *") ?? r.row, level: "row" };
17166
17204
  }
17205
+ let strip = active.closest(".knobs-card > .knobs-presets");
17206
+ if (strip)
17207
+ return { el: strip, level: "row" };
17167
17208
  let card = cardOf(active);
17168
17209
  return card ? { el: card, level: "card" } : null;
17169
17210
  }, partName = (part, active) => part.querySelector(".knobs-strip-label")?.textContent || active?.getAttribute("aria-label") || part.getAttribute("aria-label") || "Editor", trailFor = (t, active) => {
@@ -17299,6 +17340,18 @@ function buildPanel(controls, host, options) {
17299
17340
  if (!r || r.row.closest("[data-disabled],[data-readonly]"))
17300
17341
  return;
17301
17342
  return optionsOf(r);
17343
+ }, groupStripAt = (node) => node ? [...groupStrips.values()].find((s) => s.element.contains(node)) : void 0, groupStripOptions = /* @__PURE__ */ new Map, optionsOfStrip = (strip) => {
17344
+ let o = groupStripOptions.get(strip);
17345
+ if (!o)
17346
+ o = {
17347
+ get count() {
17348
+ return strip.getEntries().length;
17349
+ },
17350
+ choose: (i) => strip.select(i),
17351
+ shown: () => strip.getButtons(),
17352
+ anchor: strip.element
17353
+ }, groupStripOptions.set(strip, o);
17354
+ return o;
17302
17355
  }, stripDigits = () => visible(presetStrip.element) && presetStrip.getEntries().length > 1, stripOptions = {
17303
17356
  get count() {
17304
17357
  return presetStrip.getEntries().length;
@@ -17307,7 +17360,12 @@ function buildPanel(controls, host, options) {
17307
17360
  shown: () => presetStrip.getButtons(),
17308
17361
  anchor: presetStrip.element
17309
17362
  }, stripOptionsAt = (node) => {
17310
- if (hintCtrl.isActive || !node || !stripDigits())
17363
+ if (hintCtrl.isActive || !node || stripDigitsShown)
17364
+ return;
17365
+ let own = groupStripAt(node);
17366
+ if (own)
17367
+ return visible(own.element) && own.getEntries().length > 1 && !own.nameField() ? optionsOfStrip(own) : void 0;
17368
+ if (!stripDigits())
17311
17369
  return;
17312
17370
  return (presetStrip.element.contains(node) || focusLevel(node) === "panel") && !presetStrip.nameField() ? stripOptions : void 0;
17313
17371
  }, pickOption = (event) => {
@@ -17486,9 +17544,9 @@ function buildPanel(controls, host, options) {
17486
17544
  return;
17487
17545
  }
17488
17546
  if (pressed("presets", event)) {
17489
- let direction = key === keys.presets[0] ? -1 : 1;
17490
- if (visible(presetStrip.element) && presetStrip.getEntries().length > 1)
17491
- presetStrip.cycle(direction);
17547
+ let direction = key === keys.presets[0] ? -1 : 1, strip = groupStripAt(target) ?? presetStrip;
17548
+ if (visible(strip.element) && strip.getEntries().length > 1)
17549
+ strip.cycle(direction);
17492
17550
  else if (!cycleTab(direction))
17493
17551
  return;
17494
17552
  event.preventDefault(), event.stopPropagation();
@@ -17514,6 +17572,8 @@ function buildPanel(controls, host, options) {
17514
17572
  event.preventDefault(), searchReturn = target, search.focus();
17515
17573
  return;
17516
17574
  }
17575
+ if (/^[1-9]$/.test(key) && groupStripAt(target))
17576
+ return;
17517
17577
  if (/^[1-9]$/.test(key)) {
17518
17578
  let group = page ? focusedGroup(target) : void 0;
17519
17579
  if (Boolean(group && groupChips.has(group))) {
@@ -17562,7 +17622,7 @@ function buildPanel(controls, host, options) {
17562
17622
  return;
17563
17623
  }
17564
17624
  if (pressed("save", event) && showVariants) {
17565
- event.preventDefault(), presetStrip.save();
17625
+ event.preventDefault(), (groupStripAt(target) ?? presetStrip).save();
17566
17626
  return;
17567
17627
  }
17568
17628
  let index = rowAt(target);
@@ -17935,6 +17995,7 @@ function buildPanel(controls, host, options) {
17935
17995
  ...undock ? {} : { headerDie: { button: randomButton, die } },
17936
17996
  locks: lockIcons,
17937
17997
  presetStrip,
17998
+ groupStrips: () => groupStrips.values(),
17938
17999
  tabs: tabStrip,
17939
18000
  groups: gpuGroups,
17940
18001
  cards,
@@ -18041,7 +18102,10 @@ function buildPanel(controls, host, options) {
18041
18102
  dropGpu(r);
18042
18103
  for (let c of groupChips.values())
18043
18104
  c.dispose();
18044
- presetStrip.dispose(), tabStrip.dispose(), tabMotion.finish(), doc.removeEventListener("keydown", onGlobalKey), layout?.dispose(), tileSizes.disconnect(), headSize.disconnect(), win?.removeEventListener("scroll", onScroll), win?.removeEventListener("scrollend", onScrollEnd);
18105
+ presetStrip.dispose();
18106
+ for (let strip of groupStrips.values())
18107
+ strip.dispose();
18108
+ tabStrip.dispose(), tabMotion.finish(), doc.removeEventListener("keydown", onGlobalKey), layout?.dispose(), tileSizes.disconnect(), headSize.disconnect(), win?.removeEventListener("scroll", onScroll), win?.removeEventListener("scrollend", onScrollEnd);
18045
18109
  for (let type of ["wheel", "touchstart", "keydown"])
18046
18110
  doc.removeEventListener(type, touched, !0);
18047
18111
  doc.removeEventListener("pointerdown", onPointerDown, !0);
@@ -496,6 +496,7 @@ var META_KEYS = [
496
496
  "sectionFolded",
497
497
  "tab",
498
498
  "pinned",
499
+ "inPresets",
499
500
  "subsection"
500
501
  ];
501
502
  function pickMeta(source, skip = []) {
@@ -529,6 +530,13 @@ function pinnedKeys(schema, groups = []) {
529
530
  out.add(key);
530
531
  return out;
531
532
  }
533
+ function presetFreeKeys(schema, pinned, groups = {}) {
534
+ let out = new Set(pinned);
535
+ for (let [key, f] of Object.entries(schema))
536
+ if (f.inPresets === !1 || f.group !== void 0 && groups[f.group] === !1)
537
+ out.add(key);
538
+ return out;
539
+ }
532
540
  var SPRING2 = {
533
541
  duration: { min: 0.05, max: 2, step: 0.01 },
534
542
  bounce: { min: -0.5, max: 1, step: 0.01 }
@@ -1177,4 +1185,4 @@ function urlFlag2(name) {
1177
1185
  }
1178
1186
  var storageKey = (id, part) => `knobs:${encodeURIComponent(id)}:${part}:v1`;
1179
1187
 
1180
- export { maxChroma, oklchToRgb, parseColor2, fitColor, formatColor, colorHex2, EASINGS2, EASING_Y, easingName, bezierPoint2, bezierCss2, normalizeCombo2, letterOf2, comboFromEvent2, heldMods, matchesCombo2, comboParts2, comboLabel2, isTypingTarget, GRADIENT_SAMPLES, sampleGradient2, gradientSamples, gradientCss2, stopColorAt, sampleCurve2, curveSamples, CURVE_SHAPES2, inRange, sameRamp, randomStops, mutateStops, randomPoints, mutatePoints, pickMeta, isReadout2, hasValue2, variantsOf, pinnedKeys, SPRING2, valueKeys, knob2, choice2, action2, flatSpring2, toggle2, spring2, color2, point2, easing2, vec22, vec32, range2, angle2, seed2, keybind2, text2, gradient2, curve2, toolbarShortcut2, actionShortcut2, toolbar2, watch2, sparkline2, graph2, meter2, table2, log2, wrapAngle2, normalizeField, normalize, isRecord, same, equal, Spring, springMotion2, springResponse, springEasing, springCss2, SPRINGS2, FADE, MOTION, reducedMotion, reducedSpring2, springLinear, DEFAULT_LINEAR, motionCss, RESHAPE, RESET_KEYS, REFRESHES, readoutState, pushReadout, READOUT, memoryStorage2, browserStorage, urlFlag2, storageKey };
1188
+ export { maxChroma, oklchToRgb, parseColor2, fitColor, formatColor, colorHex2, EASINGS2, EASING_Y, easingName, bezierPoint2, bezierCss2, normalizeCombo2, letterOf2, comboFromEvent2, heldMods, matchesCombo2, comboParts2, comboLabel2, isTypingTarget, GRADIENT_SAMPLES, sampleGradient2, gradientSamples, gradientCss2, stopColorAt, sampleCurve2, curveSamples, CURVE_SHAPES2, inRange, sameRamp, randomStops, mutateStops, randomPoints, mutatePoints, pickMeta, isReadout2, hasValue2, variantsOf, pinnedKeys, presetFreeKeys, SPRING2, valueKeys, knob2, choice2, action2, flatSpring2, toggle2, spring2, color2, point2, easing2, vec22, vec32, range2, angle2, seed2, keybind2, text2, gradient2, curve2, toolbarShortcut2, actionShortcut2, toolbar2, watch2, sparkline2, graph2, meter2, table2, log2, wrapAngle2, normalizeField, normalize, isRecord, same, equal, Spring, springMotion2, springResponse, springEasing, springCss2, SPRINGS2, FADE, MOTION, reducedMotion, reducedSpring2, springLinear, DEFAULT_LINEAR, motionCss, RESHAPE, RESET_KEYS, REFRESHES, readoutState, pushReadout, READOUT, memoryStorage2, browserStorage, urlFlag2, storageKey };
package/dist/dev/index.js CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  hasValue2,
28
28
  variantsOf,
29
29
  pinnedKeys,
30
+ presetFreeKeys,
30
31
  SPRING2,
31
32
  valueKeys,
32
33
  knob2,
@@ -76,7 +77,7 @@ import {
76
77
  browserStorage,
77
78
  urlFlag2,
78
79
  storageKey
79
- } from "./chunk-7kns3qe6.js";
80
+ } from "./chunk-v2g91mrm.js";
80
81
  // src/core/random.ts
81
82
  var lattice = (min, max, step, r) => min + Math.floor(r * (Math.floor((max - min) / step + 0.000000001) + 1)) * step, between = (min, max, r) => min + (max - min) * r;
82
83
  function pick(field, random) {
@@ -575,7 +576,7 @@ function createVariants(schema, id, key, storage, get, set, notify, enabled, see
575
576
  }
576
577
 
577
578
  // src/core/store.ts
578
- var devBuild = () => !0;
579
+ var devBuild = () => !0, defaultLabelOf = (label, scope) => typeof label === "object" ? label[scope] : label;
579
580
  function sameField(a, b) {
580
581
  if (a === b)
581
582
  return !0;
@@ -605,7 +606,11 @@ function defineControls(id, input, options = {}) {
605
606
  if (pinnedOf?.schema !== schema)
606
607
  pinnedOf = { schema, keys: pinnedKeys(schema, options.pinned) };
607
608
  return pinnedOf.keys;
608
- }, variantsChoice = () => variantsOf(schema, (key) => pinned().has(key)), scopingOf, scoping = () => {
609
+ }, freeOf, presetFree = () => {
610
+ if (freeOf?.schema !== schema)
611
+ freeOf = { schema, keys: presetFreeKeys(schema, pinned(), options.inPresets) };
612
+ return freeOf.keys;
613
+ }, variantsChoice = () => variantsOf(schema, (key) => presetFree().has(key)), scopingOf, scoping = () => {
609
614
  if (scopingOf?.schema === schema)
610
615
  return scopingOf;
611
616
  let fields = schema, groupTab = /* @__PURE__ */ new Map;
@@ -620,7 +625,7 @@ function defineControls(id, input, options = {}) {
620
625
  named.add(variants);
621
626
  let of = /* @__PURE__ */ new Map;
622
627
  for (let k of valueKeys(schema)) {
623
- if (pinned().has(k))
628
+ if (presetFree().has(k))
624
629
  continue;
625
630
  let f = fields[k], tab = tabOf(f);
626
631
  of.set(k, tab !== void 0 && named.has(tab) ? tab : f.group !== void 0 && named.has(f.group) ? f.group : "");
@@ -1200,9 +1205,10 @@ function defineControls(id, input, options = {}) {
1200
1205
  sectionFolded: options.sectionFolded,
1201
1206
  presetLabel: (presetId, scope = "") => {
1202
1207
  let v = scoping().variants === scope ? variantsChoice() : void 0, option = presetId === "default" ? v?.field.value : presetId;
1203
- 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);
1208
+ return options.presetLabels?.[presetId] ?? (presetId === "default" ? defaultLabelOf(options.defaultLabel, scope) : void 0) ?? (v && option !== void 0 && v.field.options.includes(option) ? v.field.labels?.[option] ?? option : presetId);
1204
1209
  },
1205
1210
  isPinned: (key) => pinned().has(key),
1211
+ inPresets: (key) => !presetFree().has(key),
1206
1212
  get,
1207
1213
  set,
1208
1214
  sync,
@@ -1516,11 +1522,11 @@ function defineControls(id, input, options = {}) {
1516
1522
  code: (format, values) => formatCode(values ?? get(), format),
1517
1523
  variants,
1518
1524
  attach(element, chip) {
1519
- let load = options.ship ? () => options.ship().then((m) => m.attach) : () => import("./chunk-44r02asc.js").then((m) => m.attach);
1525
+ let load = options.ship ? () => options.ship().then((m) => m.attach) : () => import("./chunk-f0e5tkqg.js").then((m) => m.attach);
1520
1526
  return live(load, (attach) => attach(controls, element, chip), (detach) => detach());
1521
1527
  },
1522
1528
  mount(container, mountOptions) {
1523
- let load = options.ship ? () => options.ship().then((m) => m.mountPanel) : () => import("./chunk-mbhy4jg5.js").then((m) => m.mountPanel);
1529
+ let load = options.ship ? () => options.ship().then((m) => m.mountPanel) : () => import("./chunk-s7d5a8vb.js").then((m) => m.mountPanel);
1524
1530
  return live(load, (mountPanel) => {
1525
1531
  let panel = mountPanel(controls, container, mountOptions);
1526
1532
  return mountOptions?.onReady?.(panel), panel;
@@ -1821,6 +1827,7 @@ function createRemoteControls(id, descriptors, options = {}) {
1821
1827
  presets: options.presets,
1822
1828
  scopedPresets: options.scopedPresets,
1823
1829
  pinned: options.pinned,
1830
+ inPresets: options.inPresets,
1824
1831
  groupPresets: options.groupPresets,
1825
1832
  onPreset: options.onPreset,
1826
1833
  actions: options.actions,