@kitlangton/knobs 0.4.0-next.10 → 0.4.0-next.12

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.
@@ -1,6 +1,3 @@
1
- import {
2
- __preload
3
- } from "./chunk-3ygqgy1x.js";
4
1
  import {
5
2
  applyLook,
6
3
  panelLook,
@@ -9,7 +6,7 @@ import {
9
6
  writeSession,
10
7
  injectStyles,
11
8
  toggleLayer
12
- } from "./chunk-dyq4m24m.js";
9
+ } from "./chunk-nwfm1bfj.js";
13
10
 
14
11
  // src/panel/attach.ts
15
12
  function attach(controls, element, options = {}) {
@@ -85,7 +82,7 @@ function attach(controls, element, options = {}) {
85
82
  return;
86
83
  loading = !0;
87
84
  try {
88
- let { createPanel } = await (__preload("5pb7ymrd"), import("./chunk-5pb7ymrd.js"));
85
+ let { createPanel } = await import("./chunk-9qtvxa6k.js");
89
86
  if (disposed || !controls.enabled || panel)
90
87
  return;
91
88
  if (panel = createPanel(controls, element, close, { restored, chip }), remember)
@@ -10,7 +10,7 @@ import {
10
10
  DEFAULT_LINEAR,
11
11
  motionCss,
12
12
  storageKey
13
- } from "./chunk-3ygqgy1x.js";
13
+ } from "./chunk-7kns3qe6.js";
14
14
 
15
15
  // src/core/theme.ts
16
16
  var property = (k) => k.startsWith("--") ? k : `--k-${k}`;
@@ -631,20 +631,15 @@ function escTarget(trail, title) {
631
631
  return;
632
632
  return trail.at(-2)?.label ?? (title.trim() || "panel");
633
633
  }
634
- function fitCrumbs(widths, room, sep, more, hint = 0) {
635
- let n = widths.length;
636
- if (!n)
637
- return { from: 0, hint: !1 };
638
- let span = (from) => {
639
- let w = 0;
640
- for (let i = from;i < n; i++)
641
- w += widths[i] + (i > from ? sep : 0);
642
- return from > 0 ? w + more + sep : w;
643
- }, from = 0;
644
- while (from < n - 1 && span(from) > room)
645
- from++;
646
- let used = span(from);
647
- return { from, hint: hint > 0 && used + sep + hint <= room };
634
+ function fitCrumbs(widths, room, sep, hint = 0) {
635
+ let n = widths.length, from = n, used = 0;
636
+ for (let i = n - 1;i >= 0; i--) {
637
+ let w = used + widths[i] + (i < n - 1 ? sep : 0);
638
+ if (w > room)
639
+ break;
640
+ used = w, from = i;
641
+ }
642
+ return { from, hint: from < n && hint > 0 && used + sep + hint <= room };
648
643
  }
649
644
  function digitOption(key, count) {
650
645
  if (!/^[1-9]$/.test(key))
@@ -652,6 +647,15 @@ function digitOption(key, count) {
652
647
  let i = Number(key) - 1;
653
648
  return i < count ? i : void 0;
654
649
  }
650
+ function digitRoute(key, at) {
651
+ if (!/^[1-9]$/.test(key))
652
+ return;
653
+ if (at.options)
654
+ return "options";
655
+ if (at.keeps)
656
+ return;
657
+ return digitOption(key, at.presets) === void 0 ? void 0 : "presets";
658
+ }
655
659
  function digitHints(count, showing) {
656
660
  let n = Math.min(9, count);
657
661
  if (n <= 0)
@@ -684,9 +688,10 @@ function createCrumbs(o) {
684
688
  }, build = () => {
685
689
  if (!trail.length)
686
690
  return { pieces: [], xs: [] };
687
- let head = o.header.getBoundingClientRect(), { x, right } = titleEnd(head), sepW = width(SEP) + GAP * 2, escText = esc ? `esc ↑ ${esc}` : "", fit = fitCrumbs(trail.map((c) => width(c.label)), Math.max(0, right - x - sepW), sepW, width("…"), escText ? width(escText) + HINT_GAP - sepW : 0), pieces = [{ key: "sep:lead", text: SEP, kind: "sep" }];
688
- if (fit.from > 0)
689
- pieces.push({ key: "more", text: "…", kind: "more" }, { key: "sep:more", text: SEP, kind: "sep" });
691
+ let head = o.header.getBoundingClientRect(), { x, right } = titleEnd(head), sepW = width(SEP) + GAP * 2, escText = esc ? `esc ↑ ${esc}` : "", fit = fitCrumbs(trail.map((c) => width(c.label)), Math.max(0, right - x - sepW), sepW, escText ? width(escText) + HINT_GAP - sepW : 0);
692
+ if (fit.from >= trail.length)
693
+ return { pieces: [], xs: [] };
694
+ let pieces = [{ key: "sep:lead", text: SEP, kind: "sep" }];
690
695
  if (trail.forEach((c, i) => {
691
696
  if (i < fit.from)
692
697
  return;
@@ -2374,8 +2379,8 @@ ${GRAD_PROPS}
2374
2379
  .knobs-panel *:where(:not(.knobs-accessory,.knobs-accessory *,.knobs-live *)),.knobs-menu *{box-sizing:border-box;color:inherit;letter-spacing:inherit;word-spacing:inherit;line-height:inherit;text-transform:inherit;text-shadow:inherit;font-style:inherit}
2375
2380
  /* Hover washes come in at --k-hover-in and leave at --k-hover-out (ms). Set per element, so a
2376
2381
  hovered card doesn't hand its quick entrance to the rows inside it. */
2377
- :where(.knobs-panel,.knobs-menu) *{--k-hover-t:calc(var(--_k-hover-out) * 1ms)}
2378
- :where(.knobs-panel,.knobs-menu) :is(:hover,.knobs-active){--k-hover-t:calc(var(--_k-hover-in) * 1ms)}
2382
+ :where(.knobs-panel,.knobs-menu,.knobs-head-actions[data-docked]) *{--k-hover-t:calc(var(--_k-hover-out) * 1ms)}
2383
+ :where(.knobs-panel,.knobs-menu,.knobs-head-actions[data-docked]) :is(:hover,.knobs-active){--k-hover-t:calc(var(--_k-hover-in) * 1ms)}
2379
2384
 
2380
2385
  .knobs-panel,.knobs-chip{
2381
2386
  box-sizing:border-box;font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);text-align:left;
@@ -2495,7 +2500,7 @@ ${GRAD_PROPS}
2495
2500
  .knobs-crumbs{position:absolute;left:0;top:50%;height:0;pointer-events:none;white-space:nowrap;font-size:var(--_k-text-meta);font-weight:var(--_k-weight-label);line-height:1.2;letter-spacing:0;text-transform:none}
2496
2501
  .knobs-crumbs>*{position:absolute;left:0;top:0;translate:0 -50%;color:var(--_k-fg-3);transition:opacity .2s var(--k-ease),translate ${CRUMB_GLIDE},color .2s var(--k-ease)}
2497
2502
  .knobs-crumbs>[data-leaving]{transition:opacity .14s cubic-bezier(.4,0,1,1),translate .14s cubic-bezier(.4,0,1,1)}
2498
- .knobs-crumbs>.knobs-crumb-crumb{pointer-events:auto;cursor:pointer}
2503
+ .knobs-crumbs>.knobs-crumb-crumb{pointer-events:auto;cursor:default}
2499
2504
  .knobs-crumbs>.knobs-crumb-crumb[data-deep]{color:var(--_k-fg)}
2500
2505
  .knobs-crumbs>.knobs-crumb-crumb:not([data-deep]):hover{color:var(--_k-fg-2)}
2501
2506
  .knobs-crumbs>.knobs-crumb-sep{color:color-mix(in srgb,var(--_k-fg-3) 70%,transparent)}
@@ -2512,40 +2517,53 @@ ${GRAD_PROPS}
2512
2517
  .knobs-panel[data-gpu-active="true"] .knobs-die-icon>canvas{visibility:hidden}
2513
2518
  /* The tools sit quiet (--k-dim) until the panel has the pointer or keyboard focus, then read as
2514
2519
  labels, and as text under the pointer. */
2515
- .knobs-panel header button{position:relative;width:var(--k-icon-btn);height:var(--k-icon-btn);padding:0;display:inline-flex;align-items:center;justify-content:center;color:var(--_k-fg-3);background:transparent;border-radius:var(--_k-round);cursor:pointer;font-size:12px;line-height:1;transition:background var(--k-hover-t),color var(--k-hover-t),opacity .15s}
2520
+ .knobs-panel header button{position:relative;width:var(--k-icon-btn);height:var(--k-icon-btn);padding:0;display:inline-flex;align-items:center;justify-content:center;color:var(--_k-fg-3);background:transparent;border-radius:var(--_k-round);cursor:default;font-size:12px;line-height:1;transition:background var(--k-hover-t),color var(--k-hover-t),opacity .15s}
2516
2521
  .knobs-panel:is(:hover,:has(:focus-visible)) header button{color:var(--_k-fg-2)}
2517
2522
  .knobs-panel header button:is(:hover:not([disabled]),:focus-visible),.knobs-panel header button[aria-expanded="true"]{background:var(--_k-fill-selected);color:var(--_k-fg)}
2518
2523
  .knobs-panel header button[disabled]{opacity:var(--k-disabled-opacity);cursor:default}
2519
2524
  /* The close X's ink sits a fifth of its box in from each side, a digit's barely: nudged right, its
2520
2525
  ink ends where the values' digits do. */
2521
2526
  .knobs-panel header .knobs-close>.knobs-icon{translate:calc(var(--k-icon-size) * .1) 0}
2527
+ /* In a split pane's header (dock.ts) the buttons are the pane's: its size, ink and wash, which
2528
+ comes on at once and eases off. Outside the panel, they bring its resets with them. */
2529
+ .knobs-head-actions[data-docked]{--k-icon-size:13px;--k-icon-btn:20px;margin-right:0}
2530
+ .knobs-head-actions[data-docked] button{all:revert;box-sizing:border-box;position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:var(--k-icon-btn);height:var(--k-icon-btn);margin:0;padding:0;border:0;border-radius:var(--_k-round);
2531
+ background:transparent;color:var(--_k-fg-3);font:inherit;line-height:1;cursor:default;outline:none;box-shadow:none;transition:background var(--k-hover-t),color var(--k-hover-t)}
2532
+ .knobs-head-actions[data-docked] button:is(:hover:not([disabled]),:focus-visible){background:var(--_ku-wash-hi,var(--_k-fill-selected));color:var(--_k-fg)}
2533
+ .knobs-head-actions[data-docked] .knobs-icon>path{fill:currentColor}
2534
+ .knobs-head-actions[data-docked] button:focus-visible{--k-hover-t:calc(var(--_k-hover-in) * 1ms)}
2522
2535
  /* Undo · Redo sit apart from Reset · Copy · Random. Undo, Redo and Reset (and a group's reset)
2523
2536
  keep their slots while they're away and bloom in and out of them: nothing beside them moves. */
2524
2537
  .knobs-head-history{display:flex;flex:none;margin-right:calc(var(--_k-unit) * 1.5)}
2525
- .knobs-panel :is(.knobs-head-actions,.knobs-group-head) button[data-shown]{transition:background var(--k-hover-t),color var(--k-hover-t),opacity ${BLOOM_IN},scale ${BLOOM_IN},filter 0s,visibility 0s}
2526
- .knobs-panel :is(.knobs-head-actions,.knobs-group-head) button[data-shown="false"]{opacity:0;scale:.9;filter:blur(2.5px);visibility:hidden;
2527
- transition:background var(--k-hover-t),color var(--k-hover-t),opacity ${BLOOM_OUT},scale ${BLOOM_OUT},filter ${BLOOM_OUT},visibility 0s ${snappyOut.ms}ms}
2538
+ :is(.knobs-panel :is(.knobs-head-actions,.knobs-group-head),.knobs-head-actions[data-docked]) button[data-shown]{transition:background var(--k-hover-t),color var(--k-hover-t),opacity ${BLOOM_IN},scale ${BLOOM_IN},width ${BLOOM_IN}}
2539
+ :is(.knobs-panel :is(.knobs-head-actions,.knobs-group-head),.knobs-head-actions[data-docked]) button[data-shown="false"]{opacity:0;scale:.9;filter:blur(2.5px);visibility:hidden;
2540
+ transition:background var(--k-hover-t),color var(--k-hover-t),opacity ${BLOOM_OUT},scale ${BLOOM_OUT},filter ${BLOOM_OUT},width ${BLOOM_OUT},visibility 0s ${snappyOut.ms}ms}
2541
+ /* Docked, a slot opens and closes with its button (the width above) rather than waiting: the
2542
+ pane's title keeps the room. */
2543
+ .knobs-head-actions[data-docked] button[data-shown="false"]{width:0}
2544
+ .knobs-head-actions[data-docked]>.knobs-head-history{transition:margin ${BLOOM_IN}}
2545
+ .knobs-head-actions[data-docked]>.knobs-head-history:not(:has(>[data-shown="true"]:not([hidden]))){margin-right:0}
2528
2546
  @media(prefers-reduced-motion:reduce){
2529
- .knobs-panel :is(.knobs-head-actions,.knobs-group-head) button[data-shown]{transition:opacity .15s,visibility 0s!important}
2530
- .knobs-panel :is(.knobs-head-actions,.knobs-group-head) button[data-shown="false"]{scale:1;filter:none;transition:opacity .15s,visibility 0s .15s!important}}
2547
+ :is(.knobs-panel :is(.knobs-head-actions,.knobs-group-head),.knobs-head-actions[data-docked]) button[data-shown]{transition:opacity .15s!important}
2548
+ :is(.knobs-panel :is(.knobs-head-actions,.knobs-group-head),.knobs-head-actions[data-docked]) button[data-shown="false"]{scale:1;filter:none;transition:opacity .15s,visibility 0s .15s!important}}
2531
2549
  /* A press flash (flash.ts): from a key or a click, the button's wash brightens at once and eases
2532
2550
  back to its own, its ink with it. Two names, so a second press restarts it. */
2533
2551
  @keyframes knobs-flash-a{from{background-color:var(--k-flash-bg);color:var(--_k-fg)}}
2534
2552
  @keyframes knobs-flash-b{from{background-color:var(--k-flash-bg);color:var(--_k-fg)}}
2535
- .knobs-panel :is(header,.knobs-group-head) button{--k-flash-bg:color-mix(in srgb,var(--_k-fill-selected),var(--_k-fg) 10%)}
2536
- .knobs-panel :is(header,.knobs-group-head) button[data-k-flash="a"]{animation:knobs-flash-a ${FLASH_MS}ms cubic-bezier(.33,1,.68,1)}
2537
- .knobs-panel :is(header,.knobs-group-head) button[data-k-flash="b"]{animation:knobs-flash-b ${FLASH_MS}ms cubic-bezier(.33,1,.68,1)}
2553
+ .knobs-panel :is(header,.knobs-group-head) button,.knobs-head-actions[data-docked] button{--k-flash-bg:color-mix(in srgb,var(--_k-fill-selected),var(--_k-fg) 10%)}
2554
+ .knobs-panel :is(header,.knobs-group-head) button[data-k-flash="a"],.knobs-head-actions[data-docked] button[data-k-flash="a"]{animation:knobs-flash-a ${FLASH_MS}ms cubic-bezier(.33,1,.68,1)}
2555
+ .knobs-panel :is(header,.knobs-group-head) button[data-k-flash="b"],.knobs-head-actions[data-docked] button[data-k-flash="b"]{animation:knobs-flash-b ${FLASH_MS}ms cubic-bezier(.33,1,.68,1)}
2538
2556
  /* Copied: the glyph shrinks away and a check draws itself in its place, held, then they trade
2539
2557
  back (the check fading). Driven by --k-copy (the glyph), --k-check (how much of the check is
2540
2558
  drawn) and --k-check-a (its opacity), which the GPU renderer reads too. */
2541
- .knobs-panel header .knobs-copy{--k-copy:1;--k-check:0;--k-check-a:0;
2559
+ .knobs-panel header .knobs-copy,.knobs-head-actions[data-docked] .knobs-copy{--k-copy:1;--k-check:0;--k-check-a:0;
2542
2560
  transition:background var(--k-hover-t),color var(--k-hover-t),opacity .15s,--k-copy ${BLOOM_IN} .04s,--k-check 0s .1s,--k-check-a .1s cubic-bezier(.4,0,1,1)}
2543
- .knobs-panel header .knobs-copy[data-copied]{--k-copy:0;--k-check:1;--k-check-a:1;color:var(--_k-fg);
2561
+ .knobs-panel header .knobs-copy[data-copied],.knobs-head-actions[data-docked] .knobs-copy[data-copied]{--k-copy:0;--k-check:1;--k-check-a:1;color:var(--_k-fg);
2544
2562
  transition:background var(--k-hover-t),color var(--k-hover-t),opacity .15s,--k-copy .1s cubic-bezier(.4,0,1,1),--k-check .18s cubic-bezier(.2,.8,.2,1) .03s,--k-check-a .04s linear .03s}
2545
2563
  .knobs-copy>.knobs-icon:not(.knobs-check){opacity:var(--k-copy);scale:calc(.5 + .5 * var(--k-copy))}
2546
2564
  .knobs-copy>.knobs-check{position:absolute;inset:0;margin:auto;opacity:var(--k-check-a)}
2547
2565
  /* (Over .knobs-icon>path's fill: the check is a stroke, 16 of 256 wide like Phosphor's regular icons.) */
2548
- .knobs-panel .knobs-icon.knobs-check>path{fill:none;stroke:currentColor;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1 2;stroke-dashoffset:calc(1 - var(--k-check))}
2566
+ .knobs-panel .knobs-icon.knobs-check>path,.knobs-head-actions[data-docked] .knobs-icon.knobs-check>path{fill:none;stroke:currentColor;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1 2;stroke-dashoffset:calc(1 - var(--k-check))}
2549
2567
  .knobs-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
2550
2568
 
2551
2569
  /* Shortcuts Popover */
@@ -2567,6 +2585,15 @@ ${GRAD_PROPS}
2567
2585
  .knobs-panel[data-buttons] .knobs-preset-chip:hover{background:var(--k-button-hover-bg,var(--_k-fill-selected))!important;box-shadow:inset 0 0 0 1px var(--k-button-hover-line,var(--k-button-line,transparent))}
2568
2586
  .knobs-panel .knobs-preset-chip:hover{color:var(--_k-fg)}
2569
2587
  .knobs-panel .knobs-preset-chip[aria-checked="true"]{color:var(--_k-fg);font-weight:var(--_k-weight-title)}
2588
+ /* A list (presetDisplay "list", or chips that would take more than two lines): rows like a
2589
+ menu's, their names on the rows' text edge and each wash bleeding 8 px past it, the selection a
2590
+ quiet wash that glides between them. Hover is a wash too, on at once. */
2591
+ .knobs-presets[data-display="list"]{padding:0;background:transparent}
2592
+ .knobs-presets[data-display="list"]>.knobs-presets-track{grid-template-columns:minmax(0,1fr);gap:0;margin:0 calc(var(--_k-inset) - 8px)}
2593
+ .knobs-panel .knobs-presets[data-display="list"] .knobs-preset-chip{height:var(--_k-control-h);padding:0 16px 0 8px;text-align:left;cursor:default;font-weight:var(--_k-weight-label);transition:color var(--k-hover-t),background var(--k-hover-t)}
2594
+ .knobs-panel .knobs-presets[data-display="list"] .knobs-preset-chip:hover:not([aria-checked="true"]){background:var(--_k-row-hover)!important}
2595
+ .knobs-panel .knobs-presets[data-display="list"] .knobs-preset-chip::after{top:50%;right:8px;margin-top:-2px}
2596
+ .knobs-presets[data-display="list"] .knobs-presets-pill{background:var(--_k-fill-selected);box-shadow:none}
2570
2597
  .knobs-panel .knobs-preset-save{flex:none;width:calc((var(--_k-inset) - var(--_k-tray-pad)) * 2 + 8px);padding:0!important;color:var(--_k-fg-3)!important;font-weight:400!important;font-size:var(--_k-text-title)!important}
2571
2598
  .knobs-panel .knobs-preset-save:hover{color:var(--_k-fg)!important}
2572
2599
  /* The active preset's modified mark: the values have drifted off it (a row's dot, in its corner). */
@@ -2586,6 +2613,9 @@ ${GRAD_PROPS}
2586
2613
  .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
2614
  .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
2615
  .knobs-group-head:first-child{margin-top:0}
2616
+ .knobs-panel:not(.knobs-page) .knobs-presets+.knobs-group-head{margin-top:0;border-top:0;padding-top:0}
2617
+ .knobs-pinned{margin-bottom:var(--k-group-gap)}
2618
+ .knobs-pinned:empty{display:none}
2589
2619
  .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
2620
  .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
2621
  .knobs-group-head button:is(:hover:not([disabled]),:focus-visible){background:var(--_k-fill-selected);color:var(--_k-fg)}
@@ -2908,6 +2938,14 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
2908
2938
  :where(.knobs-panel)[data-frame="flat"]{--k-group-divider:var(--_k-border);--k-group-divider-gap:var(--_k-gap)}
2909
2939
  :where(.knobs-panel.knobs-inline:not(.knobs-page))[data-frame="flat"]{--k-chrome:0;--k-pad:0px}
2910
2940
  .knobs-panel[data-frame="flat"] .knobs-card{background:transparent;border-radius:0}
2941
+ /* One surface: an inline panel on another card (a split's pane, anything .knobs-surface, or in
2942
+ [data-nested]) takes the flat frame by itself: no padding (its rows keep their inset, so their
2943
+ text sits on the card's, and its preset chips line up with them), groups as plain sections,
2944
+ rows on hairlines. */
2945
+ :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip,[data-nested]) :where(.knobs-panel.knobs-inline:not(.knobs-page)){--k-chrome:0;--k-group-divider:var(--_k-border);--k-group-divider-gap:var(--_k-gap)}
2946
+ :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip,[data-nested]) .knobs-panel.knobs-inline:not(.knobs-page){padding:0}
2947
+ :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip,[data-nested]) .knobs-panel.knobs-inline:not(.knobs-page) .knobs-card{background:transparent;border-radius:0}
2948
+ :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip,[data-nested]) .knobs-panel.knobs-inline:not(.knobs-page) .knobs-presets:not([data-display="list"]){margin-inline:var(--_k-inset)}
2911
2949
  .knobs-panel[data-frame="flat"][data-rows] .knobs-card>*,.knobs-panel[data-frame="flat"][data-rows] .knobs-sub{background:transparent;border-radius:0}
2912
2950
 
2913
2951
  /* Disabled rows dim and take no edits (focus still reaches them, to read); read-only rows keep
@@ -3060,6 +3098,10 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3060
3098
  .knobs-panel .knobs-tool[disabled]{opacity:var(--k-disabled-opacity);cursor:default}
3061
3099
  .knobs-tool .knobs-icon{flex:none;width:var(--k-icon-sm);height:var(--k-icon-sm)}
3062
3100
  .knobs-tool-label{min-width:0;overflow:hidden;text-overflow:ellipsis}
3101
+ /* Icons only (labels that don't fit one row, or labels: false): one row of equal buttons, each
3102
+ its icon centred, its label and shortcut in its tooltip. */
3103
+ .knobs-panel .knobs-toolbar[data-icons] .knobs-tool{justify-content:center;padding:0;cursor:default}
3104
+ .knobs-panel .knobs-toolbar[data-icons] :is(.knobs-tool-label,.knobs-tool-key){display:none}
3063
3105
  /* The shortcut sits at the button's end, so a column of buttons lines their chips up. */
3064
3106
  .knobs-panel .knobs-tool-key{flex:none;margin-left:auto;padding-left:4px;font:inherit;font-size:var(--_k-text-cap);font-variant-numeric:tabular-nums;color:var(--_k-fg-3);letter-spacing:.06em}
3065
3107
  .knobs-panel :is([data-disabled],[data-readonly]) :is(.knobs-cell,.knobs-range-track,.knobs-angle,.knobs-dial,.knobs-seed,.knobs-keys,.knobs-tool){cursor:default}
@@ -3105,7 +3147,7 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3105
3147
 
3106
3148
  /* A focused row's options: tiny digits in hint mode's look, once focus settles (optkeys.ts). */
3107
3149
  .knobs-optkeys{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;z-index:5}
3108
- .knobs-panel .knobs-optkey{position:absolute;transform:translateY(-50%);display:inline-flex;align-items:center;height:12px;padding:0 3px;border-radius:var(--_k-radius-sm);background:var(--_k-accent);color:var(--_k-bg);box-shadow:0 1px 3px rgba(0,0,0,calc(.35 * var(--k-lift)));
3150
+ .knobs-panel .knobs-optkey{position:absolute;transform:translateY(-50%);display:inline-flex;white-space:nowrap;align-items:center;height:12px;padding:0 3px;border-radius:var(--_k-radius-sm);background:var(--_k-accent);color:var(--_k-bg);box-shadow:0 1px 3px rgba(0,0,0,calc(.35 * var(--k-lift)));
3109
3151
  font:700 calc(var(--_k-text-cap) * .85)/1 var(--_k-mono);letter-spacing:.04em;pointer-events:none;opacity:0;transition:opacity .12s var(--k-ease)}
3110
3152
  .knobs-panel .knobs-optkey-range{transform:translate(-100%,-50%)}
3111
3153
  .knobs-panel .knobs-optkey[data-shown]{opacity:.85;transition-duration:.15s}
@@ -3147,7 +3189,18 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3147
3189
  .knobs-menu-item[data-separated]{margin-top:calc(var(--_k-unit) * 2 + 1px)}
3148
3190
  .knobs-menu-item[data-separated]::before{content:"";position:absolute;left:8px;right:8px;top:calc(var(--_k-unit) * -1 - 1px);height:1px;transform:scaleY(.5);background:var(--_k-border)}
3149
3191
 
3192
+ /* Text: a field beside its label, or (multiline) a box under it that grows with its lines */
3193
+ .knobs-panel .knobs-text{flex:1 1 auto;min-width:0;max-width:62%;height:var(--_k-control-h);margin:0;padding:0 8px;border:0;border-radius:var(--_k-radius-md);outline:none;background:var(--_k-segmented-track);color:var(--_k-fg);caret-color:var(--_k-accent);font:inherit;font-size:var(--_k-text-control);-webkit-user-select:text;user-select:text;cursor:text}
3194
+ .knobs-panel .knobs-text::placeholder{color:var(--_k-fg-3);opacity:1}
3195
+ .knobs-panel .knobs-text:is(:hover,:focus){background:color-mix(in srgb,var(--_k-fill-selected) 70%,var(--_k-segmented-track))}
3196
+ .knobs-panel .knobs-text::selection{background:color-mix(in srgb,var(--_k-accent) 24%,transparent);color:var(--_k-fg)}
3197
+ .knobs-panel .knobs-insp[data-multiline]{flex-direction:column;align-items:stretch;justify-content:flex-start;gap:calc(var(--_k-unit) * 1.5);height:auto;padding-block:calc(var(--_k-unit) * 2)}
3198
+ .knobs-panel .knobs-insp[data-multiline]>span:first-child{align-self:flex-start;max-width:100%}
3199
+ .knobs-panel textarea.knobs-text{max-width:none;height:auto;min-height:calc(2lh + 12px);max-height:calc(8lh + 12px);margin:0;padding:6px 8px;resize:none;field-sizing:content;font:inherit;font-size:var(--_k-text-control);line-height:1.4}
3200
+ .knobs-panel :is([data-disabled],[data-readonly]) .knobs-text{cursor:default}
3201
+
3150
3202
  /* Status & Transfer Textarea */
3203
+ .knobs-panel .knobs-empty{margin:0;padding:calc(var(--_k-unit) * 2) var(--_k-inset);color:var(--_k-fg-3);font-size:var(--_k-text-control);overflow-wrap:anywhere}
3151
3204
  .knobs-panel .knobs-status{margin:6px 0 0;padding:0 var(--_k-inset);color:var(--_k-fg-2);font-size:var(--_k-text-meta);overflow-wrap:anywhere}
3152
3205
 
3153
3206
  .knobs-panel textarea{width:100%;min-height:84px;margin-top:6px;padding:6px var(--_k-inset);resize:vertical;font:var(--_k-text-meta)/1.4 var(--_k-mono)}
@@ -3179,11 +3232,11 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3179
3232
  .knobs-panel[data-gpu-active="true"] .knobs-preset-chip::after,
3180
3233
  .knobs-panel[data-gpu-active="true"] :is(.knobs-tile:not(.knobs-live)>.knobs-veil,.knobs-tile:not(.knobs-live)>.knobs-mark,.knobs-section-head>.knobs-mark){opacity:0!important}
3181
3234
  /* Parts the canvas doesn't draw (shortcuts, status, editors) sit above it. */
3182
- .knobs-panel[data-gpu-active="true"] :where(.knobs-popover,.knobs-status,textarea,.knobs-apply-btn){position:relative;z-index:2147480003}
3235
+ .knobs-panel[data-gpu-active="true"] :where(.knobs-popover,.knobs-status,textarea,.knobs-text,.knobs-apply-btn){position:relative;z-index:2147480003}
3183
3236
  .knobs-panel[data-gpu-active="true"] :is(.knobs-number,.knobs-preset-name){z-index:2147480003}
3184
3237
  /* A row accessory is the host's own DOM, so it sits above the canvas too (its row then can't
3185
3238
  contain its layout, which would trap it in a stacking context under the canvas). */
3186
- .knobs-panel[data-gpu-active="true"] .knobs-row[data-accessory]{contain:style}
3239
+ .knobs-panel[data-gpu-active="true"] :is(.knobs-row[data-accessory],.knobs-insp[data-kind="text"]){contain:style}
3187
3240
  :where(.knobs-panel[data-gpu-active="true"] .knobs-row>.knobs-accessory){position:relative}
3188
3241
  .knobs-panel[data-gpu-active="true"] .knobs-row>.knobs-accessory{z-index:2147480003}
3189
3242
 
@@ -3565,6 +3618,8 @@ function changeCue(field, before, after) {
3565
3618
  let options = field.options ?? [];
3566
3619
  return { cue: "select", detail: { direction: options.indexOf(after) - options.indexOf(before) }, detents: 1, at: 1, was: 0 };
3567
3620
  }
3621
+ if (field.kind === "text")
3622
+ return null;
3568
3623
  if (field.kind === "keybind")
3569
3624
  return after ? { cue: "capture", detail: {}, detents: 1, at: 1, was: 0 } : { cue: "select", detail: { direction: -1 }, detents: 1, at: 1, was: 0 };
3570
3625
  let { min, max, step } = field, a = numbers(before), b = numbers(after);
@@ -4390,4 +4445,4 @@ function createLayer(doc) {
4390
4445
  };
4391
4446
  }
4392
4447
 
4393
- export { resolveLook, themeOf, applyLook, panelLook, CHECK_POINTS, CHECK_PATH, iconPath, iconSvg, readSession, writeSession, rolling_css_default, FILL, LEAD, trailScale, Edges, POP_MS, popScale, settleScale, VelocityTracker, clamp, Roll, rollTarget, numeral, entranceOffset, FLASH_MS, flash, flashOf, offRow, precisionRate, RULER, RULER_NEAR, RULER_FADE, rulerLevels, rulerAlpha, rulerDepth, THIN, THINNEST, thinOf, roundOf, stretchOf, stretchAt, giveRoom, detentScale, detentsOf, roundValues, unwarp, Notches, escapeTo, commandScope, scopeLabel, crumbTrail, escTarget, digitOption, digitHints, createCrumbs, TOKENS, knobs, aliased, injectTokens, DELAYED, injectStyles, resolveSound, sound, SOUND_PREF, soundPanel, changed, cue, createHintController, pageHints, toggleLayer };
4448
+ export { resolveLook, themeOf, applyLook, panelLook, CHECK_POINTS, CHECK_PATH, iconPath, iconSvg, readSession, writeSession, rolling_css_default, FILL, LEAD, trailScale, Edges, POP_MS, popScale, settleScale, VelocityTracker, clamp, Roll, rollTarget, numeral, entranceOffset, FLASH_MS, flash, flashOf, offRow, precisionRate, RULER, RULER_NEAR, RULER_FADE, rulerLevels, rulerAlpha, rulerDepth, THIN, THINNEST, thinOf, roundOf, stretchOf, stretchAt, giveRoom, detentScale, detentsOf, roundValues, unwarp, Notches, escapeTo, commandScope, scopeLabel, crumbTrail, escTarget, digitOption, digitRoute, digitHints, createCrumbs, TOKENS, knobs, aliased, injectTokens, DELAYED, injectStyles, resolveSound, sound, SOUND_PREF, soundPanel, changed, cue, createHintController, pageHints, toggleLayer };
package/dist/dev/index.js CHANGED
@@ -1,6 +1,4 @@
1
1
  import {
2
- __preload,
3
- __chunks,
4
2
  maxChroma,
5
3
  oklchToRgb,
6
4
  parseColor2,
@@ -27,6 +25,8 @@ import {
27
25
  pickMeta,
28
26
  isReadout2,
29
27
  hasValue2,
28
+ variantsOf,
29
+ pinnedKeys,
30
30
  SPRING2,
31
31
  valueKeys,
32
32
  knob2,
@@ -44,6 +44,7 @@ import {
44
44
  angle2,
45
45
  seed2,
46
46
  keybind2,
47
+ text2,
47
48
  gradient2,
48
49
  curve2,
49
50
  toolbarShortcut2,
@@ -75,8 +76,7 @@ import {
75
76
  browserStorage,
76
77
  urlFlag2,
77
78
  storageKey
78
- } from "./chunk-3ygqgy1x.js";
79
- __chunks(import.meta.url,["kdk2476q","5pb7ymrd","a7cjgy8x","3ygqgy1x","dyq4m24m"],[["./index.js",3],["./chunk-5pb7ymrd.js",3,4],["./chunk-a7cjgy8x.js",3,4],["./chunk-3ygqgy1x.js"],["./chunk-dyq4m24m.js",3]],0);
79
+ } from "./chunk-7kns3qe6.js";
80
80
  // src/core/random.ts
81
81
  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
82
  function pick(field, random) {
@@ -119,6 +119,7 @@ function pick(field, random) {
119
119
  case "seed":
120
120
  return lattice(field.min, field.max, 1, r());
121
121
  case "keybind":
122
+ case "text":
122
123
  return field.value;
123
124
  case "gradient":
124
125
  return randomStops(field.value.length, r);
@@ -204,6 +205,7 @@ function around(field, base, random) {
204
205
  ]);
205
206
  case "seed":
206
207
  case "keybind":
208
+ case "text":
207
209
  return pick(field, random);
208
210
  }
209
211
  }
@@ -303,6 +305,7 @@ function shift(field, value, random) {
303
305
  case "seed":
304
306
  return nudge(value, field.min, field.max, 1, random);
305
307
  case "keybind":
308
+ case "text":
306
309
  return value;
307
310
  case "gradient":
308
311
  return mutateStops(value, random, MUTATE.hue, MUTATE.lightness);
@@ -594,14 +597,42 @@ function defineControls(id, input, options = {}) {
594
597
  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
598
  for (let cb of [...listeners])
596
599
  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
- ["default", locked],
599
- ...Object.entries(options.presets ?? {}).map(([id, patch]) => {
600
- if (id === "default" || id === "custom")
601
- throw Error(`Reserved preset id: ${id}`);
602
- return [id, normalize(schema, { ...locked, ...patch })];
603
- })
604
- ])), presets = buildPresets(), buildGroupPresets = () => Object.freeze(Object.fromEntries(Object.entries(options.groupPresets ?? {}).map(([group, list]) => {
600
+ }, 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 = () => {
601
+ if (pinnedOf?.schema !== schema)
602
+ pinnedOf = { schema, keys: pinnedKeys(schema, options.pinned) };
603
+ return pinnedOf.keys;
604
+ }, variantsChoice = () => variantsOf(schema, (key) => pinned().has(key)), unpinned = (values) => {
605
+ let keys = pinned();
606
+ if (keys.size === 0)
607
+ return values;
608
+ let out = { ...values };
609
+ for (let k of keys)
610
+ if (Object.hasOwn(locked, k))
611
+ out[k] = locked[k];
612
+ return Object.freeze(out);
613
+ }, presetPatch = (values) => {
614
+ let keys = pinned();
615
+ if (keys.size === 0)
616
+ return values;
617
+ return Object.fromEntries(Object.entries(values).filter(([k]) => !keys.has(k)));
618
+ }, buildPresets = () => {
619
+ let own = options.presets ?? {}, v = variantsChoice();
620
+ if (v && Object.hasOwn(own, v.field.value))
621
+ throw Error(`The default variant (${v.field.value}) is the schema's defaults; set its values there.`);
622
+ let ids = [
623
+ ...v ? v.field.options.filter((o) => o !== v.field.value) : [],
624
+ ...Object.keys(own).filter((id) => !v?.field.options.includes(id))
625
+ ];
626
+ return Object.freeze(Object.fromEntries([
627
+ ["default", locked],
628
+ ...ids.map((id) => {
629
+ if (id === "default" || id === "custom")
630
+ throw Error(`Reserved preset id: ${id}`);
631
+ let variant = v?.field.options.includes(id) ? { [v.key]: id } : {};
632
+ return [id, unpinned(normalize(schema, { ...locked, ...variant, ...own[id] }))];
633
+ })
634
+ ]));
635
+ }, presets = buildPresets(), buildGroupPresets = () => Object.freeze(Object.fromEntries(Object.entries(options.groupPresets ?? {}).map(([group, list]) => {
605
636
  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])));
606
637
  return [
607
638
  group,
@@ -700,7 +731,7 @@ function defineControls(id, input, options = {}) {
700
731
  else if (ref?.kind === "variant") {
701
732
  let found = list?.find((v) => v.id === ref.id);
702
733
  if (found)
703
- out = normalize(schema, found.values);
734
+ out = unpinned(normalize(schema, found.values));
704
735
  }
705
736
  let groups = Object.entries(b.groups);
706
737
  if (groups.length) {
@@ -712,11 +743,10 @@ function defineControls(id, input, options = {}) {
712
743
  return baseCache = { base: b, schema, presets, groupPresets, variants: list, values }, values;
713
744
  }
714
745
  let covered = (key) => {
715
- let b = activeBase();
716
- if (b.preset)
746
+ let b = activeBase(), group = schema[key]?.group;
747
+ if (group !== void 0 && Object.hasOwn(b.groups, group))
717
748
  return !0;
718
- let group = schema[key]?.group;
719
- return group !== void 0 && Object.hasOwn(b.groups, group);
749
+ return b.preset !== null && !pinned().has(key);
720
750
  };
721
751
  function declaredShortcut(id) {
722
752
  let slash = id.indexOf("/"), key = slash < 0 ? id : id.slice(0, slash), field = Object.hasOwn(schema, key) ? schema[key] : void 0;
@@ -986,7 +1016,7 @@ function defineControls(id, input, options = {}) {
986
1016
  listen(cb) {
987
1017
  return readoutListeners.add(cb), () => readoutListeners.delete(cb);
988
1018
  }
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));
1019
+ }, 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
1020
  function offBase(keys) {
991
1021
  if (!hasBase())
992
1022
  return !1;
@@ -1070,7 +1100,11 @@ function defineControls(id, input, options = {}) {
1070
1100
  sectionDescription: (section) => options.sectionDescriptions?.[section] ?? fieldLabels().sectionText.get(section),
1071
1101
  groupFolded: options.groupFolded,
1072
1102
  sectionFolded: options.sectionFolded,
1073
- presetLabel: (presetId) => options.presetLabels?.[presetId] ?? (presetId === "default" ? options.defaultLabel : void 0) ?? presetId,
1103
+ presetLabel: (presetId) => {
1104
+ let v = variantsChoice(), option = presetId === "default" ? v?.field.value : presetId;
1105
+ return options.presetLabels?.[presetId] ?? (presetId === "default" ? options.defaultLabel : void 0) ?? (v && option !== void 0 && v.field.options.includes(option) ? v.field.labels?.[option] ?? option : presetId);
1106
+ },
1107
+ isPinned: (key) => pinned().has(key),
1074
1108
  get,
1075
1109
  set,
1076
1110
  sync,
@@ -1345,9 +1379,12 @@ function defineControls(id, input, options = {}) {
1345
1379
  preset(id, presetOptions) {
1346
1380
  if (!Object.hasOwn(presets, id))
1347
1381
  throw Error(`Unknown preset: ${id}`);
1348
- write(presets[id], { source: "preset", coalesce: presetOptions?.coalesce }, id === "default" ? NO_BASE : makeBase(Object.freeze({ kind: "preset", id }), {}));
1382
+ write(presetPatch(presets[id]), { source: "preset", coalesce: presetOptions?.coalesce }, id === "default" ? NO_BASE : makeBase(Object.freeze({ kind: "preset", id }), {}));
1383
+ },
1384
+ matchingPreset: () => {
1385
+ let cur = unpinned(get());
1386
+ return Object.keys(presets).find((id) => equal(cur, presets[id])) ?? "custom";
1349
1387
  },
1350
- matchingPreset: () => Object.keys(presets).find((id) => equal(get(), presets[id])) ?? "custom",
1351
1388
  get groupPresets() {
1352
1389
  return groupPresets;
1353
1390
  },
@@ -1378,11 +1415,11 @@ function defineControls(id, input, options = {}) {
1378
1415
  code: (format, values) => formatCode(values ?? get(), format),
1379
1416
  variants,
1380
1417
  attach(element, chip) {
1381
- let load = options.ship ? () => options.ship().then((m) => m.attach) : () => (__preload("a7cjgy8x"), import("./chunk-a7cjgy8x.js")).then((m) => m.attach);
1418
+ let load = options.ship ? () => options.ship().then((m) => m.attach) : () => import("./chunk-ab7vfghg.js").then((m) => m.attach);
1382
1419
  return live(load, (attach) => attach(controls, element, chip), (detach) => detach());
1383
1420
  },
1384
1421
  mount(container, mountOptions) {
1385
- let load = options.ship ? () => options.ship().then((m) => m.mountPanel) : () => (__preload("5pb7ymrd"), import("./chunk-5pb7ymrd.js")).then((m) => m.mountPanel);
1422
+ let load = options.ship ? () => options.ship().then((m) => m.mountPanel) : () => import("./chunk-9qtvxa6k.js").then((m) => m.mountPanel);
1386
1423
  return live(load, (mountPanel) => {
1387
1424
  let panel = mountPanel(controls, container, mountOptions);
1388
1425
  return mountOptions?.onReady?.(panel), panel;
@@ -1419,7 +1456,7 @@ function descriptorToField(d) {
1419
1456
  if (readoutDescriptor(d))
1420
1457
  return readoutField(d);
1421
1458
  if (d.kind === "toolbar")
1422
- return toolbar2(d.items.map(({ key, ...item }) => ({ ...item, id: key })), meta);
1459
+ return toolbar2(d.items.map(({ key, ...item }) => ({ ...item, id: key })), d.labels !== void 0 ? { ...meta, labels: d.labels } : meta);
1423
1460
  if (d.bind)
1424
1461
  return flatSpring2({}, { ...meta, id: d.key }).duration;
1425
1462
  if (d.kind === "knob" || d.kind === "number") {
@@ -1494,6 +1531,12 @@ function descriptorToField(d) {
1494
1531
  });
1495
1532
  if (d.kind === "keybind")
1496
1533
  return keybind2(typeof d.default === "string" ? d.default : "", meta);
1534
+ if (d.kind === "text")
1535
+ return text2(typeof d.default === "string" ? d.default : "", {
1536
+ ...d.multiline ? { multiline: !0 } : {},
1537
+ ...d.placeholder ? { placeholder: d.placeholder } : {},
1538
+ ...meta
1539
+ });
1497
1540
  if (d.kind === "gradient") {
1498
1541
  let fallback = [
1499
1542
  { at: 0, color: "#000000" },
@@ -1572,7 +1615,7 @@ var decimal = (n) => String(Math.round(n * 1e4) / 1e4), swiftLiteral = (d, v) =>
1572
1615
  let [a, b] = v;
1573
1616
  return `${decimal(a)}...${decimal(b)}`;
1574
1617
  }
1575
- if (d.kind === "keybind")
1618
+ if (d.kind === "keybind" || d.kind === "text")
1576
1619
  return JSON.stringify(v);
1577
1620
  if (d.kind === "gradient")
1578
1621
  return `Gradient(stops: [${(Array.isArray(v) ? v : []).map((s) => {
@@ -1601,6 +1644,7 @@ var decimal = (n) => String(Math.round(n * 1e4) / 1e4), swiftLiteral = (d, v) =>
1601
1644
  range: "ClosedRange<Double>",
1602
1645
  seed: "Int",
1603
1646
  keybind: "String",
1647
+ text: "String",
1604
1648
  gradient: "Gradient",
1605
1649
  curve: "[CGPoint]"
1606
1650
  };
@@ -1674,6 +1718,7 @@ function createRemoteControls(id, descriptors, options = {}) {
1674
1718
  tunable: options.tunable,
1675
1719
  history: options.history,
1676
1720
  presets: options.presets,
1721
+ pinned: options.pinned,
1677
1722
  groupPresets: options.groupPresets,
1678
1723
  onPreset: options.onPreset,
1679
1724
  actions: options.actions,
@@ -1809,6 +1854,7 @@ export {
1809
1854
  springCss2 as springCss,
1810
1855
  springMotion2 as springMotion,
1811
1856
  table2 as table,
1857
+ text2 as text,
1812
1858
  toggle2 as toggle,
1813
1859
  toolbar2 as toolbar,
1814
1860
  toolbarShortcut2 as toolbarShortcut,