@kitlangton/knobs 0.4.0-next.1 → 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.
Files changed (53) hide show
  1. package/README.md +191 -13
  2. package/dist/dev/{chunk-ayb292zj.js → chunk-75gbzbyd.js} +3 -3
  3. package/dist/dev/{chunk-1qzffbpa.js → chunk-bnhzf1tj.js} +240 -87
  4. package/dist/dev/{chunk-8mabjeyk.js → chunk-j57rhjtm.js} +166 -2
  5. package/dist/dev/{chunk-3ygqgy1x.js → chunk-mjdgzad5.js} +15 -2
  6. package/dist/dev/index.js +37 -14
  7. package/dist/index.js +46 -11
  8. package/dist/panel.js +408 -73
  9. package/dist/review-react.js +18580 -0
  10. package/dist/review-solid.js +18482 -0
  11. package/dist/review.js +18488 -0
  12. package/dist/themes.js +281 -23
  13. package/dist/types/core/remote.d.ts +2 -0
  14. package/dist/types/core/schema.d.ts +13 -1
  15. package/dist/types/core/store.d.ts +8 -0
  16. package/dist/types/panel/gpu/renderer.d.ts +2 -0
  17. package/dist/types/panel/presets.d.ts +7 -1
  18. package/dist/types/review/font.d.ts +13 -0
  19. package/dist/types/review/foundations.d.ts +175 -0
  20. package/dist/types/review/index.d.ts +13 -0
  21. package/dist/types/review/react.d.ts +1 -0
  22. package/dist/types/review/solid.d.ts +1 -0
  23. package/dist/types/themes/index.d.ts +7 -2
  24. package/dist/types/themes/review.d.ts +8 -0
  25. package/dist/types/tokens/schema.d.ts +180 -0
  26. package/dist/types/ui/compare-css.d.ts +1 -1
  27. package/dist/types/ui/compare-gpu.d.ts +6 -6
  28. package/dist/types/ui/compare-model.d.ts +85 -2
  29. package/dist/types/ui/compare.d.ts +18 -4
  30. package/dist/types/ui/diff.d.ts +4 -5
  31. package/dist/types/ui/history-css.d.ts +1 -0
  32. package/dist/types/ui/history-model.d.ts +249 -0
  33. package/dist/types/ui/history.d.ts +38 -0
  34. package/dist/types/ui/icons.d.ts +40 -0
  35. package/dist/types/ui/index.d.ts +8 -4
  36. package/dist/types/ui/player-css.d.ts +1 -0
  37. package/dist/types/ui/player.d.ts +51 -0
  38. package/dist/types/ui/review-look.d.ts +6 -0
  39. package/dist/types/ui/review-options.d.ts +28 -0
  40. package/dist/types/ui/selection-pill-css.d.ts +1 -0
  41. package/dist/types/ui/selection-pill.d.ts +106 -0
  42. package/dist/types/ui/timeline-model.d.ts +27 -0
  43. package/dist/types/ui/timeline.d.ts +16 -1
  44. package/dist/types/ui/tree.d.ts +3 -2
  45. package/dist/types/ui/walkthrough-annotations.d.ts +42 -0
  46. package/dist/types/ui/walkthrough-blocks.d.ts +87 -0
  47. package/dist/types/ui/walkthrough-media.d.ts +30 -1
  48. package/dist/types/ui/walkthrough-model.d.ts +469 -5
  49. package/dist/types/ui/walkthrough.d.ts +27 -5
  50. package/dist/ui-react.js +5124 -1031
  51. package/dist/ui-solid.js +5136 -1043
  52. package/dist/ui.js +6411 -2269
  53. package/package.json +13 -1
@@ -10,7 +10,7 @@ import {
10
10
  DEFAULT_LINEAR,
11
11
  motionCss,
12
12
  storageKey
13
- } from "./chunk-3ygqgy1x.js";
13
+ } from "./chunk-mjdgzad5.js";
14
14
 
15
15
  // src/core/theme.ts
16
16
  var property = (k) => k.startsWith("--") ? k : `--k-${k}`;
