@kitlangton/knobs 0.4.0-next.10 → 0.4.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -1
- package/dist/dev/{chunk-a7cjgy8x.js → chunk-75gbzbyd.js} +3 -3
- package/dist/dev/{chunk-5pb7ymrd.js → chunk-bnhzf1tj.js} +91 -42
- package/dist/dev/{chunk-dyq4m24m.js → chunk-j57rhjtm.js} +4 -1
- package/dist/dev/{chunk-3ygqgy1x.js → chunk-mjdgzad5.js} +15 -2
- package/dist/dev/index.js +37 -14
- package/dist/index.js +46 -11
- package/dist/panel.js +96 -27
- package/dist/types/core/remote.d.ts +2 -0
- package/dist/types/core/schema.d.ts +13 -1
- package/dist/types/core/store.d.ts +8 -0
- package/dist/types/panel/gpu/renderer.d.ts +2 -0
- package/dist/types/panel/presets.d.ts +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -765,7 +765,33 @@ panel.tab("emitter") // shows a tab; panel.tab() answers the one showing
|
|
|
765
765
|
none show on every tab. The strip's underline slides to the chosen tab and the rows cross: what
|
|
766
766
|
leaves folds away as what arrives unfolds, a moment behind. `⌥1`–`⌥9` pick a tab, and `[` / `]`
|
|
767
767
|
cycle tabs when there's no preset strip. The tab last shown is remembered for these controls (and
|
|
768
|
-
in the session).
|
|
768
|
+
in the session). The preset strip sits under the tabs and crosses a tab change with the rows; a
|
|
769
|
+
tab that no preset or saved variant moves shows no strip at all (not even its `+`).
|
|
770
|
+
- **Pinned groups.** Groups that apply to every tab (a global speed, a slow-motion switch) sit at
|
|
771
|
+
the top of the panel, under its one header and above the tab strip, and a tab change never moves
|
|
772
|
+
them. Name them on the store, `pinned: ["Global"]`, or say `pinned: true` on a field (the first
|
|
773
|
+
field of a group that says wins; a field without a group pins itself). A pinned field's `tab` is
|
|
774
|
+
ignored. Pinned groups keep their own die and reset. Presets and saved variants leave them out:
|
|
775
|
+
applying one keeps their values, saving one doesn't record them, a preset's chip doesn't drift
|
|
776
|
+
when they change, and their modified marks are against their defaults. Random rolls them freely
|
|
777
|
+
even under an active preset. The header's Random and Reset still cover everything, pinned
|
|
778
|
+
groups included, as they cover every tab; give a field `random: false` to keep it out of
|
|
779
|
+
Random. Keyboard order and search follow the panel top to bottom: pinned rows, the tabs, the
|
|
780
|
+
preset strip, then the tab's rows. `controls.isPinned(key)` says whether a key is pinned.
|
|
781
|
+
|
|
782
|
+
```ts
|
|
783
|
+
const motion = defineControls(
|
|
784
|
+
"motion",
|
|
785
|
+
{
|
|
786
|
+
speed: knob(1, { min: 0.25, max: 4, step: 0.05, unit: "×", group: "Global" }),
|
|
787
|
+
slow: toggle(false, { label: "Slow motion (0.25×)", group: "Global" }),
|
|
788
|
+
sidebars: spring({ duration: 0.3, bounce: 0 }, { group: "Sidebars", tab: "Sidebars" }),
|
|
789
|
+
hover: spring({ duration: 0.14, bounce: 0 }, { group: "Experiments", tab: "Experiments" }),
|
|
790
|
+
},
|
|
791
|
+
{ pinned: ["Global"], defaultLabel: "Shipped", presets: { snappy, smooth, calm } },
|
|
792
|
+
)
|
|
793
|
+
motion.mount(el, { title: "Knobs", tabs: ["Sidebars", "Experiments"] })
|
|
794
|
+
```
|
|
769
795
|
- **Sub-headings.** Fields with the same `subsection` in a group sit under a flat heading inside the
|
|
770
796
|
group's card, split off by a hairline (never a card inside a card). They're gathered: a group's
|
|
771
797
|
rows without a sub-heading come first, then each sub-heading's rows together, in the order the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__preload
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-mjdgzad5.js";
|
|
4
4
|
import {
|
|
5
5
|
applyLook,
|
|
6
6
|
panelLook,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
writeSession,
|
|
10
10
|
injectStyles,
|
|
11
11
|
toggleLayer
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-j57rhjtm.js";
|
|
13
13
|
|
|
14
14
|
// src/panel/attach.ts
|
|
15
15
|
function attach(controls, element, options = {}) {
|
|
@@ -85,7 +85,7 @@ function attach(controls, element, options = {}) {
|
|
|
85
85
|
return;
|
|
86
86
|
loading = !0;
|
|
87
87
|
try {
|
|
88
|
-
let { createPanel } = await (__preload("
|
|
88
|
+
let { createPanel } = await (__preload("bnhzf1tj"), import("./chunk-bnhzf1tj.js"));
|
|
89
89
|
if (disposed || !controls.enabled || panel)
|
|
90
90
|
return;
|
|
91
91
|
if (panel = createPanel(controls, element, close, { restored, chip }), remember)
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
pushReadout,
|
|
45
45
|
READOUT,
|
|
46
46
|
storageKey
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-mjdgzad5.js";
|
|
48
48
|
import {
|
|
49
49
|
resolveLook,
|
|
50
50
|
themeOf,
|
|
@@ -116,7 +116,7 @@ import {
|
|
|
116
116
|
createHintController,
|
|
117
117
|
pageHints,
|
|
118
118
|
toggleLayer
|
|
119
|
-
} from "./chunk-
|
|
119
|
+
} from "./chunk-j57rhjtm.js";
|
|
120
120
|
|
|
121
121
|
// src/panel/die.ts
|
|
122
122
|
var ROUND = 16 * Math.sqrt(5), DIE = { half: 64 + ROUND, round: ROUND, pip: 12 }, PIPS = {
|
|
@@ -994,6 +994,9 @@ function resolveActivePresetEntry(entries, currentValues, lastAppliedId, baseId
|
|
|
994
994
|
isCustom: !0
|
|
995
995
|
};
|
|
996
996
|
}
|
|
997
|
+
function presetsTouch(entries, keys, defaults) {
|
|
998
|
+
return entries.some((e) => !(e.kind === "preset" && e.rawId === "default") && keys.some((k) => !same(e.values[k], defaults[k])));
|
|
999
|
+
}
|
|
997
1000
|
function nextPresetIndex(count, activeIndex, direction) {
|
|
998
1001
|
if (count <= 0)
|
|
999
1002
|
return -1;
|
|
@@ -1001,7 +1004,7 @@ function nextPresetIndex(count, activeIndex, direction) {
|
|
|
1001
1004
|
return direction > 0 ? 0 : count - 1;
|
|
1002
1005
|
return ((activeIndex + direction) % count + count) % count;
|
|
1003
1006
|
}
|
|
1004
|
-
function createPresetStrip(doc, controls, showVariants, run, onAfterChange, presetLabel = (id) => controls.presetLabel(id)) {
|
|
1007
|
+
function createPresetStrip(doc, controls, showVariants, run, onAfterChange, presetLabel = (id) => controls.presetLabel(id), relevant) {
|
|
1005
1008
|
let wake = () => onAfterChange?.(), el = (tag, cls, text) => {
|
|
1006
1009
|
let node = doc.createElement(tag);
|
|
1007
1010
|
if (cls)
|
|
@@ -1018,7 +1021,13 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
1018
1021
|
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);
|
|
1019
1022
|
if (track.append(pill), root.append(track), saveButton)
|
|
1020
1023
|
root.append(saveButton);
|
|
1021
|
-
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
|
|
1024
|
+
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, unpinned = (values) => {
|
|
1025
|
+
let out;
|
|
1026
|
+
for (let k of Object.keys(values))
|
|
1027
|
+
if (controls.isPinned(k) && !same(values[k], controls.locked[k]))
|
|
1028
|
+
out ??= { ...values }, out[k] = controls.locked[k];
|
|
1029
|
+
return out ?? values;
|
|
1030
|
+
};
|
|
1022
1031
|
function buildEntries() {
|
|
1023
1032
|
let presetEntries = Object.entries(controls.presets).map(([id, presetValues]) => ({
|
|
1024
1033
|
id: `p:${id}`,
|
|
@@ -1035,7 +1044,7 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
1035
1044
|
kind: "variant",
|
|
1036
1045
|
rawId: v.id,
|
|
1037
1046
|
label: v.name ?? v.id.slice(0, 8),
|
|
1038
|
-
values: v.values,
|
|
1047
|
+
values: unpinned(v.values),
|
|
1039
1048
|
apply: (coalesce) => controls.variants.load(v.id, { coalesce })
|
|
1040
1049
|
}));
|
|
1041
1050
|
return [...presetEntries, ...variantEntries];
|
|
@@ -1070,7 +1079,12 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
1070
1079
|
if (pill.style.left = `${left}px`, pill.style.right = `${box.trackWidth - right}px`, pill.style.top = `${top}px`, pill.style.height = `${box.height}px`, pill.style.opacity = activeState.drifted ? String(DRIFTED_PILL) : "1", pill.style.scale = "", first)
|
|
1071
1080
|
doc.defaultView?.requestAnimationFrame(() => pill.style.transition = "");
|
|
1072
1081
|
}
|
|
1073
|
-
let resolve = () => resolveActivePresetEntry(entries, controls.get(), lastAppliedId, activeEntryId(controls));
|
|
1082
|
+
let resolve = () => resolveActivePresetEntry(entries, unpinned(controls.get()), lastAppliedId, activeEntryId(controls)), wanted = (list) => (list.length > 1 || showVariants) && (relevant?.(list) ?? !0);
|
|
1083
|
+
function syncShown() {
|
|
1084
|
+
let show = wanted(entries);
|
|
1085
|
+
if (root.hidden === show && root.dataset.presence === void 0)
|
|
1086
|
+
root.hidden = !show;
|
|
1087
|
+
}
|
|
1074
1088
|
function syncSelection(snap = !1) {
|
|
1075
1089
|
if (activeState = resolve(), lastAppliedId !== null && activeState.activeId !== lastAppliedId)
|
|
1076
1090
|
lastAppliedId = null;
|
|
@@ -1110,12 +1124,12 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
1110
1124
|
run(() => {
|
|
1111
1125
|
let variantsRef = showVariants ? controls.variants.list() : null;
|
|
1112
1126
|
if (signature !== "" && variantsRef === lastVariantsRef) {
|
|
1113
|
-
syncSelection(!1);
|
|
1127
|
+
syncShown(), syncSelection(!1);
|
|
1114
1128
|
return;
|
|
1115
1129
|
}
|
|
1116
1130
|
lastVariantsRef = variantsRef;
|
|
1117
|
-
let nextEntries = buildEntries(), nextSig = JSON.stringify(nextEntries.map((e) => [e.id, e.label]))
|
|
1118
|
-
if (
|
|
1131
|
+
let nextEntries = buildEntries(), nextSig = JSON.stringify(nextEntries.map((e) => [e.id, e.label]));
|
|
1132
|
+
if (entries = nextEntries, syncShown(), nextSig !== signature)
|
|
1119
1133
|
signature = nextSig, entries = nextEntries, buttons = entries.map((entry, idx) => {
|
|
1120
1134
|
let b = el("button", "knobs-preset-chip", entry.label);
|
|
1121
1135
|
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) => {
|
|
@@ -1286,6 +1300,7 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
1286
1300
|
getEntries: () => entries,
|
|
1287
1301
|
getButtons: () => buttons,
|
|
1288
1302
|
getActiveState: () => activeState,
|
|
1303
|
+
shows: () => wanted(entries),
|
|
1289
1304
|
cycle,
|
|
1290
1305
|
selectById,
|
|
1291
1306
|
save: () => {
|
|
@@ -3762,7 +3777,9 @@ class GpuOverlayManager {
|
|
|
3762
3777
|
let ro = new ResizeObserver(() => {
|
|
3763
3778
|
this.wake();
|
|
3764
3779
|
});
|
|
3765
|
-
ro.observe(panel.root), ro.observe(panel.body),
|
|
3780
|
+
if (ro.observe(panel.root), ro.observe(panel.body), panel.pinned)
|
|
3781
|
+
ro.observe(panel.pinned);
|
|
3782
|
+
ro.observe(panel.presetStrip.track);
|
|
3766
3783
|
let mo = new MutationObserver(() => {
|
|
3767
3784
|
this.styleVersion++, this.wake();
|
|
3768
3785
|
});
|
|
@@ -5838,8 +5855,9 @@ class GpuOverlayManager {
|
|
|
5838
5855
|
], this.maskCount * MASK_FLOATS), this.curMask = ++this.maskCount;
|
|
5839
5856
|
}
|
|
5840
5857
|
}
|
|
5841
|
-
let ps = panel.presetStrip, hinting = panel.root.classList.contains("knobs-hinting");
|
|
5858
|
+
let ps = panel.presetStrip, hinting = panel.root.classList.contains("knobs-hinting"), stripAlpha = this.curAlpha;
|
|
5842
5859
|
if (!ps.element.hidden) {
|
|
5860
|
+
this.curAlpha = stripAlpha * this.presence(ps.element);
|
|
5843
5861
|
let psHovered = this.curPanelHovered && ps.element.matches(":hover");
|
|
5844
5862
|
this.curSubHovered = psHovered;
|
|
5845
5863
|
let psRect = this.rectOf(ps.element), trackRect = this.rectOf(ps.track);
|
|
@@ -5941,6 +5959,7 @@ class GpuOverlayManager {
|
|
|
5941
5959
|
align: "center"
|
|
5942
5960
|
});
|
|
5943
5961
|
}
|
|
5962
|
+
this.curAlpha = stripAlpha;
|
|
5944
5963
|
}
|
|
5945
5964
|
let tabs = panel.tabs;
|
|
5946
5965
|
if (tabs && !tabs.element.hidden)
|
|
@@ -6042,7 +6061,7 @@ class GpuOverlayManager {
|
|
|
6042
6061
|
}
|
|
6043
6062
|
if (bodyRevealing || pageMoving)
|
|
6044
6063
|
this.curBypassRectCache = !0;
|
|
6045
|
-
let bodyHovered = this.curPanelHovered && panel.body.matches(":hover");
|
|
6064
|
+
let bodyHovered = this.curPanelHovered && (panel.body.matches(":hover") || Boolean(panel.pinned?.matches(":hover")));
|
|
6046
6065
|
this.curSubHovered = bodyHovered, this.curPointerRow = bodyHovered && panel.root.hasAttribute("data-k-row-hover");
|
|
6047
6066
|
let panelAlpha = this.curAlpha;
|
|
6048
6067
|
for (let g of panel.groups) {
|
|
@@ -14856,7 +14875,7 @@ function buildPanel(controls, host, options) {
|
|
|
14856
14875
|
if (helpButton?.hidden)
|
|
14857
14876
|
titleEl.title = `${titleEl.textContent ? `${titleEl.textContent}
|
|
14858
14877
|
` : ""}Press ${show("help")} for keyboard shortcuts`;
|
|
14859
|
-
let presetLabel = (id) => options.presetLabels?.[id] ?? (id === "default" ? options.defaultLabel : void 0) ?? controls.presetLabel(id), presetStrip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel), 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, {
|
|
14878
|
+
let presetLabel = (id) => options.presetLabels?.[id] ?? (id === "default" ? options.defaultLabel : void 0) ?? controls.presetLabel(id), presetStrip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel, (entries) => presetsTouchTab(entries)), 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, {
|
|
14860
14879
|
placeholder: searchOptions.placeholder ?? "Search",
|
|
14861
14880
|
chips: (searchOptions.chips ?? ["modified"]).map((chip) => {
|
|
14862
14881
|
if (chip === "modified")
|
|
@@ -14878,7 +14897,10 @@ function buildPanel(controls, host, options) {
|
|
|
14878
14897
|
let allActions = [
|
|
14879
14898
|
...controls.actions ?? [],
|
|
14880
14899
|
...options.actions ?? []
|
|
14881
|
-
], body = el("div"), tabMotion = tabTransition(body, root, () => wakeGpu()),
|
|
14900
|
+
], body = el("div"), tabMotion = tabTransition(body, root, () => wakeGpu()), pinnedBody = el("div");
|
|
14901
|
+
if (pinnedBody.className = "knobs-pinned", !page)
|
|
14902
|
+
body.append(presetStrip.element);
|
|
14903
|
+
let blocks = [], cards = [], groupResetButtons = /* @__PURE__ */ new Map, groupDice = /* @__PURE__ */ new Map, gpuGroups = [], gpuRows = [], layout, layoutTick = 0, foldSource = null, userFolding = !1, noteFold = (source) => {
|
|
14882
14904
|
if (foldSource !== "user")
|
|
14883
14905
|
foldSource = source;
|
|
14884
14906
|
}, byUser = (act) => {
|
|
@@ -15195,8 +15217,11 @@ function buildPanel(controls, host, options) {
|
|
|
15195
15217
|
section: field.section
|
|
15196
15218
|
});
|
|
15197
15219
|
}
|
|
15198
|
-
|
|
15199
|
-
|
|
15220
|
+
let planned = gatherSubsections(specs);
|
|
15221
|
+
if (page || !planned.some(pinnedRow))
|
|
15222
|
+
return planned;
|
|
15223
|
+
return [...planned.filter(pinnedRow), ...planned.filter((spec) => !pinnedRow(spec))];
|
|
15224
|
+
}, pinnedRow = (r) => !page && controls.isPinned(r.keys[0] ?? r.id), gatherSubsections = (specs) => {
|
|
15200
15225
|
let out = [];
|
|
15201
15226
|
for (let i = 0;i < specs.length; ) {
|
|
15202
15227
|
let end = i;
|
|
@@ -15720,13 +15745,13 @@ function buildPanel(controls, host, options) {
|
|
|
15720
15745
|
} else {
|
|
15721
15746
|
let runs = [];
|
|
15722
15747
|
for (let r of next) {
|
|
15723
|
-
let last = runs.at(-1);
|
|
15724
|
-
if (last && last.group === r.group)
|
|
15748
|
+
let last = runs.at(-1), pinned = pinnedRow(r);
|
|
15749
|
+
if (last && last.group === r.group && last.pinned === pinned)
|
|
15725
15750
|
last.rows.push(r);
|
|
15726
15751
|
else
|
|
15727
|
-
runs.push({ group: r.group, rows: [r] });
|
|
15752
|
+
runs.push({ group: r.group, pinned, rows: [r] });
|
|
15728
15753
|
}
|
|
15729
|
-
let free = blocks.filter((b) => !b.leaving), chosen = [], usedSubheads = /* @__PURE__ */ new Set;
|
|
15754
|
+
let free = blocks.filter((b) => !b.leaving), chosen = [], pinnedBlocks = /* @__PURE__ */ new Set, usedSubheads = /* @__PURE__ */ new Set;
|
|
15730
15755
|
for (let run of runs) {
|
|
15731
15756
|
let at = free.findIndex((b) => b.group === run.group), block = at >= 0 ? free.splice(at, 1)[0] : newBlock(run.group);
|
|
15732
15757
|
if (at < 0) {
|
|
@@ -15741,7 +15766,8 @@ function buildPanel(controls, host, options) {
|
|
|
15741
15766
|
motion.push({ node: block.card, show: !0 });
|
|
15742
15767
|
}
|
|
15743
15768
|
}
|
|
15744
|
-
chosen.push(block)
|
|
15769
|
+
if (chosen.push(block), run.pinned)
|
|
15770
|
+
pinnedBlocks.add(block);
|
|
15745
15771
|
let nodes = [], under;
|
|
15746
15772
|
for (let r of run.rows) {
|
|
15747
15773
|
let name = r.field.subsection;
|
|
@@ -15766,7 +15792,8 @@ function buildPanel(controls, host, options) {
|
|
|
15766
15792
|
gpuRows.splice(gi, 1);
|
|
15767
15793
|
subheads.delete(id);
|
|
15768
15794
|
}
|
|
15769
|
-
|
|
15795
|
+
let nodesOf = (list) => list.flatMap((b) => b.head ? [b.head, b.card] : [b.card]);
|
|
15796
|
+
arrange(pinnedBody, nodesOf(chosen.filter((b) => pinnedBlocks.has(b)))), arrange(body, [presetStrip.element, ...nodesOf(chosen.filter((b) => !pinnedBlocks.has(b)))]);
|
|
15770
15797
|
for (let block of free) {
|
|
15771
15798
|
block.leaving = !0;
|
|
15772
15799
|
let fold = () => {
|
|
@@ -15872,15 +15899,24 @@ function buildPanel(controls, host, options) {
|
|
|
15872
15899
|
if (f.group === group && f.tab !== void 0)
|
|
15873
15900
|
return f.tab;
|
|
15874
15901
|
return;
|
|
15875
|
-
}, tabOf = (r) => r.field.tab ?? (r.group !== void 0 ? groupTab(r.group) : void 0), tabList = () => {
|
|
15902
|
+
}, tabOf = (r) => pinnedRow(r) ? void 0 : r.field.tab ?? (r.group !== void 0 ? groupTab(r.group) : void 0), tabList = () => {
|
|
15876
15903
|
if (page)
|
|
15877
15904
|
return [];
|
|
15878
15905
|
let list = (options.tabs ?? []).map((t) => typeof t === "string" ? { id: t, label: readableTitle(t) } : { id: t.id, label: t.label ?? readableTitle(t.id) }), seen = new Set(list.map((t) => t.id));
|
|
15879
|
-
for (let f of Object.
|
|
15880
|
-
if (f.tab !== void 0 && !seen.has(f.tab))
|
|
15906
|
+
for (let [key, f] of Object.entries(controls.schema))
|
|
15907
|
+
if (f.tab !== void 0 && !seen.has(f.tab) && !controls.isPinned(key))
|
|
15881
15908
|
seen.add(f.tab), list.push({ id: f.tab, label: readableTitle(f.tab) });
|
|
15882
15909
|
return list;
|
|
15883
15910
|
};
|
|
15911
|
+
function presetsTouchTab(entries) {
|
|
15912
|
+
if (page || activeTab === void 0)
|
|
15913
|
+
return !0;
|
|
15914
|
+
let keys = rows.flatMap((r) => {
|
|
15915
|
+
let tab = tabOf(r);
|
|
15916
|
+
return !pinnedRow(r) && (tab === void 0 || tab === activeTab) ? r.keys : [];
|
|
15917
|
+
});
|
|
15918
|
+
return presetsTouch(entries, keys, controls.locked);
|
|
15919
|
+
}
|
|
15884
15920
|
function syncTabs() {
|
|
15885
15921
|
let list = tabList(), has = (id) => id !== void 0 && list.some((t) => t.id === id);
|
|
15886
15922
|
if (!has(activeTab))
|
|
@@ -15934,7 +15970,7 @@ function buildPanel(controls, host, options) {
|
|
|
15934
15970
|
want.set(b.head, any);
|
|
15935
15971
|
want.set(b.card, any && !(b.group !== void 0 && folds.get(b.group) && !finding));
|
|
15936
15972
|
}
|
|
15937
|
-
return want;
|
|
15973
|
+
return want.set(presetStrip.element, presetStrip.shows()), want;
|
|
15938
15974
|
}
|
|
15939
15975
|
let shift = (node, show, delay) => {
|
|
15940
15976
|
shifting.add(node), node.dataset.presence = show ? "in" : "out", reveal(node, show, () => {
|
|
@@ -15951,6 +15987,9 @@ function buildPanel(controls, host, options) {
|
|
|
15951
15987
|
}
|
|
15952
15988
|
if (tabMotion.running)
|
|
15953
15989
|
tabMotion.finish();
|
|
15990
|
+
let strip = presetStrip.element;
|
|
15991
|
+
if (strip.hidden === want.get(strip))
|
|
15992
|
+
strip.hidden = !want.get(strip);
|
|
15954
15993
|
let showing = (n) => !n.hidden && n.dataset.leaving !== "true", set = (node, show, animate) => {
|
|
15955
15994
|
if (showing(node) === show)
|
|
15956
15995
|
return;
|
|
@@ -16155,9 +16194,9 @@ function buildPanel(controls, host, options) {
|
|
|
16155
16194
|
} else {
|
|
16156
16195
|
if (root.append(header), showFooter)
|
|
16157
16196
|
root.append(popover);
|
|
16158
|
-
if (root.append(tabStrip.element), search)
|
|
16197
|
+
if (root.append(pinnedBody, tabStrip.element), search)
|
|
16159
16198
|
root.append(search.element);
|
|
16160
|
-
if (root.append(
|
|
16199
|
+
if (root.append(body), panelActions.length > 0)
|
|
16161
16200
|
root.append(customActions);
|
|
16162
16201
|
root.append(text, apply, status, live);
|
|
16163
16202
|
}
|
|
@@ -16389,35 +16428,44 @@ function buildPanel(controls, host, options) {
|
|
|
16389
16428
|
nodes.push(...r.editor?.stops() ?? [r.input]);
|
|
16390
16429
|
return nodes.push(...body.querySelectorAll("[data-knobs-stop],.knobs-tile>[data-foldable],.knobs-section-head[data-foldable]")), nodes.filter(visible).sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
16391
16430
|
}, actionBarButtons = () => [...customActions.querySelectorAll("button")].filter((b) => visible(b) && !b.disabled), lastActionButton, stops = () => {
|
|
16392
|
-
let list = [];
|
|
16431
|
+
let list = [], strips = [];
|
|
16393
16432
|
if (visible(tabStrip.element))
|
|
16394
|
-
|
|
16395
|
-
|
|
16396
|
-
focus: (o) => tabStrip.focus(o)
|
|
16433
|
+
strips.push({
|
|
16434
|
+
at: tabStrip.element,
|
|
16435
|
+
stops: [{ contains: (n) => tabStrip.element.contains(n), focus: (o) => tabStrip.focus(o) }]
|
|
16397
16436
|
});
|
|
16398
16437
|
if (visible(presetStrip.element))
|
|
16399
|
-
|
|
16400
|
-
|
|
16401
|
-
|
|
16438
|
+
strips.push({
|
|
16439
|
+
at: presetStrip.element,
|
|
16440
|
+
stops: [
|
|
16441
|
+
{
|
|
16442
|
+
contains: (n) => presetStrip.element.contains(n),
|
|
16443
|
+
focus: (o) => presetStrip.element.focus(o)
|
|
16444
|
+
}
|
|
16445
|
+
]
|
|
16402
16446
|
});
|
|
16403
16447
|
if (page) {
|
|
16448
|
+
for (let strip of strips)
|
|
16449
|
+
list.push(...strip.stops);
|
|
16404
16450
|
for (let stop of pageStops())
|
|
16405
16451
|
list.push({ contains: (n) => stop.contains(n), focus: (o) => stop.focus(o) });
|
|
16406
16452
|
return list;
|
|
16407
16453
|
}
|
|
16408
|
-
let
|
|
16454
|
+
let stopOf = (stop) => ({
|
|
16455
|
+
contains: (n) => stop.contains(n),
|
|
16456
|
+
focus: (o) => stop.focus(o)
|
|
16457
|
+
}), shown = (stops) => stops.filter((s) => !s.closest('[hidden],[data-presence="out"]')).map(stopOf), runs = rows.map((r) => ({
|
|
16409
16458
|
at: r.row,
|
|
16410
|
-
stops: r.editor?.stops() ?? [r.input]
|
|
16459
|
+
stops: shown(r.editor?.stops() ?? [r.input])
|
|
16411
16460
|
}));
|
|
16412
16461
|
for (let b of blocks)
|
|
16413
16462
|
if (b.head?.hasAttribute("data-foldable") && !b.leaving)
|
|
16414
|
-
runs.push({ at: b.head, stops: [b.head] });
|
|
16463
|
+
runs.push({ at: b.head, stops: shown([b.head]) });
|
|
16415
16464
|
for (let sh of subheads.values())
|
|
16416
|
-
runs.push({ at: sh.el, stops: [sh.el] });
|
|
16417
|
-
runs.sort((a, b) => a.at.compareDocumentPosition(b.at) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
16465
|
+
runs.push({ at: sh.el, stops: shown([sh.el]) });
|
|
16466
|
+
runs.push(...strips), runs.sort((a, b) => a.at.compareDocumentPosition(b.at) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
16418
16467
|
for (let run of runs)
|
|
16419
|
-
|
|
16420
|
-
list.push({ contains: (n) => stop.contains(n), focus: (o) => stop.focus(o) });
|
|
16468
|
+
list.push(...run.stops);
|
|
16421
16469
|
let bar = actionBarButtons();
|
|
16422
16470
|
if (bar.length)
|
|
16423
16471
|
list.push({
|
|
@@ -17523,6 +17571,7 @@ function buildPanel(controls, host, options) {
|
|
|
17523
17571
|
id: controls.id,
|
|
17524
17572
|
root,
|
|
17525
17573
|
body,
|
|
17574
|
+
pinned: pinnedBody,
|
|
17526
17575
|
inline,
|
|
17527
17576
|
header,
|
|
17528
17577
|
titleEl,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
DEFAULT_LINEAR,
|
|
11
11
|
motionCss,
|
|
12
12
|
storageKey
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-mjdgzad5.js";
|
|
14
14
|
|
|
15
15
|
// src/core/theme.ts
|
|
16
16
|
var property = (k) => k.startsWith("--") ? k : `--k-${k}`;
|
|
@@ -2586,6 +2586,9 @@ ${GRAD_PROPS}
|
|
|
2586
2586
|
.knobs-group-head{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:20px;margin:var(--k-group-gap) 0 var(--_k-gap);padding:0 var(--k-group-inset);color:var(--_k-group-label);font-size:var(--k-group-size);font-weight:var(--_k-weight-title);letter-spacing:var(--k-group-tracking);text-transform:var(--k-group-transform)}
|
|
2587
2587
|
.knobs-panel:not(.knobs-page) .knobs-group-head:not(:first-child){border-top:1px solid var(--k-group-divider);padding-top:var(--k-group-divider-gap)}
|
|
2588
2588
|
.knobs-group-head:first-child{margin-top:0}
|
|
2589
|
+
.knobs-panel:not(.knobs-page) .knobs-presets+.knobs-group-head{margin-top:0;border-top:0;padding-top:0}
|
|
2590
|
+
.knobs-pinned{margin-bottom:var(--k-group-gap)}
|
|
2591
|
+
.knobs-pinned:empty{display:none}
|
|
2589
2592
|
.knobs-group-head>span:last-child{display:flex;align-items:center;gap:var(--_k-tray-pad);margin-right:calc((var(--k-icon-sm) - 20px) / 2)}
|
|
2590
2593
|
.knobs-group-head button{height:20px;padding:0 6px;font-size:var(--_k-text-meta);font-weight:500;letter-spacing:0;text-transform:none;color:var(--_k-fg-2);background:transparent;border-radius:var(--_k-radius-sm);cursor:pointer}
|
|
2591
2594
|
.knobs-group-head button:is(:hover:not([disabled]),:focus-visible){background:var(--_k-fill-selected);color:var(--_k-fg)}
|
|
@@ -516,6 +516,7 @@ var META_KEYS = [
|
|
|
516
516
|
"folded",
|
|
517
517
|
"sectionFolded",
|
|
518
518
|
"tab",
|
|
519
|
+
"pinned",
|
|
519
520
|
"subsection"
|
|
520
521
|
];
|
|
521
522
|
function pickMeta(source, skip = []) {
|
|
@@ -527,7 +528,19 @@ function pickMeta(source, skip = []) {
|
|
|
527
528
|
}
|
|
528
529
|
return out;
|
|
529
530
|
}
|
|
530
|
-
var READOUT_KINDS = /* @__PURE__ */ new Set(["watch", "sparkline", "graph", "meter", "table", "log"]), isReadout2 = (field) => READOUT_KINDS.has(field.kind), hasValue2 = (field) => field.kind !== "action" && field.kind !== "toolbar" && !READOUT_KINDS.has(field.kind)
|
|
531
|
+
var READOUT_KINDS = /* @__PURE__ */ new Set(["watch", "sparkline", "graph", "meter", "table", "log"]), isReadout2 = (field) => READOUT_KINDS.has(field.kind), hasValue2 = (field) => field.kind !== "action" && field.kind !== "toolbar" && !READOUT_KINDS.has(field.kind);
|
|
532
|
+
function pinnedKeys(schema, groups = []) {
|
|
533
|
+
let byGroup = new Map(groups.map((g) => [g, !0]));
|
|
534
|
+
for (let f of Object.values(schema))
|
|
535
|
+
if (f.group !== void 0 && f.pinned !== void 0 && !byGroup.has(f.group))
|
|
536
|
+
byGroup.set(f.group, f.pinned);
|
|
537
|
+
let out = /* @__PURE__ */ new Set;
|
|
538
|
+
for (let [key, f] of Object.entries(schema))
|
|
539
|
+
if (f.group === void 0 ? f.pinned === !0 : byGroup.get(f.group) === !0)
|
|
540
|
+
out.add(key);
|
|
541
|
+
return out;
|
|
542
|
+
}
|
|
543
|
+
var SPRING2 = {
|
|
531
544
|
duration: { min: 0.05, max: 2, step: 0.01 },
|
|
532
545
|
bounce: { min: -0.5, max: 1, step: 0.01 }
|
|
533
546
|
};
|
|
@@ -1160,4 +1173,4 @@ function urlFlag2(name) {
|
|
|
1160
1173
|
}
|
|
1161
1174
|
var storageKey = (id, part) => `knobs:${encodeURIComponent(id)}:${part}:v1`;
|
|
1162
1175
|
|
|
1163
|
-
export { __preload, __chunks, 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, SPRING2, valueKeys, knob2, choice2, action2, flatSpring2, toggle2, spring2, color2, point2, easing2, vec22, vec32, range2, angle2, seed2, keybind2, 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 };
|
|
1176
|
+
export { __preload, __chunks, 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, pinnedKeys, SPRING2, valueKeys, knob2, choice2, action2, flatSpring2, toggle2, spring2, color2, point2, easing2, vec22, vec32, range2, angle2, seed2, keybind2, 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
|
pickMeta,
|
|
28
28
|
isReadout2,
|
|
29
29
|
hasValue2,
|
|
30
|
+
pinnedKeys,
|
|
30
31
|
SPRING2,
|
|
31
32
|
valueKeys,
|
|
32
33
|
knob2,
|
|
@@ -75,8 +76,8 @@ import {
|
|
|
75
76
|
browserStorage,
|
|
76
77
|
urlFlag2,
|
|
77
78
|
storageKey
|
|
78
|
-
} from "./chunk-
|
|
79
|
-
__chunks(import.meta.url,["
|
|
79
|
+
} from "./chunk-mjdgzad5.js";
|
|
80
|
+
__chunks(import.meta.url,["js6xfzn6","bnhzf1tj","75gbzbyd","mjdgzad5","j57rhjtm"],[["./index.js",3],["./chunk-bnhzf1tj.js",3,4],["./chunk-75gbzbyd.js",3,4],["./chunk-mjdgzad5.js"],["./chunk-j57rhjtm.js",3]],0);
|
|
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) {
|
|
@@ -594,12 +595,30 @@ function defineControls(id, input, options = {}) {
|
|
|
594
595
|
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 = () => 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 = () => {
|
|
595
596
|
for (let cb of [...listeners])
|
|
596
597
|
cb();
|
|
597
|
-
}, 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"),
|
|
598
|
+
}, 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 = () => {
|
|
599
|
+
if (pinnedOf?.schema !== schema)
|
|
600
|
+
pinnedOf = { schema, keys: pinnedKeys(schema, options.pinned) };
|
|
601
|
+
return pinnedOf.keys;
|
|
602
|
+
}, unpinned = (values) => {
|
|
603
|
+
let keys = pinned();
|
|
604
|
+
if (keys.size === 0)
|
|
605
|
+
return values;
|
|
606
|
+
let out = { ...values };
|
|
607
|
+
for (let k of keys)
|
|
608
|
+
if (Object.hasOwn(locked, k))
|
|
609
|
+
out[k] = locked[k];
|
|
610
|
+
return Object.freeze(out);
|
|
611
|
+
}, presetPatch = (values) => {
|
|
612
|
+
let keys = pinned();
|
|
613
|
+
if (keys.size === 0)
|
|
614
|
+
return values;
|
|
615
|
+
return Object.fromEntries(Object.entries(values).filter(([k]) => !keys.has(k)));
|
|
616
|
+
}, buildPresets = () => Object.freeze(Object.fromEntries([
|
|
598
617
|
["default", locked],
|
|
599
618
|
...Object.entries(options.presets ?? {}).map(([id, patch]) => {
|
|
600
619
|
if (id === "default" || id === "custom")
|
|
601
620
|
throw Error(`Reserved preset id: ${id}`);
|
|
602
|
-
return [id, normalize(schema, { ...locked, ...patch })];
|
|
621
|
+
return [id, unpinned(normalize(schema, { ...locked, ...patch }))];
|
|
603
622
|
})
|
|
604
623
|
])), presets = buildPresets(), buildGroupPresets = () => Object.freeze(Object.fromEntries(Object.entries(options.groupPresets ?? {}).map(([group, list]) => {
|
|
605
624
|
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) => hasValue2(schema[k])));
|
|
@@ -700,7 +719,7 @@ function defineControls(id, input, options = {}) {
|
|
|
700
719
|
else if (ref?.kind === "variant") {
|
|
701
720
|
let found = list?.find((v) => v.id === ref.id);
|
|
702
721
|
if (found)
|
|
703
|
-
out = normalize(schema, found.values);
|
|
722
|
+
out = unpinned(normalize(schema, found.values));
|
|
704
723
|
}
|
|
705
724
|
let groups = Object.entries(b.groups);
|
|
706
725
|
if (groups.length) {
|
|
@@ -712,11 +731,10 @@ function defineControls(id, input, options = {}) {
|
|
|
712
731
|
return baseCache = { base: b, schema, presets, groupPresets, variants: list, values }, values;
|
|
713
732
|
}
|
|
714
733
|
let covered = (key) => {
|
|
715
|
-
let b = activeBase();
|
|
716
|
-
if (b.
|
|
734
|
+
let b = activeBase(), group = schema[key]?.group;
|
|
735
|
+
if (group !== void 0 && Object.hasOwn(b.groups, group))
|
|
717
736
|
return !0;
|
|
718
|
-
|
|
719
|
-
return group !== void 0 && Object.hasOwn(b.groups, group);
|
|
737
|
+
return b.preset !== null && !pinned().has(key);
|
|
720
738
|
};
|
|
721
739
|
function declaredShortcut(id) {
|
|
722
740
|
let slash = id.indexOf("/"), key = slash < 0 ? id : id.slice(0, slash), field = Object.hasOwn(schema, key) ? schema[key] : void 0;
|
|
@@ -986,7 +1004,7 @@ function defineControls(id, input, options = {}) {
|
|
|
986
1004
|
listen(cb) {
|
|
987
1005
|
return readoutListeners.add(cb), () => readoutListeners.delete(cb);
|
|
988
1006
|
}
|
|
989
|
-
}, variants = createVariants(() => schema, id, storageKey(id, "variants"), variantStorage, get, (v, variant, coalesce) => write(v, { source: "variant", coalesce }, makeBase(Object.freeze({ kind: "variant", id: variant }), {})), notify, enabled), resettable = (keys) => (keys ?? fieldKeys()).filter((k) => valueField(k) && !blocked(k));
|
|
1007
|
+
}, 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));
|
|
990
1008
|
function offBase(keys) {
|
|
991
1009
|
if (!hasBase())
|
|
992
1010
|
return !1;
|
|
@@ -1071,6 +1089,7 @@ function defineControls(id, input, options = {}) {
|
|
|
1071
1089
|
groupFolded: options.groupFolded,
|
|
1072
1090
|
sectionFolded: options.sectionFolded,
|
|
1073
1091
|
presetLabel: (presetId) => options.presetLabels?.[presetId] ?? (presetId === "default" ? options.defaultLabel : void 0) ?? presetId,
|
|
1092
|
+
isPinned: (key) => pinned().has(key),
|
|
1074
1093
|
get,
|
|
1075
1094
|
set,
|
|
1076
1095
|
sync,
|
|
@@ -1345,9 +1364,12 @@ function defineControls(id, input, options = {}) {
|
|
|
1345
1364
|
preset(id, presetOptions) {
|
|
1346
1365
|
if (!Object.hasOwn(presets, id))
|
|
1347
1366
|
throw Error(`Unknown preset: ${id}`);
|
|
1348
|
-
write(presets[id], { source: "preset", coalesce: presetOptions?.coalesce }, id === "default" ? NO_BASE : makeBase(Object.freeze({ kind: "preset", id }), {}));
|
|
1367
|
+
write(presetPatch(presets[id]), { source: "preset", coalesce: presetOptions?.coalesce }, id === "default" ? NO_BASE : makeBase(Object.freeze({ kind: "preset", id }), {}));
|
|
1368
|
+
},
|
|
1369
|
+
matchingPreset: () => {
|
|
1370
|
+
let cur = unpinned(get());
|
|
1371
|
+
return Object.keys(presets).find((id) => equal(cur, presets[id])) ?? "custom";
|
|
1349
1372
|
},
|
|
1350
|
-
matchingPreset: () => Object.keys(presets).find((id) => equal(get(), presets[id])) ?? "custom",
|
|
1351
1373
|
get groupPresets() {
|
|
1352
1374
|
return groupPresets;
|
|
1353
1375
|
},
|
|
@@ -1378,11 +1400,11 @@ function defineControls(id, input, options = {}) {
|
|
|
1378
1400
|
code: (format, values) => formatCode(values ?? get(), format),
|
|
1379
1401
|
variants,
|
|
1380
1402
|
attach(element, chip) {
|
|
1381
|
-
let load = options.ship ? () => options.ship().then((m) => m.attach) : () => (__preload("
|
|
1403
|
+
let load = options.ship ? () => options.ship().then((m) => m.attach) : () => (__preload("75gbzbyd"), import("./chunk-75gbzbyd.js")).then((m) => m.attach);
|
|
1382
1404
|
return live(load, (attach) => attach(controls, element, chip), (detach) => detach());
|
|
1383
1405
|
},
|
|
1384
1406
|
mount(container, mountOptions) {
|
|
1385
|
-
let load = options.ship ? () => options.ship().then((m) => m.mountPanel) : () => (__preload("
|
|
1407
|
+
let load = options.ship ? () => options.ship().then((m) => m.mountPanel) : () => (__preload("bnhzf1tj"), import("./chunk-bnhzf1tj.js")).then((m) => m.mountPanel);
|
|
1386
1408
|
return live(load, (mountPanel) => {
|
|
1387
1409
|
let panel = mountPanel(controls, container, mountOptions);
|
|
1388
1410
|
return mountOptions?.onReady?.(panel), panel;
|
|
@@ -1674,6 +1696,7 @@ function createRemoteControls(id, descriptors, options = {}) {
|
|
|
1674
1696
|
tunable: options.tunable,
|
|
1675
1697
|
history: options.history,
|
|
1676
1698
|
presets: options.presets,
|
|
1699
|
+
pinned: options.pinned,
|
|
1677
1700
|
groupPresets: options.groupPresets,
|
|
1678
1701
|
onPreset: options.onPreset,
|
|
1679
1702
|
actions: options.actions,
|
package/dist/index.js
CHANGED
|
@@ -460,6 +460,7 @@ var META_KEYS = [
|
|
|
460
460
|
"folded",
|
|
461
461
|
"sectionFolded",
|
|
462
462
|
"tab",
|
|
463
|
+
"pinned",
|
|
463
464
|
"subsection"
|
|
464
465
|
];
|
|
465
466
|
function pickMeta(source, skip = []) {
|
|
@@ -471,7 +472,19 @@ function pickMeta(source, skip = []) {
|
|
|
471
472
|
}
|
|
472
473
|
return out;
|
|
473
474
|
}
|
|
474
|
-
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)
|
|
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 pinnedKeys(schema, groups = []) {
|
|
477
|
+
let byGroup = new Map(groups.map((g) => [g, !0]));
|
|
478
|
+
for (let f of Object.values(schema))
|
|
479
|
+
if (f.group !== void 0 && f.pinned !== void 0 && !byGroup.has(f.group))
|
|
480
|
+
byGroup.set(f.group, f.pinned);
|
|
481
|
+
let out = /* @__PURE__ */ new Set;
|
|
482
|
+
for (let [key, f] of Object.entries(schema))
|
|
483
|
+
if (f.group === void 0 ? f.pinned === !0 : byGroup.get(f.group) === !0)
|
|
484
|
+
out.add(key);
|
|
485
|
+
return out;
|
|
486
|
+
}
|
|
487
|
+
var SPRING = {
|
|
475
488
|
duration: { min: 0.05, max: 2, step: 0.01 },
|
|
476
489
|
bounce: { min: -0.5, max: 1, step: 0.01 }
|
|
477
490
|
};
|
|
@@ -1612,12 +1625,30 @@ function defineControls(id, input, options = {}) {
|
|
|
1612
1625
|
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 = () => {
|
|
1613
1626
|
for (let cb of [...listeners])
|
|
1614
1627
|
cb();
|
|
1615
|
-
}, 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"),
|
|
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 = () => {
|
|
1629
|
+
if (pinnedOf?.schema !== schema)
|
|
1630
|
+
pinnedOf = { schema, keys: pinnedKeys(schema, options.pinned) };
|
|
1631
|
+
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([
|
|
1616
1647
|
["default", locked],
|
|
1617
1648
|
...Object.entries(options.presets ?? {}).map(([id, patch]) => {
|
|
1618
1649
|
if (id === "default" || id === "custom")
|
|
1619
1650
|
throw Error(`Reserved preset id: ${id}`);
|
|
1620
|
-
return [id, normalize(schema, { ...locked, ...patch })];
|
|
1651
|
+
return [id, unpinned(normalize(schema, { ...locked, ...patch }))];
|
|
1621
1652
|
})
|
|
1622
1653
|
])), presets = buildPresets(), buildGroupPresets = () => Object.freeze(Object.fromEntries(Object.entries(options.groupPresets ?? {}).map(([group, list]) => {
|
|
1623
1654
|
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])));
|
|
@@ -1718,7 +1749,7 @@ function defineControls(id, input, options = {}) {
|
|
|
1718
1749
|
else if (ref?.kind === "variant") {
|
|
1719
1750
|
let found = list?.find((v) => v.id === ref.id);
|
|
1720
1751
|
if (found)
|
|
1721
|
-
out = normalize(schema, found.values);
|
|
1752
|
+
out = unpinned(normalize(schema, found.values));
|
|
1722
1753
|
}
|
|
1723
1754
|
let groups = Object.entries(b.groups);
|
|
1724
1755
|
if (groups.length) {
|
|
@@ -1730,11 +1761,10 @@ function defineControls(id, input, options = {}) {
|
|
|
1730
1761
|
return baseCache = { base: b, schema, presets, groupPresets, variants: list, values }, values;
|
|
1731
1762
|
}
|
|
1732
1763
|
let covered = (key) => {
|
|
1733
|
-
let b = activeBase();
|
|
1734
|
-
if (b.
|
|
1764
|
+
let b = activeBase(), group = schema[key]?.group;
|
|
1765
|
+
if (group !== void 0 && Object.hasOwn(b.groups, group))
|
|
1735
1766
|
return !0;
|
|
1736
|
-
|
|
1737
|
-
return group !== void 0 && Object.hasOwn(b.groups, group);
|
|
1767
|
+
return b.preset !== null && !pinned().has(key);
|
|
1738
1768
|
};
|
|
1739
1769
|
function declaredShortcut(id) {
|
|
1740
1770
|
let slash = id.indexOf("/"), key = slash < 0 ? id : id.slice(0, slash), field = Object.hasOwn(schema, key) ? schema[key] : void 0;
|
|
@@ -2004,7 +2034,7 @@ function defineControls(id, input, options = {}) {
|
|
|
2004
2034
|
listen(cb) {
|
|
2005
2035
|
return readoutListeners.add(cb), () => readoutListeners.delete(cb);
|
|
2006
2036
|
}
|
|
2007
|
-
}, variants = createVariants(() => schema, id, storageKey(id, "variants"), variantStorage, get, (v, variant, coalesce) => write(v, { source: "variant", coalesce }, makeBase(Object.freeze({ kind: "variant", id: variant }), {})), notify, enabled), resettable = (keys) => (keys ?? fieldKeys()).filter((k) => valueField(k) && !blocked(k));
|
|
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));
|
|
2008
2038
|
function offBase(keys) {
|
|
2009
2039
|
if (!hasBase())
|
|
2010
2040
|
return !1;
|
|
@@ -2089,6 +2119,7 @@ function defineControls(id, input, options = {}) {
|
|
|
2089
2119
|
groupFolded: options.groupFolded,
|
|
2090
2120
|
sectionFolded: options.sectionFolded,
|
|
2091
2121
|
presetLabel: (presetId) => options.presetLabels?.[presetId] ?? (presetId === "default" ? options.defaultLabel : void 0) ?? presetId,
|
|
2122
|
+
isPinned: (key) => pinned().has(key),
|
|
2092
2123
|
get,
|
|
2093
2124
|
set,
|
|
2094
2125
|
sync,
|
|
@@ -2363,9 +2394,12 @@ function defineControls(id, input, options = {}) {
|
|
|
2363
2394
|
preset(id, presetOptions) {
|
|
2364
2395
|
if (!Object.hasOwn(presets, id))
|
|
2365
2396
|
throw Error(`Unknown preset: ${id}`);
|
|
2366
|
-
write(presets[id], { source: "preset", coalesce: presetOptions?.coalesce }, id === "default" ? NO_BASE : makeBase(Object.freeze({ kind: "preset", id }), {}));
|
|
2397
|
+
write(presetPatch(presets[id]), { source: "preset", coalesce: presetOptions?.coalesce }, id === "default" ? NO_BASE : makeBase(Object.freeze({ kind: "preset", id }), {}));
|
|
2398
|
+
},
|
|
2399
|
+
matchingPreset: () => {
|
|
2400
|
+
let cur = unpinned(get());
|
|
2401
|
+
return Object.keys(presets).find((id) => equal(cur, presets[id])) ?? "custom";
|
|
2367
2402
|
},
|
|
2368
|
-
matchingPreset: () => Object.keys(presets).find((id) => equal(get(), presets[id])) ?? "custom",
|
|
2369
2403
|
get groupPresets() {
|
|
2370
2404
|
return groupPresets;
|
|
2371
2405
|
},
|
|
@@ -2692,6 +2726,7 @@ function createRemoteControls(id, descriptors, options = {}) {
|
|
|
2692
2726
|
tunable: options.tunable,
|
|
2693
2727
|
history: options.history,
|
|
2694
2728
|
presets: options.presets,
|
|
2729
|
+
pinned: options.pinned,
|
|
2695
2730
|
groupPresets: options.groupPresets,
|
|
2696
2731
|
onPreset: options.onPreset,
|
|
2697
2732
|
actions: options.actions,
|
package/dist/panel.js
CHANGED
|
@@ -3288,6 +3288,9 @@ ${GRAD_PROPS}
|
|
|
3288
3288
|
.knobs-group-head{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:20px;margin:var(--k-group-gap) 0 var(--_k-gap);padding:0 var(--k-group-inset);color:var(--_k-group-label);font-size:var(--k-group-size);font-weight:var(--_k-weight-title);letter-spacing:var(--k-group-tracking);text-transform:var(--k-group-transform)}
|
|
3289
3289
|
.knobs-panel:not(.knobs-page) .knobs-group-head:not(:first-child){border-top:1px solid var(--k-group-divider);padding-top:var(--k-group-divider-gap)}
|
|
3290
3290
|
.knobs-group-head:first-child{margin-top:0}
|
|
3291
|
+
.knobs-panel:not(.knobs-page) .knobs-presets+.knobs-group-head{margin-top:0;border-top:0;padding-top:0}
|
|
3292
|
+
.knobs-pinned{margin-bottom:var(--k-group-gap)}
|
|
3293
|
+
.knobs-pinned:empty{display:none}
|
|
3291
3294
|
.knobs-group-head>span:last-child{display:flex;align-items:center;gap:var(--_k-tray-pad);margin-right:calc((var(--k-icon-sm) - 20px) / 2)}
|
|
3292
3295
|
.knobs-group-head button{height:20px;padding:0 6px;font-size:var(--_k-text-meta);font-weight:500;letter-spacing:0;text-transform:none;color:var(--_k-fg-2);background:transparent;border-radius:var(--_k-radius-sm);cursor:pointer}
|
|
3293
3296
|
.knobs-group-head button:is(:hover:not([disabled]),:focus-visible){background:var(--_k-fill-selected);color:var(--_k-fg)}
|
|
@@ -6928,6 +6931,9 @@ function resolveActivePresetEntry(entries, currentValues, lastAppliedId, baseId
|
|
|
6928
6931
|
isCustom: true
|
|
6929
6932
|
};
|
|
6930
6933
|
}
|
|
6934
|
+
function presetsTouch(entries, keys, defaults) {
|
|
6935
|
+
return entries.some((e) => !(e.kind === "preset" && e.rawId === "default") && keys.some((k) => !same2(e.values[k], defaults[k])));
|
|
6936
|
+
}
|
|
6931
6937
|
function nextPresetIndex(count, activeIndex, direction) {
|
|
6932
6938
|
if (count <= 0)
|
|
6933
6939
|
return -1;
|
|
@@ -6935,7 +6941,7 @@ function nextPresetIndex(count, activeIndex, direction) {
|
|
|
6935
6941
|
return direction > 0 ? 0 : count - 1;
|
|
6936
6942
|
return ((activeIndex + direction) % count + count) % count;
|
|
6937
6943
|
}
|
|
6938
|
-
function createPresetStrip(doc, controls, showVariants, run, onAfterChange, presetLabel = (id) => controls.presetLabel(id)) {
|
|
6944
|
+
function createPresetStrip(doc, controls, showVariants, run, onAfterChange, presetLabel = (id) => controls.presetLabel(id), relevant) {
|
|
6939
6945
|
const wake = () => onAfterChange?.();
|
|
6940
6946
|
const el = (tag, cls, text) => {
|
|
6941
6947
|
const node = doc.createElement(tag);
|
|
@@ -6981,6 +6987,15 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
6981
6987
|
let prevDrifted = false;
|
|
6982
6988
|
let initializedPill = false;
|
|
6983
6989
|
let slideUntil = 0;
|
|
6990
|
+
const unpinned = (values) => {
|
|
6991
|
+
let out;
|
|
6992
|
+
for (const k of Object.keys(values))
|
|
6993
|
+
if (controls.isPinned(k) && !same2(values[k], controls.locked[k])) {
|
|
6994
|
+
out ??= { ...values };
|
|
6995
|
+
out[k] = controls.locked[k];
|
|
6996
|
+
}
|
|
6997
|
+
return out ?? values;
|
|
6998
|
+
};
|
|
6984
6999
|
function buildEntries() {
|
|
6985
7000
|
const presetEntries = Object.entries(controls.presets).map(([id, presetValues]) => ({
|
|
6986
7001
|
id: `p:${id}`,
|
|
@@ -6998,7 +7013,7 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
6998
7013
|
kind: "variant",
|
|
6999
7014
|
rawId: v.id,
|
|
7000
7015
|
label: v.name ?? v.id.slice(0, 8),
|
|
7001
|
-
values: v.values,
|
|
7016
|
+
values: unpinned(v.values),
|
|
7002
7017
|
apply: (coalesce) => controls.variants.load(v.id, { coalesce })
|
|
7003
7018
|
}));
|
|
7004
7019
|
return [...presetEntries, ...variantEntries];
|
|
@@ -7050,7 +7065,13 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7050
7065
|
if (first)
|
|
7051
7066
|
doc.defaultView?.requestAnimationFrame(() => pill.style.transition = "");
|
|
7052
7067
|
}
|
|
7053
|
-
const resolve = () => resolveActivePresetEntry(entries, controls.get(), lastAppliedId, activeEntryId(controls));
|
|
7068
|
+
const resolve = () => resolveActivePresetEntry(entries, unpinned(controls.get()), lastAppliedId, activeEntryId(controls));
|
|
7069
|
+
const wanted = (list) => (list.length > 1 || showVariants) && (relevant?.(list) ?? true);
|
|
7070
|
+
function syncShown() {
|
|
7071
|
+
const show = wanted(entries);
|
|
7072
|
+
if (root.hidden === show && root.dataset.presence === undefined)
|
|
7073
|
+
root.hidden = !show;
|
|
7074
|
+
}
|
|
7054
7075
|
function syncSelection(snap = false) {
|
|
7055
7076
|
activeState = resolve();
|
|
7056
7077
|
if (lastAppliedId !== null && activeState.activeId !== lastAppliedId) {
|
|
@@ -7107,14 +7128,15 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7107
7128
|
run(() => {
|
|
7108
7129
|
const variantsRef = showVariants ? controls.variants.list() : null;
|
|
7109
7130
|
if (signature !== "" && variantsRef === lastVariantsRef) {
|
|
7131
|
+
syncShown();
|
|
7110
7132
|
syncSelection(false);
|
|
7111
7133
|
return;
|
|
7112
7134
|
}
|
|
7113
7135
|
lastVariantsRef = variantsRef;
|
|
7114
7136
|
const nextEntries = buildEntries();
|
|
7115
7137
|
const nextSig = JSON.stringify(nextEntries.map((e) => [e.id, e.label]));
|
|
7116
|
-
|
|
7117
|
-
|
|
7138
|
+
entries = nextEntries;
|
|
7139
|
+
syncShown();
|
|
7118
7140
|
if (nextSig !== signature) {
|
|
7119
7141
|
signature = nextSig;
|
|
7120
7142
|
entries = nextEntries;
|
|
@@ -7385,6 +7407,7 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7385
7407
|
getEntries: () => entries,
|
|
7386
7408
|
getButtons: () => buttons,
|
|
7387
7409
|
getActiveState: () => activeState,
|
|
7410
|
+
shows: () => wanted(entries),
|
|
7388
7411
|
cycle,
|
|
7389
7412
|
selectById,
|
|
7390
7413
|
save: () => {
|
|
@@ -10164,6 +10187,8 @@ class GpuOverlayManager {
|
|
|
10164
10187
|
});
|
|
10165
10188
|
ro.observe(panel.root);
|
|
10166
10189
|
ro.observe(panel.body);
|
|
10190
|
+
if (panel.pinned)
|
|
10191
|
+
ro.observe(panel.pinned);
|
|
10167
10192
|
ro.observe(panel.presetStrip.track);
|
|
10168
10193
|
const mo = new MutationObserver(() => {
|
|
10169
10194
|
this.styleVersion++;
|
|
@@ -12873,7 +12898,9 @@ class GpuOverlayManager {
|
|
|
12873
12898
|
}
|
|
12874
12899
|
const ps = panel.presetStrip;
|
|
12875
12900
|
const hinting = panel.root.classList.contains("knobs-hinting");
|
|
12901
|
+
const stripAlpha = this.curAlpha;
|
|
12876
12902
|
if (!ps.element.hidden) {
|
|
12903
|
+
this.curAlpha = stripAlpha * this.presence(ps.element);
|
|
12877
12904
|
const psHovered = this.curPanelHovered && ps.element.matches(":hover");
|
|
12878
12905
|
this.curSubHovered = psHovered;
|
|
12879
12906
|
const psRect = this.rectOf(ps.element);
|
|
@@ -12997,6 +13024,7 @@ class GpuOverlayManager {
|
|
|
12997
13024
|
align: "center"
|
|
12998
13025
|
});
|
|
12999
13026
|
}
|
|
13027
|
+
this.curAlpha = stripAlpha;
|
|
13000
13028
|
}
|
|
13001
13029
|
const tabs = panel.tabs;
|
|
13002
13030
|
if (tabs && !tabs.element.hidden)
|
|
@@ -13112,7 +13140,7 @@ class GpuOverlayManager {
|
|
|
13112
13140
|
}
|
|
13113
13141
|
if (bodyRevealing || pageMoving)
|
|
13114
13142
|
this.curBypassRectCache = true;
|
|
13115
|
-
const bodyHovered = this.curPanelHovered && panel.body.matches(":hover");
|
|
13143
|
+
const bodyHovered = this.curPanelHovered && (panel.body.matches(":hover") || Boolean(panel.pinned?.matches(":hover")));
|
|
13116
13144
|
this.curSubHovered = bodyHovered;
|
|
13117
13145
|
this.curPointerRow = bodyHovered && panel.root.hasAttribute("data-k-row-hover");
|
|
13118
13146
|
const panelAlpha = this.curAlpha;
|
|
@@ -25740,7 +25768,7 @@ function buildPanel(controls, host, options) {
|
|
|
25740
25768
|
titleEl.title = `${titleEl.textContent ? `${titleEl.textContent}
|
|
25741
25769
|
` : ""}Press ${show("help")} for keyboard shortcuts`;
|
|
25742
25770
|
const presetLabel = (id) => options.presetLabels?.[id] ?? (id === "default" ? options.defaultLabel : undefined) ?? controls.presetLabel(id);
|
|
25743
|
-
const presetStrip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel);
|
|
25771
|
+
const presetStrip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel, (entries) => presetsTouchTab(entries));
|
|
25744
25772
|
const tabStrip = createTabStrip(doc, (id, animate) => selectTab(id, animate));
|
|
25745
25773
|
let activeTab;
|
|
25746
25774
|
const searchOptions = typeof options.search === "object" ? options.search : {};
|
|
@@ -25783,6 +25811,10 @@ function buildPanel(controls, host, options) {
|
|
|
25783
25811
|
];
|
|
25784
25812
|
const body = el("div");
|
|
25785
25813
|
const tabMotion = tabTransition(body, root, () => wakeGpu());
|
|
25814
|
+
const pinnedBody = el("div");
|
|
25815
|
+
pinnedBody.className = "knobs-pinned";
|
|
25816
|
+
if (!page)
|
|
25817
|
+
body.append(presetStrip.element);
|
|
25786
25818
|
const blocks = [];
|
|
25787
25819
|
const cards = [];
|
|
25788
25820
|
const groupResetButtons = new Map;
|
|
@@ -26264,8 +26296,12 @@ function buildPanel(controls, host, options) {
|
|
|
26264
26296
|
section: field.section
|
|
26265
26297
|
});
|
|
26266
26298
|
}
|
|
26267
|
-
|
|
26299
|
+
const planned = gatherSubsections(specs);
|
|
26300
|
+
if (page || !planned.some(pinnedRow))
|
|
26301
|
+
return planned;
|
|
26302
|
+
return [...planned.filter(pinnedRow), ...planned.filter((spec) => !pinnedRow(spec))];
|
|
26268
26303
|
};
|
|
26304
|
+
const pinnedRow = (r) => !page && controls.isPinned(r.keys[0] ?? r.id);
|
|
26269
26305
|
const gatherSubsections = (specs) => {
|
|
26270
26306
|
const out = [];
|
|
26271
26307
|
for (let i = 0;i < specs.length; ) {
|
|
@@ -26911,13 +26947,15 @@ function buildPanel(controls, host, options) {
|
|
|
26911
26947
|
const runs = [];
|
|
26912
26948
|
for (const r of next) {
|
|
26913
26949
|
const last = runs.at(-1);
|
|
26914
|
-
|
|
26950
|
+
const pinned = pinnedRow(r);
|
|
26951
|
+
if (last && last.group === r.group && last.pinned === pinned)
|
|
26915
26952
|
last.rows.push(r);
|
|
26916
26953
|
else
|
|
26917
|
-
runs.push({ group: r.group, rows: [r] });
|
|
26954
|
+
runs.push({ group: r.group, pinned, rows: [r] });
|
|
26918
26955
|
}
|
|
26919
26956
|
const free = blocks.filter((b) => !b.leaving);
|
|
26920
26957
|
const chosen = [];
|
|
26958
|
+
const pinnedBlocks = new Set;
|
|
26921
26959
|
const usedSubheads = new Set;
|
|
26922
26960
|
for (const run of runs) {
|
|
26923
26961
|
const at = free.findIndex((b) => b.group === run.group);
|
|
@@ -26935,6 +26973,8 @@ function buildPanel(controls, host, options) {
|
|
|
26935
26973
|
}
|
|
26936
26974
|
}
|
|
26937
26975
|
chosen.push(block);
|
|
26976
|
+
if (run.pinned)
|
|
26977
|
+
pinnedBlocks.add(block);
|
|
26938
26978
|
const nodes = [];
|
|
26939
26979
|
let under;
|
|
26940
26980
|
for (const r of run.rows) {
|
|
@@ -26966,7 +27006,9 @@ function buildPanel(controls, host, options) {
|
|
|
26966
27006
|
gpuRows.splice(gi, 1);
|
|
26967
27007
|
subheads.delete(id);
|
|
26968
27008
|
}
|
|
26969
|
-
|
|
27009
|
+
const nodesOf = (list) => list.flatMap((b) => b.head ? [b.head, b.card] : [b.card]);
|
|
27010
|
+
arrange(pinnedBody, nodesOf(chosen.filter((b) => pinnedBlocks.has(b))));
|
|
27011
|
+
arrange(body, [presetStrip.element, ...nodesOf(chosen.filter((b) => !pinnedBlocks.has(b)))]);
|
|
26970
27012
|
for (const block of free) {
|
|
26971
27013
|
block.leaving = true;
|
|
26972
27014
|
const fold = () => {
|
|
@@ -27097,19 +27139,28 @@ function buildPanel(controls, host, options) {
|
|
|
27097
27139
|
return f.tab;
|
|
27098
27140
|
return;
|
|
27099
27141
|
};
|
|
27100
|
-
const tabOf = (r) => r.field.tab ?? (r.group !== undefined ? groupTab(r.group) : undefined);
|
|
27142
|
+
const tabOf = (r) => pinnedRow(r) ? undefined : r.field.tab ?? (r.group !== undefined ? groupTab(r.group) : undefined);
|
|
27101
27143
|
const tabList = () => {
|
|
27102
27144
|
if (page)
|
|
27103
27145
|
return [];
|
|
27104
27146
|
const list = (options.tabs ?? []).map((t) => typeof t === "string" ? { id: t, label: readableTitle(t) } : { id: t.id, label: t.label ?? readableTitle(t.id) });
|
|
27105
27147
|
const seen = new Set(list.map((t) => t.id));
|
|
27106
|
-
for (const f of Object.
|
|
27107
|
-
if (f.tab !== undefined && !seen.has(f.tab)) {
|
|
27148
|
+
for (const [key, f] of Object.entries(controls.schema))
|
|
27149
|
+
if (f.tab !== undefined && !seen.has(f.tab) && !controls.isPinned(key)) {
|
|
27108
27150
|
seen.add(f.tab);
|
|
27109
27151
|
list.push({ id: f.tab, label: readableTitle(f.tab) });
|
|
27110
27152
|
}
|
|
27111
27153
|
return list;
|
|
27112
27154
|
};
|
|
27155
|
+
function presetsTouchTab(entries) {
|
|
27156
|
+
if (page || activeTab === undefined)
|
|
27157
|
+
return true;
|
|
27158
|
+
const keys = rows.flatMap((r) => {
|
|
27159
|
+
const tab = tabOf(r);
|
|
27160
|
+
return !pinnedRow(r) && (tab === undefined || tab === activeTab) ? r.keys : [];
|
|
27161
|
+
});
|
|
27162
|
+
return presetsTouch(entries, keys, controls.locked);
|
|
27163
|
+
}
|
|
27113
27164
|
function syncTabs() {
|
|
27114
27165
|
const list = tabList();
|
|
27115
27166
|
const has = (id) => id !== undefined && list.some((t) => t.id === id);
|
|
@@ -27172,6 +27223,7 @@ function buildPanel(controls, host, options) {
|
|
|
27172
27223
|
want.set(b.head, any);
|
|
27173
27224
|
want.set(b.card, any && !(b.group !== undefined && folds.get(b.group) && !finding));
|
|
27174
27225
|
}
|
|
27226
|
+
want.set(presetStrip.element, presetStrip.shows());
|
|
27175
27227
|
return want;
|
|
27176
27228
|
}
|
|
27177
27229
|
const shift = (node, show, delay) => {
|
|
@@ -27195,6 +27247,9 @@ function buildPanel(controls, host, options) {
|
|
|
27195
27247
|
}
|
|
27196
27248
|
if (tabMotion.running)
|
|
27197
27249
|
tabMotion.finish();
|
|
27250
|
+
const strip = presetStrip.element;
|
|
27251
|
+
if (strip.hidden === want.get(strip))
|
|
27252
|
+
strip.hidden = !want.get(strip);
|
|
27198
27253
|
const showing = (n) => !n.hidden && n.dataset.leaving !== "true";
|
|
27199
27254
|
const set = (node, show, animate) => {
|
|
27200
27255
|
if (showing(node) === show)
|
|
@@ -27481,10 +27536,10 @@ function buildPanel(controls, host, options) {
|
|
|
27481
27536
|
root.append(header);
|
|
27482
27537
|
if (showFooter)
|
|
27483
27538
|
root.append(popover);
|
|
27484
|
-
root.append(tabStrip.element);
|
|
27539
|
+
root.append(pinnedBody, tabStrip.element);
|
|
27485
27540
|
if (search)
|
|
27486
27541
|
root.append(search.element);
|
|
27487
|
-
root.append(
|
|
27542
|
+
root.append(body);
|
|
27488
27543
|
if (panelActions.length > 0)
|
|
27489
27544
|
root.append(customActions);
|
|
27490
27545
|
root.append(text, apply, status, live);
|
|
@@ -27784,34 +27839,47 @@ function buildPanel(controls, host, options) {
|
|
|
27784
27839
|
let lastActionButton;
|
|
27785
27840
|
const stops = () => {
|
|
27786
27841
|
const list = [];
|
|
27842
|
+
const strips = [];
|
|
27787
27843
|
if (visible(tabStrip.element))
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
focus: (o) => tabStrip.focus(o)
|
|
27844
|
+
strips.push({
|
|
27845
|
+
at: tabStrip.element,
|
|
27846
|
+
stops: [{ contains: (n) => tabStrip.element.contains(n), focus: (o) => tabStrip.focus(o) }]
|
|
27791
27847
|
});
|
|
27792
27848
|
if (visible(presetStrip.element))
|
|
27793
|
-
|
|
27794
|
-
|
|
27795
|
-
|
|
27849
|
+
strips.push({
|
|
27850
|
+
at: presetStrip.element,
|
|
27851
|
+
stops: [
|
|
27852
|
+
{
|
|
27853
|
+
contains: (n) => presetStrip.element.contains(n),
|
|
27854
|
+
focus: (o) => presetStrip.element.focus(o)
|
|
27855
|
+
}
|
|
27856
|
+
]
|
|
27796
27857
|
});
|
|
27797
27858
|
if (page) {
|
|
27859
|
+
for (const strip of strips)
|
|
27860
|
+
list.push(...strip.stops);
|
|
27798
27861
|
for (const stop of pageStops())
|
|
27799
27862
|
list.push({ contains: (n) => stop.contains(n), focus: (o) => stop.focus(o) });
|
|
27800
27863
|
return list;
|
|
27801
27864
|
}
|
|
27865
|
+
const stopOf = (stop) => ({
|
|
27866
|
+
contains: (n) => stop.contains(n),
|
|
27867
|
+
focus: (o) => stop.focus(o)
|
|
27868
|
+
});
|
|
27869
|
+
const shown = (stops) => stops.filter((s) => !s.closest('[hidden],[data-presence="out"]')).map(stopOf);
|
|
27802
27870
|
const runs = rows.map((r) => ({
|
|
27803
27871
|
at: r.row,
|
|
27804
|
-
stops: r.editor?.stops() ?? [r.input]
|
|
27872
|
+
stops: shown(r.editor?.stops() ?? [r.input])
|
|
27805
27873
|
}));
|
|
27806
27874
|
for (const b of blocks)
|
|
27807
27875
|
if (b.head?.hasAttribute("data-foldable") && !b.leaving)
|
|
27808
|
-
runs.push({ at: b.head, stops: [b.head] });
|
|
27876
|
+
runs.push({ at: b.head, stops: shown([b.head]) });
|
|
27809
27877
|
for (const sh of subheads.values())
|
|
27810
|
-
runs.push({ at: sh.el, stops: [sh.el] });
|
|
27878
|
+
runs.push({ at: sh.el, stops: shown([sh.el]) });
|
|
27879
|
+
runs.push(...strips);
|
|
27811
27880
|
runs.sort((a, b) => a.at.compareDocumentPosition(b.at) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
27812
27881
|
for (const run of runs)
|
|
27813
|
-
|
|
27814
|
-
list.push({ contains: (n) => stop.contains(n), focus: (o) => stop.focus(o) });
|
|
27882
|
+
list.push(...run.stops);
|
|
27815
27883
|
const bar = actionBarButtons();
|
|
27816
27884
|
if (bar.length)
|
|
27817
27885
|
list.push({
|
|
@@ -29265,6 +29333,7 @@ function buildPanel(controls, host, options) {
|
|
|
29265
29333
|
id: controls.id,
|
|
29266
29334
|
root,
|
|
29267
29335
|
body,
|
|
29336
|
+
pinned: pinnedBody,
|
|
29268
29337
|
inline,
|
|
29269
29338
|
header,
|
|
29270
29339
|
titleEl,
|
|
@@ -130,6 +130,8 @@ export interface RemoteControlsOptions {
|
|
|
130
130
|
readonly nullDefaults?: boolean;
|
|
131
131
|
readonly values?: State;
|
|
132
132
|
readonly presets?: Readonly<Record<string, State>>;
|
|
133
|
+
/** Groups pinned above the tab strip, outside the presets (see `ControlsOptions.pinned`). */
|
|
134
|
+
readonly pinned?: readonly string[];
|
|
133
135
|
/** Numbered presets per group, e.g. the phone's preset sets (see `ControlsOptions.groupPresets`). */
|
|
134
136
|
readonly groupPresets?: Readonly<Record<string, readonly GroupPreset[]>>;
|
|
135
137
|
/** Display labels by group id, section id and preset id (see `ControlsOptions.groupLabels`). */
|
|
@@ -50,6 +50,12 @@ export interface FieldMeta {
|
|
|
50
50
|
* Fields without a tab show on every tab.
|
|
51
51
|
*/
|
|
52
52
|
readonly tab?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Pins the field's group to the top of a panel, under the header and above the tab strip, on
|
|
55
|
+
* every tab (the first field of a group that says wins; a field without a group pins itself).
|
|
56
|
+
* Panel presets and saved variants leave pinned fields alone. See `ControlsOptions.pinned`.
|
|
57
|
+
*/
|
|
58
|
+
readonly pinned?: boolean;
|
|
53
59
|
/**
|
|
54
60
|
* A collapsible sub-heading inside the field's group: a flat heading with a hairline, never a
|
|
55
61
|
* card inside the card. Consecutive fields that share it sit under one heading.
|
|
@@ -57,7 +63,7 @@ export interface FieldMeta {
|
|
|
57
63
|
readonly subsection?: string;
|
|
58
64
|
}
|
|
59
65
|
/** Meta keys copied from a field's options or a descriptor onto the field. */
|
|
60
|
-
export declare const META_KEYS: readonly ["label", "group", "groupLabel", "section", "sectionLabel", "groupDescription", "sectionDescription", "shortcut", "random", "disabled", "readOnly", "folded", "sectionFolded", "tab", "subsection"];
|
|
66
|
+
export declare const META_KEYS: readonly ["label", "group", "groupLabel", "section", "sectionLabel", "groupDescription", "sectionDescription", "shortcut", "random", "disabled", "readOnly", "folded", "sectionFolded", "tab", "pinned", "subsection"];
|
|
61
67
|
/** The meta `source` gives, without undefined keys. */
|
|
62
68
|
export declare function pickMeta(source: FieldMeta | undefined, skip?: readonly string[]): FieldMeta;
|
|
63
69
|
/** Marks a knob as half of a flat spring: two number keys drawn as one spring card. */
|
|
@@ -329,6 +335,11 @@ export declare const isReadout: (field: {
|
|
|
329
335
|
/** Whether a field holds a value (actions, toolbars and readouts don't). */
|
|
330
336
|
export declare const hasValue: (field: Field) => field is ValueField;
|
|
331
337
|
export type Schema = Readonly<Record<string, Field>>;
|
|
338
|
+
/**
|
|
339
|
+
* The schema's pinned keys (values, actions and readouts alike): every key of a group in `groups`,
|
|
340
|
+
* or whose first field that gives `pinned` says `true`, and ungrouped fields that say so themselves.
|
|
341
|
+
*/
|
|
342
|
+
export declare function pinnedKeys(schema: Schema, groups?: readonly string[]): ReadonlySet<string>;
|
|
332
343
|
export type FieldValue<F extends Field> = F extends Knob ? number : F extends Choice<infer T> ? T : F extends Toggle ? boolean : F extends SpringField ? SpringValue : F extends ColorField ? string : F extends PointField ? Point : F extends EasingField ? Bezier : F extends VecField<3> ? Vec3 : F extends VecField<2> ? Point : F extends RangeField ? readonly [number, number] : F extends AngleField | SeedField ? number : F extends KeybindField ? string : F extends GradientField ? readonly GradientStop[] : F extends CurveField ? readonly CurvePoint[] : never;
|
|
333
344
|
/** The keys of a schema's readouts (what `controls.watch` takes). */
|
|
334
345
|
export type ReadoutKeys<S extends Schema> = {
|
|
@@ -449,6 +460,7 @@ export declare function range(value: readonly [number, number], options: FieldMe
|
|
|
449
460
|
folded?: boolean;
|
|
450
461
|
sectionFolded?: boolean;
|
|
451
462
|
tab?: string;
|
|
463
|
+
pinned?: boolean;
|
|
452
464
|
subsection?: string;
|
|
453
465
|
};
|
|
454
466
|
/** Degrees on a dial (0–360 and wrapping by default): `angle(45)`, `angle(30, { min: -90, max: 90 })`. */
|
|
@@ -348,6 +348,12 @@ export interface ControlsOptions<S extends Schema> extends LabelOptions {
|
|
|
348
348
|
/** An undo stack the host owns; undo and redo call it (see `HostHistory`). */
|
|
349
349
|
history?: HostHistory;
|
|
350
350
|
presets?: Readonly<Record<string, Partial<Values<S>>>>;
|
|
351
|
+
/**
|
|
352
|
+
* Groups pinned to the top of a panel, under the header and above the tab strip, on every tab
|
|
353
|
+
* (as fields' `pinned: true` pins theirs). Presets and saved variants leave them alone: applying
|
|
354
|
+
* one keeps their values, saving one keeps them out, and they're modified against their defaults.
|
|
355
|
+
*/
|
|
356
|
+
pinned?: readonly string[];
|
|
351
357
|
/**
|
|
352
358
|
* Numbered presets per group, shown as 1, 2, 3… on the group's card in a page. A group's
|
|
353
359
|
* presets cover every key any of them names; keys a preset leaves out are at their defaults,
|
|
@@ -426,6 +432,8 @@ export interface Controls<S extends Schema = Schema> {
|
|
|
426
432
|
readonly sectionFolded: SectionFolded | undefined;
|
|
427
433
|
/** A preset's display label (`presetLabels`, `defaultLabel`), else its id. */
|
|
428
434
|
presetLabel(id: string): string;
|
|
435
|
+
/** Whether `key` is pinned (its group is, see `ControlsOptions.pinned`), outside the presets. */
|
|
436
|
+
isPinned(key: string): boolean;
|
|
429
437
|
readonly error: string | null;
|
|
430
438
|
get(): Values<S>;
|
|
431
439
|
getServerSnapshot(): Values<S>;
|
|
@@ -407,6 +407,8 @@ export interface GpuPanelBinding {
|
|
|
407
407
|
readonly id: string;
|
|
408
408
|
readonly root: HTMLElement;
|
|
409
409
|
readonly body: HTMLElement;
|
|
410
|
+
/** The pinned groups' cards, above the tab strip (empty while none are pinned). */
|
|
411
|
+
readonly pinned?: HTMLElement;
|
|
410
412
|
readonly inline: boolean;
|
|
411
413
|
readonly header: HTMLElement;
|
|
412
414
|
readonly titleEl: HTMLElement;
|
|
@@ -28,6 +28,8 @@ export declare function activeEntryId(controls: Controls<any>): string | null;
|
|
|
28
28
|
* cycling never gets stuck when a saved variant has the exact same values as a built-in preset.
|
|
29
29
|
*/
|
|
30
30
|
export declare function resolveActivePresetEntry<V extends Record<string, any>>(entries: readonly PresetEntry<V>[], currentValues: V, lastAppliedId: string | null, baseId?: string | null): ActivePresetState;
|
|
31
|
+
/** Whether any entry but the defaults gives one of `keys` a value other than its default. */
|
|
32
|
+
export declare function presetsTouch(entries: readonly PresetEntry<any>[], keys: readonly string[], defaults: Readonly<Record<string, unknown>>): boolean;
|
|
31
33
|
export declare function nextPresetIndex(count: number, activeIndex: number, direction: number): number;
|
|
32
34
|
export interface PresetStrip {
|
|
33
35
|
readonly element: HTMLElement;
|
|
@@ -52,9 +54,13 @@ export interface PresetStrip {
|
|
|
52
54
|
readonly nameField: () => HTMLInputElement | null;
|
|
53
55
|
/** Deletes the selected chip when it's a saved variant; returns a status message. */
|
|
54
56
|
deleteActive(): string | undefined;
|
|
57
|
+
/** Whether the strip should show: presets to pick (or saving on) that touch what's shown. */
|
|
58
|
+
shows(): boolean;
|
|
55
59
|
update(): void;
|
|
56
60
|
dispose(): void;
|
|
57
61
|
}
|
|
58
62
|
export declare function createPresetStrip<S extends Schema>(doc: Document, controls: Controls<S>, showVariants: boolean, run: (action: () => unknown) => void, onAfterChange?: () => void,
|
|
59
63
|
/** What a preset's chip says (its id stays its identity); `controls.presetLabel` by default. */
|
|
60
|
-
presetLabel?: (id: string) => string
|
|
64
|
+
presetLabel?: (id: string) => string,
|
|
65
|
+
/** Whether these entries matter where the strip is shown (a tab they don't touch hides it). */
|
|
66
|
+
relevant?: (entries: readonly PresetEntry<any>[]) => boolean): PresetStrip;
|