@kitlangton/knobs 0.4.0-next.12 → 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.
- package/README.md +43 -15
- package/dist/dev/{chunk-nwfm1bfj.js → chunk-bejz4qcg.js} +15 -7
- package/dist/dev/{chunk-ab7vfghg.js → chunk-f0e5tkqg.js} +2 -2
- package/dist/dev/{chunk-9qtvxa6k.js → chunk-s7d5a8vb.js} +271 -163
- package/dist/dev/{chunk-7kns3qe6.js → chunk-v2g91mrm.js} +9 -1
- package/dist/dev/index.js +166 -57
- package/dist/index.js +170 -54
- package/dist/panel.js +339 -181
- package/dist/review-react.js +5 -3
- package/dist/review-solid.js +5 -3
- package/dist/review.js +5 -3
- package/dist/types/core/remote.d.ts +5 -1
- package/dist/types/core/schema.d.ts +12 -1
- package/dist/types/core/store.d.ts +62 -12
- package/dist/types/core/variants.d.ts +3 -1
- package/dist/types/panel/gpu/renderer.d.ts +8 -0
- package/dist/types/panel/presets.d.ts +14 -4
- package/dist/types/ui/index.d.ts +1 -1
- package/dist/types/ui/palette.d.ts +15 -0
- package/dist/types/ui/tree-model.d.ts +5 -2
- package/dist/ui-react.js +5 -3
- package/dist/ui-solid.js +5 -3
- package/dist/ui.js +28 -6
- package/package.json +1 -1
package/dist/panel.js
CHANGED
|
@@ -3099,7 +3099,7 @@ ${GRAD_PROPS}
|
|
|
3099
3099
|
|
|
3100
3100
|
/* The chip: a round cog button, or a capsule with a label beside the cog. */
|
|
3101
3101
|
.knobs-chip{position:fixed;z-index:2147480000;display:inline-flex;align-items:center;justify-content:center;gap:5px;width:26px;height:26px;padding:0;border-radius:999px;cursor:pointer;background:var(--_k-bg);border:1px solid transparent;
|
|
3102
|
-
color:var(--_k-fg-2);font-size:var(--_k-text-control);line-height:1;letter-spacing:.01em;white-space:nowrap;--k-chip-edge:var(--_k-border-subtle);box-shadow:0 0 0 var(--k-rim-width) var(--k-chip-edge),0 4px 16px rgba(0,0,0,calc(.35 * var(--_k-shadow-k,1)))
|
|
3102
|
+
color:var(--_k-fg-2);font-size:var(--_k-text-control);line-height:1;letter-spacing:.01em;white-space:nowrap;--k-chip-edge:var(--_k-border-subtle);box-shadow:0 calc(var(--k-rim-width) * -1) 0 var(--_k-rim),0 0 0 var(--k-rim-width) var(--k-chip-edge),0 4px 16px rgba(0,0,0,calc(.35 * var(--_k-shadow-k,1)));transition:color .15s,border-color .15s,transform .2s var(--k-ease),opacity .2s,scale .12s var(--k-ease),${TOKEN_TRANSITIONS};}
|
|
3103
3103
|
.knobs-chip[data-label]{width:auto;padding:0 10px 0 7px}
|
|
3104
3104
|
.knobs-chip .knobs-icon{display:block;flex:none;width:15px;height:15px}
|
|
3105
3105
|
.knobs-chip:hover{color:var(--_k-fg);--k-chip-edge:var(--_k-border-strong)}
|
|
@@ -3126,9 +3126,10 @@ ${GRAD_PROPS}
|
|
|
3126
3126
|
/* Panel material: --k-glass (0 opaque … 1 clearest) thins the surface over a blurred, saturated
|
|
3127
3127
|
backdrop, toned toward the theme (--k-glass-tone dims it under dark panels) so text keeps its
|
|
3128
3128
|
contrast. Its edge is a hairline (--k-edge, --k-rim-width wide) and a hairline of light (--k-rim)
|
|
3129
|
-
catches its top.
|
|
3129
|
+
catches its top. The light rides on the edge (a shadow raised by its width, so only its top
|
|
3130
|
+
shows), not inside the card's 1px border, where it read as a second edge. */
|
|
3130
3131
|
.knobs-panel{-webkit-user-select:none;user-select:none;transition:${TOKEN_TRANSITIONS},--k-armed ${ARMED_T};position:fixed;z-index:2147480001;width:var(--_k-width);max-width:calc(100vw - 16px);max-height:calc(100dvh - 16px);overflow:auto;padding:var(--_k-pad);
|
|
3131
|
-
background:color-mix(in srgb,var(--_k-bg) calc(100% - var(--_k-glass) * 20%),transparent);border:1px solid transparent;border-radius:var(--_k-radius-panel);box-shadow:var(--_k-shadow-overlay),0
|
|
3132
|
+
background:color-mix(in srgb,var(--_k-bg) calc(100% - var(--_k-glass) * 20%),transparent);border:1px solid transparent;border-radius:var(--_k-radius-panel);box-shadow:var(--_k-shadow-overlay),0 calc(var(--k-rim-width) * -1) 0 var(--_k-rim),0 0 0 var(--k-rim-width) var(--ka-edge),var(--ka-glow);scrollbar-width:thin;
|
|
3132
3133
|
/* Armed (focus inside: keys come here), the edge takes the accent and a faint glow lifts it. */
|
|
3133
3134
|
--ka-edge:color-mix(in srgb,var(--_k-accent) calc(var(--k-armed) * var(--k-armed-edge) * 100%),var(--_k-border-subtle));
|
|
3134
3135
|
--ka-glow:0 0 calc(var(--k-armed-glow) * 1px) color-mix(in srgb,var(--_k-accent) calc(var(--k-armed) * var(--k-armed-glow-a) * var(--k-chrome,1) * 100%),transparent);
|
|
@@ -3141,7 +3142,7 @@ ${GRAD_PROPS}
|
|
|
3141
3142
|
.knobs-panel[data-morphing]{background:transparent;border-color:transparent;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;animation:none}
|
|
3142
3143
|
.knobs-morph{position:fixed;left:0;top:0;z-index:2147480001;pointer-events:none;box-sizing:border-box;--p:0;--spec:0;
|
|
3143
3144
|
background:color-mix(in srgb,var(--_k-bg) calc(100% - var(--_k-glass) * 20% * var(--p)),transparent);border:1px solid transparent;
|
|
3144
|
-
box-shadow:0
|
|
3145
|
+
box-shadow:0 calc(var(--k-rim-width) * -1) 0 color-mix(in srgb,var(--_k-rim) calc(var(--p) * 100%),transparent),0 0 0 var(--k-rim-width) var(--_k-border-subtle),inset 0 0 0 var(--k-rim-width) rgb(255 255 255/var(--spec));
|
|
3145
3146
|
--k-backdrop:blur(calc(var(--_k-glass) * 24px)) saturate(calc(1 + var(--_k-glass) * .8)) brightness(calc(1 + (var(--_k-glass-tone) - 1) * var(--_k-glass)));
|
|
3146
3147
|
-webkit-backdrop-filter:var(--k-backdrop);backdrop-filter:var(--k-backdrop)}
|
|
3147
3148
|
.knobs-morph::before,.knobs-morph::after{content:"";position:absolute;inset:-1px;border-radius:inherit;pointer-events:none}
|
|
@@ -3149,7 +3150,7 @@ ${GRAD_PROPS}
|
|
|
3149
3150
|
.knobs-morph::after{box-shadow:var(--_k-shadow-overlay);opacity:var(--p)}
|
|
3150
3151
|
/* Meanwhile the chip is just its label, fading on the shape. */
|
|
3151
3152
|
.knobs-chip.knobs-chip[data-morph]{visibility:visible;pointer-events:auto;transform:none;transition:none;background:transparent;border-color:transparent;box-shadow:none}
|
|
3152
|
-
.knobs-panel.knobs-inline{position:relative;z-index:auto;left:auto!important;top:auto!important;max-width:100%;max-height:none;box-shadow:0
|
|
3153
|
+
.knobs-panel.knobs-inline{position:relative;z-index:auto;left:auto!important;top:auto!important;max-width:100%;max-height:none;box-shadow:0 calc(var(--k-rim-width) * -1) 0 color-mix(in srgb,var(--_k-rim) calc(var(--k-chrome) * 100%),transparent),0 0 0 var(--k-rim-width) color-mix(in srgb,var(--ka-edge) calc(var(--k-chrome) * 100%),transparent),var(--ka-glow);animation:none;
|
|
3153
3154
|
/* --k-chrome 0: an embedded panel sheds its card (background, border, rim) for the host's column,
|
|
3154
3155
|
border width too, so its rows line up with the column's edges. */
|
|
3155
3156
|
border-width:calc(var(--k-chrome) * 1px);
|
|
@@ -3302,9 +3303,16 @@ ${GRAD_PROPS}
|
|
|
3302
3303
|
/* The active preset's modified mark: the values have drifted off it (a row's dot, in its corner). */
|
|
3303
3304
|
.knobs-panel .knobs-preset-chip::after{content:"";position:absolute;top:4px;right:4px;width:4px;height:4px;border-radius:50%;background:var(--_k-modified);opacity:0;transform:scale(.4);pointer-events:none;transition:opacity .16s ease-out,transform .28s cubic-bezier(.34,1.5,.64,1)}
|
|
3304
3305
|
.knobs-panel .knobs-preset-chip[data-drifted]::after{opacity:.95;transform:none}
|
|
3306
|
+
/* Digits that stay on the chips (hints: { digits: "always" }): quiet, in a column of their own. */
|
|
3307
|
+
.knobs-panel .knobs-preset-key{display:inline-block;margin-right:6px;color:var(--_k-fg-3);font-size:var(--_k-text-meta);font-weight:var(--_k-weight-label);font-variant-numeric:tabular-nums}
|
|
3308
|
+
/* A group's own presets at the top of its card: the chips sit on the card, with no tray. */
|
|
3309
|
+
.knobs-panel .knobs-card>.knobs-presets{margin:0;padding:max(4px,calc((var(--_k-row) - var(--k-button-h,var(--_k-control-h))) / 2)) var(--_k-inset);background:transparent;border-radius:0}
|
|
3310
|
+
.knobs-panel .knobs-card>.knobs-presets[data-display="list"]{padding-inline:0}
|
|
3311
|
+
.knobs-card>.knobs-presets:hover{background:transparent}
|
|
3305
3312
|
/* Naming a saved preset: a field over its chip that opens out from the middle to the chip's width;
|
|
3306
3313
|
the chip's own name hides beneath it. Leaving, it fades. */
|
|
3307
3314
|
.knobs-panel .knobs-preset-chip[data-naming]{color:transparent!important}
|
|
3315
|
+
.knobs-panel .knobs-preset-chip[data-naming] .knobs-preset-key{color:transparent}
|
|
3308
3316
|
.knobs-panel .knobs-preset-name{position:absolute;z-index:2;box-sizing:border-box;min-width:0;margin:0;padding:0 6px;border:1px solid color-mix(in srgb,var(--_k-accent) 55%,transparent);border-radius:var(--k-button-radius,var(--_k-radius-md));outline:none;background:color-mix(in srgb,var(--_k-accent) 10%,transparent);color:var(--_k-fg);caret-color:var(--_k-accent);
|
|
3309
3317
|
font-family:inherit;font-size:var(--k-button-font-size,var(--_k-text-control));font-weight:var(--_k-weight-title);letter-spacing:inherit;text-align:center;animation:knobs-name-in .25s cubic-bezier(.2,.8,.2,1);transition:opacity .11s cubic-bezier(.4,0,1,1)}
|
|
3310
3318
|
.knobs-preset-name::selection{background:color-mix(in srgb,var(--_k-accent) 24%,transparent);color:var(--_k-fg)}
|
|
@@ -3915,7 +3923,7 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
|
|
|
3915
3923
|
.knobs-panel[data-gpu-active="true"] :where(header strong,header button,.knobs-tile,.knobs-section-head,.knobs-group-label,.knobs-peek,.knobs-card>*,.knobs-presets,.knobs-preset-chip,.knobs-group-head,.knobs-group-head button,.knobs-card,.knobs-row,.knobs-row>span:first-child,.knobs-slider,.knobs-track,.knobs-label,.knobs-value,.knobs-digits,.knobs-digits *,.knobs-unit,.knobs-seg,.knobs-seg button,.knobs-custom-actions button,.knobs-action,.knobs-select,.knobs-select-value,
|
|
3916
3924
|
.knobs-sub,.knobs-x-head,.knobs-x-label,.knobs-x-read,.knobs-strip-label,.knobs-strip-read,.knobs-pad,.knobs-ease,.knobs-spring-curve,.knobs-ease-chips,.knobs-ease-chip,.knobs-graph,.knobs-graph-read,
|
|
3917
3925
|
.knobs-tabs,.knobs-tab,.knobs-subhead,.knobs-subhead-label,.knobs-match,.knobs-cell,.knobs-cell-axis,.knobs-cell-read,.knobs-link,.knobs-range-read,.knobs-angle,.knobs-angle-read,.knobs-dial,
|
|
3918
|
-
.knobs-seed,.knobs-seed-read,.knobs-seed-digits,.knobs-seed-digits *,.knobs-seed-roll,.knobs-keys,.knobs-keys-text,.knobs-tool,.knobs-tool-label,.knobs-tool-key,
|
|
3926
|
+
.knobs-seed,.knobs-seed-read,.knobs-seed-digits,.knobs-seed-digits *,.knobs-seed-roll,.knobs-keys,.knobs-keys-text,.knobs-tool,.knobs-tool-label,.knobs-tool-key,.knobs-preset-key,
|
|
3919
3927
|
.knobs-section-name,.knobs-section-summary,.knobs-section-desc,.knobs-group-desc,.knobs-group-summary,.knobs-tab-count):not(.knobs-live,.knobs-live *){
|
|
3920
3928
|
background:transparent!important;color:transparent!important;border-color:transparent!important;box-shadow:none!important;
|
|
3921
3929
|
}
|
|
@@ -6955,8 +6963,8 @@ var init_swipe = __esm(() => {
|
|
|
6955
6963
|
});
|
|
6956
6964
|
|
|
6957
6965
|
// src/panel/presets.ts
|
|
6958
|
-
function activeEntryId(controls) {
|
|
6959
|
-
const ref = controls.activePreset();
|
|
6966
|
+
function activeEntryId(controls, scope = "") {
|
|
6967
|
+
const ref = controls.activePreset(scope);
|
|
6960
6968
|
return ref ? `${ref.kind === "preset" ? "p" : "v"}:${ref.id}` : null;
|
|
6961
6969
|
}
|
|
6962
6970
|
function resolveActivePresetEntry(entries, currentValues, lastAppliedId, baseId = null) {
|
|
@@ -7009,7 +7017,7 @@ function nextPresetIndex(count, activeIndex, direction) {
|
|
|
7009
7017
|
return direction > 0 ? 0 : count - 1;
|
|
7010
7018
|
return ((activeIndex + direction) % count + count) % count;
|
|
7011
7019
|
}
|
|
7012
|
-
function createPresetStrip(doc, controls, showVariants, run, onAfterChange, presetLabel = (id) => controls.presetLabel(id), relevant) {
|
|
7020
|
+
function createPresetStrip(doc, controls, showVariants, run, onAfterChange, presetLabel = (id, scope) => controls.presetLabel(id, scope), relevant, scopeOf = () => "") {
|
|
7013
7021
|
const wake = () => onAfterChange?.();
|
|
7014
7022
|
const el = (tag, cls, text) => {
|
|
7015
7023
|
const node = doc.createElement(tag);
|
|
@@ -7056,35 +7064,43 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7056
7064
|
let initializedPill = false;
|
|
7057
7065
|
let slideUntil = 0;
|
|
7058
7066
|
let display = "auto";
|
|
7067
|
+
let digits = false;
|
|
7059
7068
|
let chipMin = 0;
|
|
7069
|
+
let scope = null;
|
|
7070
|
+
const at = () => scope ?? "";
|
|
7071
|
+
const source = () => scope === null ? undefined : scope === "" ? { presets: controls.presets, variants: controls.variants } : controls.scopes[scope];
|
|
7060
7072
|
const unpinned = (values) => {
|
|
7061
7073
|
let out;
|
|
7062
7074
|
for (const k of Object.keys(values))
|
|
7063
|
-
if (controls.
|
|
7075
|
+
if (controls.scopeOf(k) !== at() && !same2(values[k], controls.locked[k])) {
|
|
7064
7076
|
out ??= { ...values };
|
|
7065
7077
|
out[k] = controls.locked[k];
|
|
7066
7078
|
}
|
|
7067
7079
|
return out ?? values;
|
|
7068
7080
|
};
|
|
7069
7081
|
function buildEntries() {
|
|
7070
|
-
const
|
|
7082
|
+
const from = source();
|
|
7083
|
+
if (!from)
|
|
7084
|
+
return [];
|
|
7085
|
+
const where = at();
|
|
7086
|
+
const presetEntries = Object.entries(from.presets).map(([id, presetValues]) => ({
|
|
7071
7087
|
id: `p:${id}`,
|
|
7072
7088
|
kind: "preset",
|
|
7073
7089
|
rawId: id,
|
|
7074
|
-
label: presetLabel(id),
|
|
7090
|
+
label: presetLabel(id, where),
|
|
7075
7091
|
values: presetValues,
|
|
7076
|
-
apply: (coalesce) => controls.preset(id, { coalesce })
|
|
7092
|
+
apply: (coalesce) => controls.preset(id, { coalesce, scope: where })
|
|
7077
7093
|
}));
|
|
7078
7094
|
if (!showVariants)
|
|
7079
7095
|
return presetEntries;
|
|
7080
|
-
const variants =
|
|
7096
|
+
const variants = from.variants.list();
|
|
7081
7097
|
const variantEntries = variants.map((v) => ({
|
|
7082
7098
|
id: `v:${v.id}`,
|
|
7083
7099
|
kind: "variant",
|
|
7084
7100
|
rawId: v.id,
|
|
7085
7101
|
label: v.name ?? v.id.slice(0, 8),
|
|
7086
7102
|
values: unpinned(v.values),
|
|
7087
|
-
apply: (coalesce) =>
|
|
7103
|
+
apply: (coalesce) => from.variants.load(v.id, { coalesce })
|
|
7088
7104
|
}));
|
|
7089
7105
|
return [...presetEntries, ...variantEntries];
|
|
7090
7106
|
}
|
|
@@ -7135,8 +7151,8 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7135
7151
|
if (first)
|
|
7136
7152
|
doc.defaultView?.requestAnimationFrame(() => pill.style.transition = "");
|
|
7137
7153
|
}
|
|
7138
|
-
const resolve = () => resolveActivePresetEntry(entries, unpinned(controls.get()), lastAppliedId, activeEntryId(controls));
|
|
7139
|
-
const wanted = (list) => (list.length > 1 || showVariants) && (relevant?.(list) ?? true);
|
|
7154
|
+
const resolve = () => resolveActivePresetEntry(entries, unpinned(controls.get()), lastAppliedId, activeEntryId(controls, at()));
|
|
7155
|
+
const wanted = (list) => scope !== null && (list.length > 1 || showVariants) && (relevant?.(list) ?? true);
|
|
7140
7156
|
function syncShown() {
|
|
7141
7157
|
const show = wanted(entries);
|
|
7142
7158
|
if (root.hidden === show && root.dataset.presence === undefined)
|
|
@@ -7196,22 +7212,33 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7196
7212
|
}
|
|
7197
7213
|
function update() {
|
|
7198
7214
|
run(() => {
|
|
7199
|
-
const
|
|
7200
|
-
|
|
7215
|
+
const was = scope;
|
|
7216
|
+
scope = scopeOf();
|
|
7217
|
+
if (scope !== was)
|
|
7218
|
+
lastAppliedId = null;
|
|
7219
|
+
const variantsRef = showVariants ? source()?.variants.list() ?? null : null;
|
|
7220
|
+
if (signature !== "" && scope === was && variantsRef === lastVariantsRef) {
|
|
7201
7221
|
syncShown();
|
|
7202
7222
|
syncSelection(false);
|
|
7203
7223
|
return;
|
|
7204
7224
|
}
|
|
7205
7225
|
lastVariantsRef = variantsRef;
|
|
7206
7226
|
const nextEntries = buildEntries();
|
|
7207
|
-
const nextSig = JSON.stringify(nextEntries.map((e) => [e.id, e.label]));
|
|
7227
|
+
const nextSig = JSON.stringify([scope, digits, nextEntries.map((e) => [e.id, e.label])]);
|
|
7208
7228
|
entries = nextEntries;
|
|
7209
7229
|
syncShown();
|
|
7210
7230
|
if (nextSig !== signature) {
|
|
7211
7231
|
signature = nextSig;
|
|
7212
7232
|
entries = nextEntries;
|
|
7213
7233
|
buttons = entries.map((entry, idx) => {
|
|
7214
|
-
const b = el("button", "knobs-preset-chip"
|
|
7234
|
+
const b = el("button", "knobs-preset-chip");
|
|
7235
|
+
if (digits && idx < 9) {
|
|
7236
|
+
const key = el("span", "knobs-preset-key", String(idx + 1));
|
|
7237
|
+
key.setAttribute("aria-hidden", "true");
|
|
7238
|
+
b.append(key, el("span", "knobs-preset-label", entry.label));
|
|
7239
|
+
b.setAttribute("aria-keyshortcuts", String(idx + 1));
|
|
7240
|
+
} else
|
|
7241
|
+
b.textContent = entry.label;
|
|
7215
7242
|
b.type = "button";
|
|
7216
7243
|
b.tabIndex = -1;
|
|
7217
7244
|
b.dataset.presetId = entry.id;
|
|
@@ -7269,12 +7296,15 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7269
7296
|
}
|
|
7270
7297
|
function save() {
|
|
7271
7298
|
finishName(true);
|
|
7272
|
-
const
|
|
7299
|
+
const variants = source()?.variants;
|
|
7300
|
+
if (!variants)
|
|
7301
|
+
return "";
|
|
7302
|
+
const used = new Set(variants.list().map((v) => v.name));
|
|
7273
7303
|
let n = 1;
|
|
7274
7304
|
while (used.has(`Variant ${n}`))
|
|
7275
7305
|
n++;
|
|
7276
|
-
const variant =
|
|
7277
|
-
|
|
7306
|
+
const variant = variants.save(`Variant ${n}`);
|
|
7307
|
+
variants.load(variant.id);
|
|
7278
7308
|
lastAppliedId = `v:${variant.id}`;
|
|
7279
7309
|
update();
|
|
7280
7310
|
startName(entries.findIndex((e) => e.id === `v:${variant.id}`), true);
|
|
@@ -7349,7 +7379,7 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7349
7379
|
focusQuietly();
|
|
7350
7380
|
let message;
|
|
7351
7381
|
if (keep && entry?.kind === "variant" && name && name !== was) {
|
|
7352
|
-
|
|
7382
|
+
source()?.variants.rename(entry.rawId, name);
|
|
7353
7383
|
update();
|
|
7354
7384
|
message = `Named “${name}”.`;
|
|
7355
7385
|
}
|
|
@@ -7370,7 +7400,7 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7370
7400
|
const entry = entries[activeState.activeIndex];
|
|
7371
7401
|
if (!entry || entry.kind !== "variant")
|
|
7372
7402
|
return;
|
|
7373
|
-
|
|
7403
|
+
source()?.variants.remove(entry.rawId);
|
|
7374
7404
|
lastAppliedId = null;
|
|
7375
7405
|
update();
|
|
7376
7406
|
onAfterChange?.();
|
|
@@ -7506,6 +7536,16 @@ function createPresetStrip(doc, controls, showVariants, run, onAfterChange, pres
|
|
|
7506
7536
|
if (placeDisplay())
|
|
7507
7537
|
measurePill(true);
|
|
7508
7538
|
},
|
|
7539
|
+
digits(on) {
|
|
7540
|
+
if (digits === on)
|
|
7541
|
+
return;
|
|
7542
|
+
digits = on;
|
|
7543
|
+
root.toggleAttribute("data-digits", on);
|
|
7544
|
+
if (signature === "")
|
|
7545
|
+
return;
|
|
7546
|
+
signature = "";
|
|
7547
|
+
update();
|
|
7548
|
+
},
|
|
7509
7549
|
save: () => {
|
|
7510
7550
|
let message = "";
|
|
7511
7551
|
run(() => message = save());
|
|
@@ -9459,7 +9499,8 @@ fn hash21(q: vec2f) -> f32 {
|
|
|
9459
9499
|
|
|
9460
9500
|
// kind 0: Panel card surface + elevation shadow + border + specular rim. With a hairline width
|
|
9461
9501
|
// (extra.z, whole device px) the edge is that hairline just outside the card, like CSS's
|
|
9462
|
-
// 0 0 0 w spread shadow, and the rim
|
|
9502
|
+
// 0 0 0 w spread shadow, and the rim lights its top, like CSS's 0 -w 0 (one edge, not two);
|
|
9503
|
+
// otherwise a 1px border and a 1px rim inside it.
|
|
9463
9504
|
fn shadePanel(p: Prim, local: vec2f, size: vec2f) -> vec4f {
|
|
9464
9505
|
let r = p.look.x;
|
|
9465
9506
|
let dist = roundedBox(local, size, r);
|
|
@@ -9468,6 +9509,8 @@ fn shadePanel(p: Prim, local: vec2f, size: vec2f) -> vec4f {
|
|
|
9468
9509
|
var out = vec4f(0.0);
|
|
9469
9510
|
if (hair > 0.0 && dist > -1.0) {
|
|
9470
9511
|
out = premul(p.color2) * clamp(covSd(dist - hair) - bodyCov, 0.0, 1.0);
|
|
9512
|
+
let raised = covSd(roundedBox(local + vec2f(0.0, hair), size, r));
|
|
9513
|
+
out = over(out, premul(p.aux2) * clamp(raised - bodyCov, 0.0, 1.0));
|
|
9471
9514
|
}
|
|
9472
9515
|
|
|
9473
9516
|
// Floating drop shadow when glowOrBlur (look.z) > 0; like box-shadow, never under the card.
|
|
@@ -9481,13 +9524,16 @@ fn shadePanel(p: Prim, local: vec2f, size: vec2f) -> vec4f {
|
|
|
9481
9524
|
|
|
9482
9525
|
var card = premul(p.color) * bodyCov;
|
|
9483
9526
|
|
|
9484
|
-
//
|
|
9527
|
+
// Without a hairline, the rim is CSS's inset 0 1px 0: inside the border, minus that shape
|
|
9528
|
+
// moved down.
|
|
9485
9529
|
let inner = local - vec2f(1.0);
|
|
9486
9530
|
let innerSize = size - vec2f(2.0);
|
|
9487
9531
|
let w = select(1.0, hair, hair > 0.0);
|
|
9488
9532
|
let innerCov = covSd(roundedBox(inner, innerSize, r - 1.0));
|
|
9489
|
-
|
|
9490
|
-
|
|
9533
|
+
if (hair <= 0.0) {
|
|
9534
|
+
let rimCov = innerCov * (1.0 - covSd(roundedBox(inner - vec2f(0.0, w), innerSize, r - 1.0)));
|
|
9535
|
+
card = over(card, premul(p.aux2) * rimCov);
|
|
9536
|
+
}
|
|
9491
9537
|
|
|
9492
9538
|
// A morphing card's specular ring (extra.y), w wide inside the border like CSS's inset 0 0 0 w.
|
|
9493
9539
|
if (p.extra.y > 0.001) {
|
|
@@ -10617,6 +10663,135 @@ class GpuOverlayManager {
|
|
|
10617
10663
|
d[b + 42] = more ? more[2] : 0;
|
|
10618
10664
|
d[b + 43] = more ? more[3] : 0;
|
|
10619
10665
|
}
|
|
10666
|
+
drawPresetStrip(ps, tokens, clip, hinting, reducedMotion, dt, tray) {
|
|
10667
|
+
let moving = false;
|
|
10668
|
+
const psHovered = this.curPanelHovered && ps.element.matches(":hover");
|
|
10669
|
+
this.curSubHovered = psHovered;
|
|
10670
|
+
const psRect = this.rectOf(ps.element);
|
|
10671
|
+
const trackRect = this.rectOf(ps.track);
|
|
10672
|
+
if (tray && !tokens.boxed)
|
|
10673
|
+
this.pushPrim({
|
|
10674
|
+
x: psRect.left,
|
|
10675
|
+
y: psRect.top,
|
|
10676
|
+
w: psRect.width,
|
|
10677
|
+
h: psRect.height,
|
|
10678
|
+
color: tokens.segBg,
|
|
10679
|
+
color2: tokens.accent,
|
|
10680
|
+
radius: tokens.radius,
|
|
10681
|
+
glowOrBlur: tokens.glowStrength * 8,
|
|
10682
|
+
kind: 1,
|
|
10683
|
+
p0: psHovered || this.isFocused(ps.element) ? 0.4 : 0
|
|
10684
|
+
});
|
|
10685
|
+
const stripClip = intersectClip(clip, [psRect.left, psRect.top, psRect.right, psRect.bottom]);
|
|
10686
|
+
const activeState = ps.getActiveState();
|
|
10687
|
+
const chipButtons = ps.getButtons();
|
|
10688
|
+
if (tokens.boxed)
|
|
10689
|
+
for (const btn of chipButtons) {
|
|
10690
|
+
const br = this.rectOf(btn);
|
|
10691
|
+
if (br.right < stripClip[0] || br.left > stripClip[2])
|
|
10692
|
+
continue;
|
|
10693
|
+
this.drawButtonBox(btn, br, this.isHovered(btn), this.missed(btn, hinting), stripClip);
|
|
10694
|
+
}
|
|
10695
|
+
const activeBtn = activeState.activeIndex >= 0 ? chipButtons[activeState.activeIndex] : undefined;
|
|
10696
|
+
const pe = ps.pillEdges;
|
|
10697
|
+
const activeBtnRect = activeBtn ? this.rectOf(activeBtn) : undefined;
|
|
10698
|
+
const panned = livePill(ps.track);
|
|
10699
|
+
if (activeBtn && activeBtnRect && !activeState.isCustom) {
|
|
10700
|
+
const left = activeBtnRect.left - trackRect.left;
|
|
10701
|
+
const right = left + Math.max(20, activeBtnRect.width);
|
|
10702
|
+
const top = activeBtnRect.top - trackRect.top;
|
|
10703
|
+
this.carry(pe, activeBtn, left, right, ps.pillY, top);
|
|
10704
|
+
if (panned) {
|
|
10705
|
+
pe.snap(panned.left, panned.right);
|
|
10706
|
+
ps.pillY.snap(panned.top);
|
|
10707
|
+
} else if (reducedMotion || pe.end.x - pe.start.x <= 1) {
|
|
10708
|
+
pe.snap(left, right);
|
|
10709
|
+
ps.pillY.snap(top);
|
|
10710
|
+
} else {
|
|
10711
|
+
pe.to(left, right, 0, trailScale(tokens.blurStrength));
|
|
10712
|
+
ps.pillY.set(top);
|
|
10713
|
+
}
|
|
10714
|
+
ps.pillAlpha.set(activeState.drifted ? DRIFTED_PILL : 1);
|
|
10715
|
+
} else {
|
|
10716
|
+
ps.pillAlpha.set(0);
|
|
10717
|
+
}
|
|
10718
|
+
for (const sp of [pe.start, pe.end, ps.pillY, ps.pillAlpha]) {
|
|
10719
|
+
if (reducedMotion)
|
|
10720
|
+
sp.snap(sp.target);
|
|
10721
|
+
else {
|
|
10722
|
+
sp.step(dt);
|
|
10723
|
+
if (!sp.resting)
|
|
10724
|
+
moving = true;
|
|
10725
|
+
}
|
|
10726
|
+
}
|
|
10727
|
+
const pillAlpha = clamp2(ps.pillAlpha.x, 0, 1);
|
|
10728
|
+
if (pillAlpha > 0.01) {
|
|
10729
|
+
this.pushPrim({
|
|
10730
|
+
x: trackRect.left + pe.start.x,
|
|
10731
|
+
y: trackRect.top + ps.pillY.x,
|
|
10732
|
+
w: Math.max(18, pe.end.x - pe.start.x),
|
|
10733
|
+
h: Math.max(16, activeBtnRect?.height ?? trackRect.height),
|
|
10734
|
+
clip: stripClip,
|
|
10735
|
+
color: withAlpha2(tokens.pillBg, pillAlpha),
|
|
10736
|
+
color2: withAlpha2(tokens.pillBorder, pillAlpha),
|
|
10737
|
+
strokeWidth: this.hairline(tokens),
|
|
10738
|
+
aux2: withAlpha2(tokens.rim, tokens.rim[3] * pillAlpha),
|
|
10739
|
+
radius: tokens.buttonRadius ?? tokens.radiusMd,
|
|
10740
|
+
glowOrBlur: tokens.glowStrength,
|
|
10741
|
+
kind: 3,
|
|
10742
|
+
blurX: reducedMotion ? 0 : clamp2(pe.velocity * 0.006 * tokens.motionBlur, -8, 8)
|
|
10743
|
+
});
|
|
10744
|
+
}
|
|
10745
|
+
for (let i = 0;i < chipButtons.length; i++) {
|
|
10746
|
+
const btn = chipButtons[i];
|
|
10747
|
+
const br = this.rectOf(btn);
|
|
10748
|
+
if (br.right < stripClip[0] || br.left > stripClip[2])
|
|
10749
|
+
continue;
|
|
10750
|
+
const isActive = i === activeState.activeIndex;
|
|
10751
|
+
const chipHovered = this.isHovered(btn);
|
|
10752
|
+
if (chipHovered)
|
|
10753
|
+
this.curSubHovered = false;
|
|
10754
|
+
const mark = this.ease(btn, "dot", btn.hasAttribute("data-drifted") ? 1 : 0);
|
|
10755
|
+
if (mark > 0.01) {
|
|
10756
|
+
const size = 4 * (0.4 + 0.6 * mark);
|
|
10757
|
+
this.pushPrim({
|
|
10758
|
+
x: br.right - 6 - size / 2,
|
|
10759
|
+
y: br.top + 6 - size / 2,
|
|
10760
|
+
w: size,
|
|
10761
|
+
h: size,
|
|
10762
|
+
clip: stripClip,
|
|
10763
|
+
color: withAlpha2(tokens.modified, 0.95 * mark),
|
|
10764
|
+
glowOrBlur: 0.5,
|
|
10765
|
+
kind: 7
|
|
10766
|
+
});
|
|
10767
|
+
}
|
|
10768
|
+
if (btn.hasAttribute("data-naming"))
|
|
10769
|
+
continue;
|
|
10770
|
+
const key = btn.querySelector(".knobs-preset-key");
|
|
10771
|
+
const name = key && btn.querySelector(".knobs-preset-label");
|
|
10772
|
+
if (key && name) {
|
|
10773
|
+
const kr = this.rectOf(key);
|
|
10774
|
+
this.drawText(key.textContent ?? "", kr.left, br.top + br.height * 0.5, withAlpha2(tokens.dim, this.missed(btn, hinting)), { px: tokens.textMeta, weight: tokens.labelWeight, clip: stripClip });
|
|
10775
|
+
}
|
|
10776
|
+
const nr = name ? this.rectOf(name) : undefined;
|
|
10777
|
+
this.drawText(name ? name.textContent ?? "" : btn.textContent ?? "", nr ? nr.left : br.left + br.width * 0.5, br.top + br.height * 0.5, withAlpha2(this.hot(btn, isActive || chipHovered, tokens.buttonColor ?? tokens.label), this.missed(btn, hinting)), {
|
|
10778
|
+
px: tokens.buttonFontSize ?? tokens.textControl,
|
|
10779
|
+
weight: isActive ? tokens.titleWeight : tokens.labelWeight,
|
|
10780
|
+
align: nr ? "left" : "center",
|
|
10781
|
+
clip: stripClip
|
|
10782
|
+
});
|
|
10783
|
+
}
|
|
10784
|
+
if (ps.saveButton && !ps.saveButton.hidden) {
|
|
10785
|
+
const sr = this.rectOf(ps.saveButton);
|
|
10786
|
+
const hot = this.isHovered(ps.saveButton);
|
|
10787
|
+
this.drawText("+", sr.left + sr.width * 0.5, sr.top + sr.height * 0.5, this.hot(ps.saveButton, hot, tokens.dim), {
|
|
10788
|
+
px: tokens.textTitle,
|
|
10789
|
+
weight: 400,
|
|
10790
|
+
align: "center"
|
|
10791
|
+
});
|
|
10792
|
+
}
|
|
10793
|
+
return moving;
|
|
10794
|
+
}
|
|
10620
10795
|
hairline(tokens) {
|
|
10621
10796
|
return Math.max(1, Math.round(tokens.rimWidth * this.curScale)) / this.curScale;
|
|
10622
10797
|
}
|
|
@@ -12830,7 +13005,6 @@ class GpuOverlayManager {
|
|
|
12830
13005
|
this.placeBackdrop(panel);
|
|
12831
13006
|
continue;
|
|
12832
13007
|
}
|
|
12833
|
-
const pillFill = tokens.pillBg;
|
|
12834
13008
|
if (pageB)
|
|
12835
13009
|
this.placeBackdrop(panel);
|
|
12836
13010
|
const covers = drawn.some((r) => r.left < card.right && r.right > card.left && r.top < card.bottom && r.bottom > card.top);
|
|
@@ -12998,129 +13172,7 @@ class GpuOverlayManager {
|
|
|
12998
13172
|
const stripAlpha = this.curAlpha;
|
|
12999
13173
|
if (!ps.element.hidden) {
|
|
13000
13174
|
this.curAlpha = stripAlpha * this.presence(ps.element);
|
|
13001
|
-
|
|
13002
|
-
this.curSubHovered = psHovered;
|
|
13003
|
-
const psRect = this.rectOf(ps.element);
|
|
13004
|
-
const trackRect = this.rectOf(ps.track);
|
|
13005
|
-
if (!tokens.boxed)
|
|
13006
|
-
this.pushPrim({
|
|
13007
|
-
x: psRect.left,
|
|
13008
|
-
y: psRect.top,
|
|
13009
|
-
w: psRect.width,
|
|
13010
|
-
h: psRect.height,
|
|
13011
|
-
color: tokens.segBg,
|
|
13012
|
-
color2: tokens.accent,
|
|
13013
|
-
radius: tokens.radius,
|
|
13014
|
-
glowOrBlur: tokens.glowStrength * 8,
|
|
13015
|
-
kind: 1,
|
|
13016
|
-
p0: psHovered || this.isFocused(ps.element) ? 0.4 : 0
|
|
13017
|
-
});
|
|
13018
|
-
const stripClip = intersectClip(bodyClip, [
|
|
13019
|
-
psRect.left,
|
|
13020
|
-
psRect.top,
|
|
13021
|
-
psRect.right,
|
|
13022
|
-
psRect.bottom
|
|
13023
|
-
]);
|
|
13024
|
-
const activeState = ps.getActiveState();
|
|
13025
|
-
const chipButtons = ps.getButtons();
|
|
13026
|
-
if (tokens.boxed)
|
|
13027
|
-
for (const btn of chipButtons) {
|
|
13028
|
-
const br = this.rectOf(btn);
|
|
13029
|
-
if (br.right < stripClip[0] || br.left > stripClip[2])
|
|
13030
|
-
continue;
|
|
13031
|
-
this.drawButtonBox(btn, br, this.isHovered(btn), this.missed(btn, hinting), stripClip);
|
|
13032
|
-
}
|
|
13033
|
-
const activeBtn = activeState.activeIndex >= 0 ? chipButtons[activeState.activeIndex] : undefined;
|
|
13034
|
-
const pe = ps.pillEdges;
|
|
13035
|
-
const activeBtnRect = activeBtn ? this.rectOf(activeBtn) : undefined;
|
|
13036
|
-
const panned = livePill(ps.track);
|
|
13037
|
-
if (activeBtn && activeBtnRect && !activeState.isCustom) {
|
|
13038
|
-
const left = activeBtnRect.left - trackRect.left;
|
|
13039
|
-
const right = left + Math.max(20, activeBtnRect.width);
|
|
13040
|
-
const top = activeBtnRect.top - trackRect.top;
|
|
13041
|
-
this.carry(pe, activeBtn, left, right, ps.pillY, top);
|
|
13042
|
-
if (panned) {
|
|
13043
|
-
pe.snap(panned.left, panned.right);
|
|
13044
|
-
ps.pillY.snap(panned.top);
|
|
13045
|
-
} else if (reducedMotion || pe.end.x - pe.start.x <= 1) {
|
|
13046
|
-
pe.snap(left, right);
|
|
13047
|
-
ps.pillY.snap(top);
|
|
13048
|
-
} else {
|
|
13049
|
-
pe.to(left, right, 0, trailScale(tokens.blurStrength));
|
|
13050
|
-
ps.pillY.set(top);
|
|
13051
|
-
}
|
|
13052
|
-
ps.pillAlpha.set(activeState.drifted ? DRIFTED_PILL : 1);
|
|
13053
|
-
} else {
|
|
13054
|
-
ps.pillAlpha.set(0);
|
|
13055
|
-
}
|
|
13056
|
-
for (const sp of [pe.start, pe.end, ps.pillY, ps.pillAlpha]) {
|
|
13057
|
-
if (reducedMotion)
|
|
13058
|
-
sp.snap(sp.target);
|
|
13059
|
-
else {
|
|
13060
|
-
sp.step(dt);
|
|
13061
|
-
if (!sp.resting)
|
|
13062
|
-
anyMoving = true;
|
|
13063
|
-
}
|
|
13064
|
-
}
|
|
13065
|
-
const pillAlpha = clamp2(ps.pillAlpha.x, 0, 1);
|
|
13066
|
-
if (pillAlpha > 0.01) {
|
|
13067
|
-
this.pushPrim({
|
|
13068
|
-
x: trackRect.left + pe.start.x,
|
|
13069
|
-
y: trackRect.top + ps.pillY.x,
|
|
13070
|
-
w: Math.max(18, pe.end.x - pe.start.x),
|
|
13071
|
-
h: Math.max(16, activeBtnRect?.height ?? trackRect.height),
|
|
13072
|
-
clip: stripClip,
|
|
13073
|
-
color: withAlpha2(pillFill, pillAlpha),
|
|
13074
|
-
color2: withAlpha2(tokens.pillBorder, pillAlpha),
|
|
13075
|
-
strokeWidth: this.hairline(tokens),
|
|
13076
|
-
aux2: withAlpha2(tokens.rim, tokens.rim[3] * pillAlpha),
|
|
13077
|
-
radius: tokens.buttonRadius ?? tokens.radiusMd,
|
|
13078
|
-
glowOrBlur: tokens.glowStrength,
|
|
13079
|
-
kind: 3,
|
|
13080
|
-
blurX: reducedMotion ? 0 : clamp2(pe.velocity * 0.006 * tokens.motionBlur, -8, 8)
|
|
13081
|
-
});
|
|
13082
|
-
}
|
|
13083
|
-
for (let i = 0;i < chipButtons.length; i++) {
|
|
13084
|
-
const btn = chipButtons[i];
|
|
13085
|
-
const br = this.rectOf(btn);
|
|
13086
|
-
if (br.right < stripClip[0] || br.left > stripClip[2])
|
|
13087
|
-
continue;
|
|
13088
|
-
const isActive = i === activeState.activeIndex;
|
|
13089
|
-
const chipHovered = this.isHovered(btn);
|
|
13090
|
-
if (chipHovered)
|
|
13091
|
-
this.curSubHovered = false;
|
|
13092
|
-
const mark = this.ease(btn, "dot", btn.hasAttribute("data-drifted") ? 1 : 0);
|
|
13093
|
-
if (mark > 0.01) {
|
|
13094
|
-
const size = 4 * (0.4 + 0.6 * mark);
|
|
13095
|
-
this.pushPrim({
|
|
13096
|
-
x: br.right - 6 - size / 2,
|
|
13097
|
-
y: br.top + 6 - size / 2,
|
|
13098
|
-
w: size,
|
|
13099
|
-
h: size,
|
|
13100
|
-
clip: stripClip,
|
|
13101
|
-
color: withAlpha2(tokens.modified, 0.95 * mark),
|
|
13102
|
-
glowOrBlur: 0.5,
|
|
13103
|
-
kind: 7
|
|
13104
|
-
});
|
|
13105
|
-
}
|
|
13106
|
-
if (btn.hasAttribute("data-naming"))
|
|
13107
|
-
continue;
|
|
13108
|
-
this.drawText(btn.textContent ?? "", br.left + br.width * 0.5, br.top + br.height * 0.5, withAlpha2(this.hot(btn, isActive || chipHovered, tokens.buttonColor ?? tokens.label), this.missed(btn, hinting)), {
|
|
13109
|
-
px: tokens.buttonFontSize ?? tokens.textControl,
|
|
13110
|
-
weight: isActive ? tokens.titleWeight : tokens.labelWeight,
|
|
13111
|
-
align: "center",
|
|
13112
|
-
clip: stripClip
|
|
13113
|
-
});
|
|
13114
|
-
}
|
|
13115
|
-
if (ps.saveButton && !ps.saveButton.hidden) {
|
|
13116
|
-
const sr = this.rectOf(ps.saveButton);
|
|
13117
|
-
const hot = this.isHovered(ps.saveButton);
|
|
13118
|
-
this.drawText("+", sr.left + sr.width * 0.5, sr.top + sr.height * 0.5, this.hot(ps.saveButton, hot, tokens.dim), {
|
|
13119
|
-
px: tokens.textTitle,
|
|
13120
|
-
weight: 400,
|
|
13121
|
-
align: "center"
|
|
13122
|
-
});
|
|
13123
|
-
}
|
|
13175
|
+
anyMoving = this.drawPresetStrip(ps, tokens, bodyClip, hinting, reducedMotion, dt, true) || anyMoving;
|
|
13124
13176
|
this.curAlpha = stripAlpha;
|
|
13125
13177
|
}
|
|
13126
13178
|
const tabs = panel.tabs;
|
|
@@ -13377,6 +13429,15 @@ class GpuOverlayManager {
|
|
|
13377
13429
|
this.drawScope(card, cr, tokens.radius, 1);
|
|
13378
13430
|
}
|
|
13379
13431
|
}
|
|
13432
|
+
for (const gs of panel.groupStrips?.() ?? []) {
|
|
13433
|
+
const card = gs.element.parentElement;
|
|
13434
|
+
if (!card || gs.element.closest("[hidden]"))
|
|
13435
|
+
continue;
|
|
13436
|
+
const cr = cardRects.get(card) ?? this.rectOf(card);
|
|
13437
|
+
this.curClip = clipToTile(card, intersectClip(bodyClip, [cr.left, cr.top, cr.right, cr.bottom]));
|
|
13438
|
+
this.curAlpha = panelAlpha * this.presence(card) * fadeOf(card);
|
|
13439
|
+
anyMoving = this.drawPresetStrip(gs, tokens, this.curClip, hinting, reducedMotion, dt, false) || anyMoving;
|
|
13440
|
+
}
|
|
13380
13441
|
this.curClip = bodyClip;
|
|
13381
13442
|
this.curAlpha = panelAlpha;
|
|
13382
13443
|
const washed = new Set;
|
|
@@ -20492,7 +20553,7 @@ function presence(el, o = {}) {
|
|
|
20492
20553
|
});
|
|
20493
20554
|
const tune = (seconds, zeta) => {
|
|
20494
20555
|
const reduced = reducedMotion(win);
|
|
20495
|
-
spring.omega = 2 * Math.PI / (reduced ? FADE.duration : seconds);
|
|
20556
|
+
spring.omega = 2 * Math.PI / (reduced ? Math.min(FADE.duration, seconds) : seconds);
|
|
20496
20557
|
spring.zeta = reduced ? 1 : zeta;
|
|
20497
20558
|
};
|
|
20498
20559
|
render();
|
|
@@ -26046,8 +26107,9 @@ function buildPanel(controls, host, options) {
|
|
|
26046
26107
|
const group = scope === "row" ? rows[rowAt(from)]?.group : scope === "group" ? cardOf(from)?.dataset.group ?? undefined : undefined;
|
|
26047
26108
|
const keys = scope === "row" ? rows[rowAt(from)]?.keys ?? [] : scope === "group" ? cardKeys(cardOf(from)) : scope === "selection" ? selectionKeys() ?? [] : undefined;
|
|
26048
26109
|
const index = group === undefined ? -1 : controls.activeGroupPreset(group);
|
|
26049
|
-
const
|
|
26050
|
-
const
|
|
26110
|
+
const where = (keys?.length ? controls.scopeOf(keys[0]) : stripScope()) ?? null;
|
|
26111
|
+
const ref = where === null ? null : controls.activePreset(where);
|
|
26112
|
+
const name = index >= 0 ? controls.groupPresets[group]?.[index]?.name : ref?.kind === "preset" ? presetLabel(ref.id, where) : ref ? (where === "" ? controls.variants : controls.scopes[where]?.variants)?.list().find((v) => v.id === ref.id)?.name : undefined;
|
|
26051
26113
|
if (!name)
|
|
26052
26114
|
return null;
|
|
26053
26115
|
const off = keys ? keys.some((k) => controls.isModified(k)) : controls.isModified();
|
|
@@ -26200,8 +26262,10 @@ function buildPanel(controls, host, options) {
|
|
|
26200
26262
|
if (helpButton?.hidden)
|
|
26201
26263
|
titleEl.title = `${titleEl.textContent ? `${titleEl.textContent}
|
|
26202
26264
|
` : ""}Press ${show("help")} for keyboard shortcuts`;
|
|
26203
|
-
const presetLabel = (id) => options.presetLabels?.[id] ?? (id === "default" ? options.defaultLabel : undefined) ?? controls.presetLabel(id);
|
|
26204
|
-
const
|
|
26265
|
+
const presetLabel = (id, scope = "") => options.presetLabels?.[id] ?? (id === "default" ? typeof options.defaultLabel === "object" ? options.defaultLabel[scope] : options.defaultLabel : undefined) ?? controls.presetLabel(id, scope);
|
|
26266
|
+
const hintReach = (typeof options.hints === "object" ? options.hints.reach : options.hints) ?? "page";
|
|
26267
|
+
const stripDigitsShown = typeof options.hints === "object" && options.hints.digits === "always";
|
|
26268
|
+
const presetStrip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel, (entries) => presetsTouchTab(entries), () => stripScope());
|
|
26205
26269
|
const tabStrip = createTabStrip(doc, (id, animate) => selectTab(id, animate));
|
|
26206
26270
|
let activeTab;
|
|
26207
26271
|
const searchOptions = typeof options.search === "object" ? options.search : {};
|
|
@@ -26254,6 +26318,27 @@ function buildPanel(controls, host, options) {
|
|
|
26254
26318
|
reveal(header, any, () => wakeGpu());
|
|
26255
26319
|
};
|
|
26256
26320
|
presetStrip.display(page ? "chips" : options.presetDisplay ?? "auto");
|
|
26321
|
+
presetStrip.digits(stripDigitsShown);
|
|
26322
|
+
const groupStrips = new Map;
|
|
26323
|
+
const tabNamed = (id) => (options.tabs ?? []).some((t) => (typeof t === "string" ? t : t.id) === id) || Object.values(controls.schema).some((f) => f.tab === id);
|
|
26324
|
+
const groupStripFor = (group) => {
|
|
26325
|
+
if (page || !Object.hasOwn(controls.scopes, group) || tabNamed(group))
|
|
26326
|
+
return;
|
|
26327
|
+
let strip = groupStrips.get(group);
|
|
26328
|
+
if (!strip) {
|
|
26329
|
+
strip = createPresetStrip(doc, controls, showVariants, run, () => wakeGpu(), presetLabel, undefined, () => stripScope() === group ? null : group);
|
|
26330
|
+
strip.element.setAttribute("aria-label", `${groupTitle(group)} presets`);
|
|
26331
|
+
strip.display(options.presetDisplay ?? "auto");
|
|
26332
|
+
strip.digits(stripDigitsShown);
|
|
26333
|
+
groupStrips.set(group, strip);
|
|
26334
|
+
}
|
|
26335
|
+
return strip;
|
|
26336
|
+
};
|
|
26337
|
+
const updateStrips = () => {
|
|
26338
|
+
presetStrip.update();
|
|
26339
|
+
for (const strip of groupStrips.values())
|
|
26340
|
+
strip.update();
|
|
26341
|
+
};
|
|
26257
26342
|
const allActions = [
|
|
26258
26343
|
...controls.actions ?? [],
|
|
26259
26344
|
...options.actions ?? []
|
|
@@ -26712,7 +26797,7 @@ function buildPanel(controls, host, options) {
|
|
|
26712
26797
|
const specs = [];
|
|
26713
26798
|
const placed = new Set;
|
|
26714
26799
|
for (const [key, field] of entries) {
|
|
26715
|
-
if (field.kind === "choice" && field.variants &&
|
|
26800
|
+
if (field.kind === "choice" && field.variants && controls.inPresets(key))
|
|
26716
26801
|
continue;
|
|
26717
26802
|
const pair = field.kind === "knob" && field.spring ? halves.get(field.spring.id) : undefined;
|
|
26718
26803
|
if (pair?.duration && pair.bounce && field.kind === "knob" && field.spring) {
|
|
@@ -27410,6 +27495,7 @@ function buildPanel(controls, host, options) {
|
|
|
27410
27495
|
const chosen = [];
|
|
27411
27496
|
const pinnedBlocks = new Set;
|
|
27412
27497
|
const usedSubheads = new Set;
|
|
27498
|
+
const stripped = new Set;
|
|
27413
27499
|
for (const run of runs) {
|
|
27414
27500
|
const at = free.findIndex((b) => b.group === run.group);
|
|
27415
27501
|
const block = at >= 0 ? free.splice(at, 1)[0] : newBlock(run.group);
|
|
@@ -27444,7 +27530,10 @@ function buildPanel(controls, host, options) {
|
|
|
27444
27530
|
under = name;
|
|
27445
27531
|
nodes.push(r.row);
|
|
27446
27532
|
}
|
|
27447
|
-
|
|
27533
|
+
const strip = run.group !== undefined && !run.pinned && !stripped.has(run.group) ? groupStripFor(run.group) : undefined;
|
|
27534
|
+
if (strip)
|
|
27535
|
+
stripped.add(run.group);
|
|
27536
|
+
arrange(block.card, strip ? [strip.element, ...nodes] : nodes);
|
|
27448
27537
|
if (animate && at >= 0) {
|
|
27449
27538
|
for (const r of run.rows)
|
|
27450
27539
|
if (entering.has(r))
|
|
@@ -27606,13 +27695,13 @@ function buildPanel(controls, host, options) {
|
|
|
27606
27695
|
return list;
|
|
27607
27696
|
};
|
|
27608
27697
|
function presetsTouchTab(entries) {
|
|
27609
|
-
if (page || activeTab === undefined)
|
|
27698
|
+
if (page || activeTab === undefined || stripScope() !== "")
|
|
27610
27699
|
return true;
|
|
27611
27700
|
const keys = rows.flatMap((r) => {
|
|
27612
27701
|
const tab = tabOf(r);
|
|
27613
|
-
return !pinnedRow(r) && (tab === undefined || tab === activeTab) ? r.keys : [];
|
|
27702
|
+
return !pinnedRow(r) && (tab === undefined || tab === activeTab) ? r.keys.filter((k) => controls.scopeOf(k) === "") : [];
|
|
27614
27703
|
});
|
|
27615
|
-
const v = variantsOf(controls.schema, controls.
|
|
27704
|
+
const v = variantsOf(controls.schema, (key) => !controls.inPresets(key));
|
|
27616
27705
|
if (v) {
|
|
27617
27706
|
const tab = v.field.tab ?? (v.field.group !== undefined ? groupTab(v.field.group) : undefined);
|
|
27618
27707
|
if (tab === undefined || tab === activeTab)
|
|
@@ -27620,6 +27709,22 @@ function buildPanel(controls, host, options) {
|
|
|
27620
27709
|
}
|
|
27621
27710
|
return presetsTouch(entries, keys, controls.locked);
|
|
27622
27711
|
}
|
|
27712
|
+
function stripScope() {
|
|
27713
|
+
const named = Object.keys(controls.scopes);
|
|
27714
|
+
const panelWide = named.length === 0 || Object.keys(controls.presets).length > 1;
|
|
27715
|
+
if (page)
|
|
27716
|
+
return panelWide ? "" : null;
|
|
27717
|
+
if (activeTab === undefined)
|
|
27718
|
+
return panelWide ? "" : named[0] ?? null;
|
|
27719
|
+
if (named.includes(activeTab))
|
|
27720
|
+
return activeTab;
|
|
27721
|
+
const group = named.find((id) => rows.some((r) => r.group === id && tabOf(r) === activeTab));
|
|
27722
|
+
return group ?? (panelWide ? "" : null);
|
|
27723
|
+
}
|
|
27724
|
+
const stripVariants = () => {
|
|
27725
|
+
const scope = stripScope();
|
|
27726
|
+
return (scope ? controls.scopes[scope]?.variants : undefined) ?? controls.variants;
|
|
27727
|
+
};
|
|
27623
27728
|
function syncTabs() {
|
|
27624
27729
|
const list = tabList();
|
|
27625
27730
|
const has = (id) => id !== undefined && list.some((t) => t.id === id);
|
|
@@ -27636,6 +27741,7 @@ function buildPanel(controls, host, options) {
|
|
|
27636
27741
|
writeSession(controls.id, { tab: id });
|
|
27637
27742
|
tabStrip.select(id, animate);
|
|
27638
27743
|
const focused = doc.activeElement;
|
|
27744
|
+
updateStrips();
|
|
27639
27745
|
applyVisibility(animate ? "tab" : "instant");
|
|
27640
27746
|
if (focused && root.contains(focused) && focused.closest("[hidden],[data-leaving]"))
|
|
27641
27747
|
tabStrip.focus({ preventScroll: true, focusVisible: false });
|
|
@@ -27911,7 +28017,7 @@ function buildPanel(controls, host, options) {
|
|
|
27911
28017
|
text.spellcheck = false;
|
|
27912
28018
|
text.setAttribute("aria-label", "Import or copy text");
|
|
27913
28019
|
const apply = button("Apply import", () => {
|
|
27914
|
-
|
|
28020
|
+
stripVariants().import(text.value);
|
|
27915
28021
|
say("Presets imported.");
|
|
27916
28022
|
reveal(text, false);
|
|
27917
28023
|
reveal(apply, false);
|
|
@@ -27957,7 +28063,7 @@ function buildPanel(controls, host, options) {
|
|
|
27957
28063
|
if (showVariants && showFooter) {
|
|
27958
28064
|
const transfer = el("div");
|
|
27959
28065
|
transfer.className = "knobs-transfer";
|
|
27960
|
-
transfer.append(button("Export presets", () => void copy(
|
|
28066
|
+
transfer.append(button("Export presets", () => void copy(stripVariants().export(), false)), button("Import presets", () => {
|
|
27961
28067
|
reveal(text, true);
|
|
27962
28068
|
reveal(apply, true);
|
|
27963
28069
|
text.value = "";
|
|
@@ -28053,6 +28159,7 @@ function buildPanel(controls, host, options) {
|
|
|
28053
28159
|
let shownBase = "";
|
|
28054
28160
|
const baseSignature = () => JSON.stringify([
|
|
28055
28161
|
controls.activePreset(),
|
|
28162
|
+
Object.keys(controls.scopes).map((s) => controls.activePreset(s)),
|
|
28056
28163
|
Object.keys(controls.groupPresets).map((g) => controls.activeGroupPreset(g))
|
|
28057
28164
|
]);
|
|
28058
28165
|
function update() {
|
|
@@ -28081,7 +28188,7 @@ function buildPanel(controls, host, options) {
|
|
|
28081
28188
|
}
|
|
28082
28189
|
const stagger = initialized ? cascade(changedCount) : 0;
|
|
28083
28190
|
const moving = initialized ? rows.filter(changed) : rows;
|
|
28084
|
-
|
|
28191
|
+
updateStrips();
|
|
28085
28192
|
batchMeasures(() => {
|
|
28086
28193
|
for (const row of moving) {
|
|
28087
28194
|
row.slider?.prime();
|
|
@@ -28251,14 +28358,15 @@ function buildPanel(controls, host, options) {
|
|
|
28251
28358
|
if (s.label?.hasAttribute("data-foldable"))
|
|
28252
28359
|
add(s.label, sectionTitle(s.name), () => s.label.focus(), s.label.firstElementChild);
|
|
28253
28360
|
}
|
|
28254
|
-
presetStrip.
|
|
28255
|
-
|
|
28256
|
-
|
|
28257
|
-
|
|
28258
|
-
|
|
28259
|
-
|
|
28260
|
-
|
|
28261
|
-
|
|
28361
|
+
for (const strip of [presetStrip, ...groupStrips.values()])
|
|
28362
|
+
strip.getEntries().forEach((entry, i) => {
|
|
28363
|
+
const chip = strip.getButtons()[i];
|
|
28364
|
+
if (chip)
|
|
28365
|
+
add(chip, entry.label, () => {
|
|
28366
|
+
strip.selectById(entry.id);
|
|
28367
|
+
strip.element.focus({ preventScroll: true });
|
|
28368
|
+
});
|
|
28369
|
+
});
|
|
28262
28370
|
for (const 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")) {
|
|
28263
28371
|
add(b, b.dataset.hint ?? b.getAttribute("aria-label") ?? b.textContent ?? "", () => b.focus());
|
|
28264
28372
|
}
|
|
@@ -28277,7 +28385,7 @@ function buildPanel(controls, host, options) {
|
|
|
28277
28385
|
}
|
|
28278
28386
|
return targets;
|
|
28279
28387
|
};
|
|
28280
|
-
const unregisterHints =
|
|
28388
|
+
const unregisterHints = hintReach === "page" && bound("hint") ? pageHints(doc).register(root, () => collectHintTargets().map((t) => ({
|
|
28281
28389
|
el: t.el,
|
|
28282
28390
|
label: t.display,
|
|
28283
28391
|
...t.labelEl ? { labelEl: t.labelEl } : {},
|
|
@@ -28343,6 +28451,9 @@ function buildPanel(controls, host, options) {
|
|
|
28343
28451
|
runs.push({ at: b.head, stops: shown([b.head]) });
|
|
28344
28452
|
for (const sh of subheads.values())
|
|
28345
28453
|
runs.push({ at: sh.el, stops: shown([sh.el]) });
|
|
28454
|
+
for (const gs of groupStrips.values())
|
|
28455
|
+
if (visible(gs.element))
|
|
28456
|
+
runs.push({ at: gs.element, stops: shown([gs.element]) });
|
|
28346
28457
|
runs.push(...strips);
|
|
28347
28458
|
runs.sort((a, b) => a.at.compareDocumentPosition(b.at) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
28348
28459
|
for (const run of runs)
|
|
@@ -28820,6 +28931,9 @@ function buildPanel(controls, host, options) {
|
|
|
28820
28931
|
return { el: sub, level: "row" };
|
|
28821
28932
|
return { el: r.row.closest(".knobs-card > *") ?? r.row, level: "row" };
|
|
28822
28933
|
}
|
|
28934
|
+
const strip = active.closest(".knobs-card > .knobs-presets");
|
|
28935
|
+
if (strip)
|
|
28936
|
+
return { el: strip, level: "row" };
|
|
28823
28937
|
const card = cardOf(active);
|
|
28824
28938
|
return card ? { el: card, level: "card" } : null;
|
|
28825
28939
|
};
|
|
@@ -28834,7 +28948,7 @@ function buildPanel(controls, host, options) {
|
|
|
28834
28948
|
editor: t.level === "editor" ? partName(t.el, active) : undefined
|
|
28835
28949
|
});
|
|
28836
28950
|
};
|
|
28837
|
-
const crumbs = options.crumbs
|
|
28951
|
+
const crumbs = options.crumbs !== true || header.hidden || !titleEl.isConnected ? undefined : createCrumbs({
|
|
28838
28952
|
header,
|
|
28839
28953
|
title: titleEl,
|
|
28840
28954
|
reduced: () => reducedMotion(),
|
|
@@ -28858,6 +28972,22 @@ function buildPanel(controls, host, options) {
|
|
|
28858
28972
|
to.focus(quiet);
|
|
28859
28973
|
queueScope();
|
|
28860
28974
|
};
|
|
28975
|
+
root.addEventListener("mousedown", (event) => {
|
|
28976
|
+
if (event.button !== 0 || event.shiftKey || event.metaKey || event.ctrlKey)
|
|
28977
|
+
return;
|
|
28978
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
28979
|
+
const r = rows[rowAt(target)];
|
|
28980
|
+
if (!target || !r || r.leaving || r.row.closest("[hidden]"))
|
|
28981
|
+
return;
|
|
28982
|
+
const own = target.closest("button,input,select,textarea,a[href],[tabindex],[contenteditable]");
|
|
28983
|
+
if (own && r.row.contains(own))
|
|
28984
|
+
return;
|
|
28985
|
+
event.preventDefault();
|
|
28986
|
+
r.input.focus({ preventScroll: true, focusVisible: false });
|
|
28987
|
+
const t = lensTarget(doc.activeElement);
|
|
28988
|
+
jumpedTo = t && t !== "out" ? t.el : null;
|
|
28989
|
+
queueScope();
|
|
28990
|
+
}, true);
|
|
28861
28991
|
let titleDown;
|
|
28862
28992
|
titleEl.addEventListener("pointerdown", (event) => {
|
|
28863
28993
|
titleDown = [event.clientX, event.clientY];
|
|
@@ -28993,6 +29123,23 @@ function buildPanel(controls, host, options) {
|
|
|
28993
29123
|
return;
|
|
28994
29124
|
return optionsOf(r);
|
|
28995
29125
|
};
|
|
29126
|
+
const groupStripAt = (node) => node ? [...groupStrips.values()].find((s) => s.element.contains(node)) : undefined;
|
|
29127
|
+
const groupStripOptions = new Map;
|
|
29128
|
+
const optionsOfStrip = (strip) => {
|
|
29129
|
+
let o = groupStripOptions.get(strip);
|
|
29130
|
+
if (!o) {
|
|
29131
|
+
o = {
|
|
29132
|
+
get count() {
|
|
29133
|
+
return strip.getEntries().length;
|
|
29134
|
+
},
|
|
29135
|
+
choose: (i) => strip.select(i),
|
|
29136
|
+
shown: () => strip.getButtons(),
|
|
29137
|
+
anchor: strip.element
|
|
29138
|
+
};
|
|
29139
|
+
groupStripOptions.set(strip, o);
|
|
29140
|
+
}
|
|
29141
|
+
return o;
|
|
29142
|
+
};
|
|
28996
29143
|
const stripDigits = () => visible(presetStrip.element) && presetStrip.getEntries().length > 1;
|
|
28997
29144
|
const stripOptions = {
|
|
28998
29145
|
get count() {
|
|
@@ -29003,7 +29150,12 @@ function buildPanel(controls, host, options) {
|
|
|
29003
29150
|
anchor: presetStrip.element
|
|
29004
29151
|
};
|
|
29005
29152
|
const stripOptionsAt = (node) => {
|
|
29006
|
-
if (hintCtrl.isActive || !node ||
|
|
29153
|
+
if (hintCtrl.isActive || !node || stripDigitsShown)
|
|
29154
|
+
return;
|
|
29155
|
+
const own = groupStripAt(node);
|
|
29156
|
+
if (own)
|
|
29157
|
+
return visible(own.element) && own.getEntries().length > 1 && !own.nameField() ? optionsOfStrip(own) : undefined;
|
|
29158
|
+
if (!stripDigits())
|
|
29007
29159
|
return;
|
|
29008
29160
|
const at = presetStrip.element.contains(node) || focusLevel(node) === "panel";
|
|
29009
29161
|
return at && !presetStrip.nameField() ? stripOptions : undefined;
|
|
@@ -29240,9 +29392,10 @@ function buildPanel(controls, host, options) {
|
|
|
29240
29392
|
}
|
|
29241
29393
|
if (pressed("presets", event)) {
|
|
29242
29394
|
const direction = key === keys.presets[0] ? -1 : 1;
|
|
29243
|
-
const
|
|
29395
|
+
const strip = groupStripAt(target) ?? presetStrip;
|
|
29396
|
+
const presetsShown = visible(strip.element) && strip.getEntries().length > 1;
|
|
29244
29397
|
if (presetsShown)
|
|
29245
|
-
|
|
29398
|
+
strip.cycle(direction);
|
|
29246
29399
|
else if (!cycleTab(direction))
|
|
29247
29400
|
return;
|
|
29248
29401
|
event.preventDefault();
|
|
@@ -29275,6 +29428,8 @@ function buildPanel(controls, host, options) {
|
|
|
29275
29428
|
search.focus();
|
|
29276
29429
|
return;
|
|
29277
29430
|
}
|
|
29431
|
+
if (/^[1-9]$/.test(key) && groupStripAt(target))
|
|
29432
|
+
return;
|
|
29278
29433
|
if (/^[1-9]$/.test(key)) {
|
|
29279
29434
|
const group = page ? focusedGroup(target) : undefined;
|
|
29280
29435
|
const groupDigits = Boolean(group && groupChips.has(group));
|
|
@@ -29335,7 +29490,7 @@ function buildPanel(controls, host, options) {
|
|
|
29335
29490
|
}
|
|
29336
29491
|
if (pressed("save", event) && showVariants) {
|
|
29337
29492
|
event.preventDefault();
|
|
29338
|
-
presetStrip.save();
|
|
29493
|
+
(groupStripAt(target) ?? presetStrip).save();
|
|
29339
29494
|
return;
|
|
29340
29495
|
}
|
|
29341
29496
|
const index = rowAt(target);
|
|
@@ -29834,6 +29989,7 @@ function buildPanel(controls, host, options) {
|
|
|
29834
29989
|
...undock ? {} : { headerDie: { button: randomButton, die } },
|
|
29835
29990
|
locks: lockIcons,
|
|
29836
29991
|
presetStrip,
|
|
29992
|
+
groupStrips: () => groupStrips.values(),
|
|
29837
29993
|
tabs: tabStrip,
|
|
29838
29994
|
groups: gpuGroups,
|
|
29839
29995
|
cards,
|
|
@@ -29962,6 +30118,8 @@ function buildPanel(controls, host, options) {
|
|
|
29962
30118
|
for (const c of groupChips.values())
|
|
29963
30119
|
c.dispose();
|
|
29964
30120
|
presetStrip.dispose();
|
|
30121
|
+
for (const strip of groupStrips.values())
|
|
30122
|
+
strip.dispose();
|
|
29965
30123
|
tabStrip.dispose();
|
|
29966
30124
|
tabMotion.finish();
|
|
29967
30125
|
doc.removeEventListener("keydown", onGlobalKey);
|