@@ -1194,6 +1194,22 @@ var theme = { from: "theme" }, css = (s) => ({ css: s }), value = (s) => ({ valu
1194
1194
  source: css("var(--k-warn)"),
1195
1195
  doc: "Modified lines."
1196
1196
  },
1197
+ {
1198
+ name: "before",
1199
+ tier: "semantic",
1200
+ type: "color",
1201
+ group: "Data",
1202
+ source: css("var(--k-fg-3)"),
1203
+ doc: "Before in a comparison, and only Before: its label and side of the seam. Neutral unless a theme gives it a colour."
1204
+ },
1205
+ {
1206
+ name: "after",
1207
+ tier: "semantic",
1208
+ type: "color",
1209
+ group: "Data",
1210
+ source: css("var(--k-fg)"),
1211
+ doc: "After in a comparison, and only After."
1212
+ },
1197
1213
  {
1198
1214
  name: "series-1",
1199
1215
  tier: "semantic",
@@ -1284,6 +1300,150 @@ var theme = { from: "theme" }, css = (s) => ({ css: s }), value = (s) => ({ valu
1284
1300
  source: value("288px"),
1285
1301
  doc: "Panel width."
1286
1302
  },
1303
+ {
1304
+ name: "space-inside",
1305
+ tier: "semantic",
1306
+ type: "length",
1307
+ group: "Space",
1308
+ source: css("var(--k-unit)"),
1309
+ doc: "Parts of one thing: an icon and its label, a meta line and its title."
1310
+ },
1311
+ {
1312
+ name: "space-sibling",
1313
+ tier: "semantic",
1314
+ type: "length",
1315
+ group: "Space",
1316
+ source: css("calc(var(--k-unit) * 2)"),
1317
+ doc: "Siblings in a group: buttons in a row, a list item's lines."
1318
+ },
1319
+ {
1320
+ name: "space-title",
1321
+ tier: "semantic",
1322
+ type: "length",
1323
+ group: "Space",
1324
+ source: css("calc(var(--k-unit) * 3)"),
1325
+ doc: "A heading and what it heads."
1326
+ },
1327
+ {
1328
+ name: "space-group",
1329
+ tier: "semantic",
1330
+ type: "length",
1331
+ group: "Space",
1332
+ source: css("calc(var(--k-unit) * 12)"),
1333
+ doc: "Groups inside a section (prose, video, diff), stacked media, feed items (a hairline in the middle)."
1334
+ },
1335
+ {
1336
+ name: "space-section",
1337
+ tier: "semantic",
1338
+ type: "length",
1339
+ group: "Space",
1340
+ source: css("calc(var(--k-unit) * 24)"),
1341
+ doc: "Sections of a page (the intro, the updates, each PR), a hairline in the middle."
1342
+ },
1343
+ {
1344
+ name: "space-column",
1345
+ tier: "semantic",
1346
+ type: "length",
1347
+ group: "Space",
1348
+ source: css("calc(var(--k-unit) * 6)"),
1349
+ doc: "Columns: a contents rail and the page, media side by side."
1350
+ },
1351
+ {
1352
+ name: "pad-page",
1353
+ tier: "semantic",
1354
+ type: "length",
1355
+ group: "Space",
1356
+ source: css("calc(var(--k-unit) * 6)"),
1357
+ doc: "A page's edge; 16px under 640px wide."
1358
+ },
1359
+ {
1360
+ name: "pad-card",
1361
+ tier: "semantic",
1362
+ type: "length",
1363
+ group: "Space",
1364
+ source: css("calc(var(--k-unit) * 3)"),
1365
+ doc: "Inside a card or popover."
1366
+ },
1367
+ {
1368
+ name: "type-heading",
1369
+ tier: "semantic",
1370
+ type: "length",
1371
+ group: "Type",
1372
+ source: value("20px"),
1373
+ doc: "A page's title, weight 500."
1374
+ },
1375
+ {
1376
+ name: "leading-heading",
1377
+ tier: "semantic",
1378
+ type: "length",
1379
+ group: "Type",
1380
+ source: value("28px"),
1381
+ doc: "Line height for type-heading."
1382
+ },
1383
+ {
1384
+ name: "type-title",
1385
+ tier: "semantic",
1386
+ type: "length",
1387
+ group: "Type",
1388
+ source: value("16px"),
1389
+ doc: "Section titles (under their kicker), weight 500."
1390
+ },
1391
+ {
1392
+ name: "leading-title",
1393
+ tier: "semantic",
1394
+ type: "length",
1395
+ group: "Type",
1396
+ source: value("24px"),
1397
+ doc: "Line height for type-title."
1398
+ },
1399
+ {
1400
+ name: "type-body",
1401
+ tier: "semantic",
1402
+ type: "length",
1403
+ group: "Type",
1404
+ source: value("13px"),
1405
+ doc: "Prose, questions, list rows, step titles; 400, or 500 for emphasis."
1406
+ },
1407
+ {
1408
+ name: "leading-body",
1409
+ tier: "semantic",
1410
+ type: "length",
1411
+ group: "Type",
1412
+ source: value("20px"),
1413
+ doc: "Line height for type-body."
1414
+ },
1415
+ {
1416
+ name: "type-label",
1417
+ tier: "semantic",
1418
+ type: "length",
1419
+ group: "Type",
1420
+ source: value("12px"),
1421
+ doc: "Mono inside body text: code and paths."
1422
+ },
1423
+ {
1424
+ name: "leading-label",
1425
+ tier: "semantic",
1426
+ type: "length",
1427
+ group: "Type",
1428
+ source: value("16px"),
1429
+ doc: "Line height for type-label."
1430
+ },
1431
+ {
1432
+ name: "type-small",
1433
+ tier: "semantic",
1434
+ type: "length",
1435
+ group: "Type",
1436
+ source: value("11px"),
1437
+ doc: "Meta, controls, keys, timestamps; mono inside labels."
1438
+ },
1439
+ {
1440
+ name: "leading-small",
1441
+ tier: "semantic",
1442
+ type: "length",
1443
+ group: "Type",
1444
+ source: value("16px"),
1445
+ doc: "Line height for type-small."
1446
+ },
1287
1447
  {
1288
1448
  name: "font-size",
1289
1449
  tier: "semantic",
@@ -2141,8 +2301,9 @@ ${register(["width"], "<length-percentage>", "288px")}
2141
2301
  @media(prefers-reduced-motion:reduce){:where(.knobs-chip,.knobs-ui){--k-token-t:0s}}`, aliased = (css) => css.replace(new RegExp(`var\\(--_k-(${ANIMATED.join("|")})\\)`, "g"), "var(--kv-$1)").replace(/var\(--k-blur\)/g, "var(--kv-blur)");
2142
2302
 
2143
2303
  // src/core/root.ts
2144
- var ROOT_CLASS = "knobs-root", extras = `:where(.${ROOT_CLASS}){--_k-shadow-k:calc(var(--k-shadow-strength,calc(.48 - .32 * var(--_k-light,0))) / .48);--_k-glass-tone:var(--k-glass-tone,calc(.5 + .58 * var(--_k-light,0)));--_k-fill-base:var(--k-fill-base,var(--_k-fill-selected));--_k-fill-hover-mix:var(--k-fill-hover-mix,var(--k-fill-rest-mix,20%));--_k-reveal-ease:var(--k-ease,cubic-bezier(.2,.8,.2,1))}`, spring = `:where(:root,:host){--k-fill-ease:${DEFAULT_LINEAR.easing};--k-fill-ms:${DEFAULT_LINEAR.ms}}`, rootCss = `${tokensCss(knobs)}
2304
+ var ROOT_CLASS = "knobs-root", extras = `:where(.${ROOT_CLASS}){--_k-shadow-k:calc(var(--k-shadow-strength,calc(.48 - .32 * var(--_k-light,0))) / .48);--_k-glass-tone:var(--k-glass-tone,calc(.5 + .58 * var(--_k-light,0)));--_k-fill-base:var(--k-fill-base,var(--_k-fill-selected));--_k-fill-hover-mix:var(--k-fill-hover-mix,var(--k-fill-rest-mix,20%));--_k-reveal-ease:var(--k-ease,cubic-bezier(.2,.8,.2,1))}`, narrow = `@media(max-width:639.98px){:where(.${ROOT_CLASS}){--_k-pad-page:var(--k-pad-page,calc(var(--_k-unit) * 4))}}`, spring = `:where(:root,:host){--k-fill-ease:${DEFAULT_LINEAR.easing};--k-fill-ms:${DEFAULT_LINEAR.ms}}`, rootCss = `${tokensCss(knobs)}
2145
2305
  ${extras}
2306
+ ${narrow}
2146
2307
  ${spring}
2147
2308
  ${DRIVERS}
2148
2309
  ${motionCss}`;
@@ -2425,6 +2586,9 @@ ${GRAD_PROPS}
2425
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)}
2426
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)}
2427
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}
2428
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)}
2429
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}
2430
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), SPRING2 = {
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-3ygqgy1x.js";
79
- __chunks(import.meta.url,["kd01pt6y","1qzffbpa","ayb292zj","3ygqgy1x","8mabjeyk"],[["./index.js",3],["./chunk-1qzffbpa.js",3,4],["./chunk-ayb292zj.js",3,4],["./chunk-3ygqgy1x.js"],["./chunk-8mabjeyk.js",3]],0);
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"), buildPresets = () => Object.freeze(Object.fromEntries([
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.preset)
734
+ let b = activeBase(), group = schema[key]?.group;
735
+ if (group !== void 0 && Object.hasOwn(b.groups, group))
717
736
  return !0;
718
- let group = schema[key]?.group;
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("ayb292zj"), import("./chunk-ayb292zj.js")).then((m) => m.attach);
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("1qzffbpa"), import("./chunk-1qzffbpa.js")).then((m) => m.mountPanel);
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), SPRING = {
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"), buildPresets = () => Object.freeze(Object.fromEntries([
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.preset)
1764
+ let b = activeBase(), group = schema[key]?.group;
1765
+ if (group !== void 0 && Object.hasOwn(b.groups, group))
1735
1766
  return !0;
1736
- let group = schema[key]?.group;
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,