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

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.
@@ -10,7 +10,7 @@ import {
10
10
  DEFAULT_LINEAR,
11
11
  motionCss,
12
12
  storageKey
13
- } from "./chunk-mjdgzad5.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;
@@ -2391,7 +2396,7 @@ ${GRAD_PROPS}
2391
2396
 
2392
2397
  /* The chip: a round cog button, or a capsule with a label beside the cog. */
2393
2398
  .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;
2394
- 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))),inset 0 var(--k-rim-width) 0 var(--_k-rim);transition:color .15s,border-color .15s,transform .2s var(--k-ease),opacity .2s,scale .12s var(--k-ease),${TOKEN_TRANSITIONS};}
2399
+ 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};}
2395
2400
  .knobs-chip[data-label]{width:auto;padding:0 10px 0 7px}
2396
2401
  .knobs-chip .knobs-icon{display:block;flex:none;width:15px;height:15px}
2397
2402
  .knobs-chip:hover{color:var(--_k-fg);--k-chip-edge:var(--_k-border-strong)}
@@ -2418,9 +2423,10 @@ ${GRAD_PROPS}
2418
2423
  /* Panel material: --k-glass (0 opaque … 1 clearest) thins the surface over a blurred, saturated
2419
2424
  backdrop, toned toward the theme (--k-glass-tone dims it under dark panels) so text keeps its
2420
2425
  contrast. Its edge is a hairline (--k-edge, --k-rim-width wide) and a hairline of light (--k-rim)
2421
- catches its top. */
2426
+ catches its top. The light rides on the edge (a shadow raised by its width, so only its top
2427
+ shows), not inside the card's 1px border, where it read as a second edge. */
2422
2428
  .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);
2423
- 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 0 0 var(--k-rim-width) var(--ka-edge),inset 0 var(--k-rim-width) 0 var(--_k-rim),var(--ka-glow);scrollbar-width:thin;
2429
+ 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;
2424
2430
  /* Armed (focus inside: keys come here), the edge takes the accent and a faint glow lifts it. */
2425
2431
  --ka-edge:color-mix(in srgb,var(--_k-accent) calc(var(--k-armed) * var(--k-armed-edge) * 100%),var(--_k-border-subtle));
2426
2432
  --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);
@@ -2433,7 +2439,7 @@ ${GRAD_PROPS}
2433
2439
  .knobs-panel[data-morphing]{background:transparent;border-color:transparent;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;animation:none}
2434
2440
  .knobs-morph{position:fixed;left:0;top:0;z-index:2147480001;pointer-events:none;box-sizing:border-box;--p:0;--spec:0;
2435
2441
  background:color-mix(in srgb,var(--_k-bg) calc(100% - var(--_k-glass) * 20% * var(--p)),transparent);border:1px solid transparent;
2436
- box-shadow: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)),inset 0 var(--k-rim-width) 0 color-mix(in srgb,var(--_k-rim) calc(var(--p) * 100%),transparent);
2442
+ 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));
2437
2443
  --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)));
2438
2444
  -webkit-backdrop-filter:var(--k-backdrop);backdrop-filter:var(--k-backdrop)}
2439
2445
  .knobs-morph::before,.knobs-morph::after{content:"";position:absolute;inset:-1px;border-radius:inherit;pointer-events:none}
@@ -2441,7 +2447,7 @@ ${GRAD_PROPS}
2441
2447
  .knobs-morph::after{box-shadow:var(--_k-shadow-overlay);opacity:var(--p)}
2442
2448
  /* Meanwhile the chip is just its label, fading on the shape. */
2443
2449
  .knobs-chip.knobs-chip[data-morph]{visibility:visible;pointer-events:auto;transform:none;transition:none;background:transparent;border-color:transparent;box-shadow:none}
2444
- .knobs-panel.knobs-inline{position:relative;z-index:auto;left:auto!important;top:auto!important;max-width:100%;max-height:none;box-shadow:0 0 0 var(--k-rim-width) color-mix(in srgb,var(--ka-edge) calc(var(--k-chrome) * 100%),transparent),inset 0 var(--k-rim-width) 0 color-mix(in srgb,var(--_k-rim) calc(var(--k-chrome) * 100%),transparent),var(--ka-glow);animation:none;
2450
+ .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;
2445
2451
  /* --k-chrome 0: an embedded panel sheds its card (background, border, rim) for the host's column,
2446
2452
  border width too, so its rows line up with the column's edges. */
2447
2453
  border-width:calc(var(--k-chrome) * 1px);
@@ -2495,7 +2501,7 @@ ${GRAD_PROPS}
2495
2501
  .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
2502
  .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
2503
  .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}
2504
+ .knobs-crumbs>.knobs-crumb-crumb{pointer-events:auto;cursor:default}
2499
2505
  .knobs-crumbs>.knobs-crumb-crumb[data-deep]{color:var(--_k-fg)}
2500
2506
  .knobs-crumbs>.knobs-crumb-crumb:not([data-deep]):hover{color:var(--_k-fg-2)}
2501
2507
  .knobs-crumbs>.knobs-crumb-sep{color:color-mix(in srgb,var(--_k-fg-3) 70%,transparent)}
@@ -2512,40 +2518,53 @@ ${GRAD_PROPS}
2512
2518
  .knobs-panel[data-gpu-active="true"] .knobs-die-icon>canvas{visibility:hidden}
2513
2519
  /* The tools sit quiet (--k-dim) until the panel has the pointer or keyboard focus, then read as
2514
2520
  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}
2521
+ .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
2522
  .knobs-panel:is(:hover,:has(:focus-visible)) header button{color:var(--_k-fg-2)}
2517
2523
  .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
2524
  .knobs-panel header button[disabled]{opacity:var(--k-disabled-opacity);cursor:default}
2519
2525
  /* The close X's ink sits a fifth of its box in from each side, a digit's barely: nudged right, its
2520
2526
  ink ends where the values' digits do. */
2521
2527
  .knobs-panel header .knobs-close>.knobs-icon{translate:calc(var(--k-icon-size) * .1) 0}
2528
+ /* In a split pane's header (dock.ts) the buttons are the pane's: its size, ink and wash, which
2529
+ comes on at once and eases off. Outside the panel, they bring its resets with them. */
2530
+ .knobs-head-actions[data-docked]{--k-icon-size:13px;--k-icon-btn:20px;margin-right:0}
2531
+ .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);
2532
+ 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)}
2533
+ .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)}
2534
+ .knobs-head-actions[data-docked] .knobs-icon>path{fill:currentColor}
2535
+ .knobs-head-actions[data-docked] button:focus-visible{--k-hover-t:calc(var(--_k-hover-in) * 1ms)}
2522
2536
  /* Undo · Redo sit apart from Reset · Copy · Random. Undo, Redo and Reset (and a group's reset)
2523
2537
  keep their slots while they're away and bloom in and out of them: nothing beside them moves. */
2524
2538
  .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}
2539
+ :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}}
2540
+ :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;
2541
+ 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}
2542
+ /* Docked, a slot opens and closes with its button (the width above) rather than waiting: the
2543
+ pane's title keeps the room. */
2544
+ .knobs-head-actions[data-docked] button[data-shown="false"]{width:0}
2545
+ .knobs-head-actions[data-docked]>.knobs-head-history{transition:margin ${BLOOM_IN}}
2546
+ .knobs-head-actions[data-docked]>.knobs-head-history:not(:has(>[data-shown="true"]:not([hidden]))){margin-right:0}
2528
2547
  @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}}
2548
+ :is(.knobs-panel :is(.knobs-head-actions,.knobs-group-head),.knobs-head-actions[data-docked]) button[data-shown]{transition:opacity .15s!important}
2549
+ :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
2550
  /* A press flash (flash.ts): from a key or a click, the button's wash brightens at once and eases
2532
2551
  back to its own, its ink with it. Two names, so a second press restarts it. */
2533
2552
  @keyframes knobs-flash-a{from{background-color:var(--k-flash-bg);color:var(--_k-fg)}}
2534
2553
  @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)}
2554
+ .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%)}
2555
+ .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)}
2556
+ .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
2557
  /* Copied: the glyph shrinks away and a check draws itself in its place, held, then they trade
2539
2558
  back (the check fading). Driven by --k-copy (the glyph), --k-check (how much of the check is
2540
2559
  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;
2560
+ .knobs-panel header .knobs-copy,.knobs-head-actions[data-docked] .knobs-copy{--k-copy:1;--k-check:0;--k-check-a:0;
2542
2561
  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);
2562
+ .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
2563
  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
2564
  .knobs-copy>.knobs-icon:not(.knobs-check){opacity:var(--k-copy);scale:calc(.5 + .5 * var(--k-copy))}
2546
2565
  .knobs-copy>.knobs-check{position:absolute;inset:0;margin:auto;opacity:var(--k-check-a)}
2547
2566
  /* (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))}
2567
+ .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
2568
  .knobs-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
2550
2569
 
2551
2570
  /* Shortcuts Popover */
@@ -2567,6 +2586,15 @@ ${GRAD_PROPS}
2567
2586
  .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
2587
  .knobs-panel .knobs-preset-chip:hover{color:var(--_k-fg)}
2569
2588
  .knobs-panel .knobs-preset-chip[aria-checked="true"]{color:var(--_k-fg);font-weight:var(--_k-weight-title)}
2589
+ /* A list (presetDisplay "list", or chips that would take more than two lines): rows like a
2590
+ menu's, their names on the rows' text edge and each wash bleeding 8 px past it, the selection a
2591
+ quiet wash that glides between them. Hover is a wash too, on at once. */
2592
+ .knobs-presets[data-display="list"]{padding:0;background:transparent}
2593
+ .knobs-presets[data-display="list"]>.knobs-presets-track{grid-template-columns:minmax(0,1fr);gap:0;margin:0 calc(var(--_k-inset) - 8px)}
2594
+ .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)}
2595
+ .knobs-panel .knobs-presets[data-display="list"] .knobs-preset-chip:hover:not([aria-checked="true"]){background:var(--_k-row-hover)!important}
2596
+ .knobs-panel .knobs-presets[data-display="list"] .knobs-preset-chip::after{top:50%;right:8px;margin-top:-2px}
2597
+ .knobs-presets[data-display="list"] .knobs-presets-pill{background:var(--_k-fill-selected);box-shadow:none}
2570
2598
  .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
2599
  .knobs-panel .knobs-preset-save:hover{color:var(--_k-fg)!important}
2572
2600
  /* The active preset's modified mark: the values have drifted off it (a row's dot, in its corner). */
@@ -2911,6 +2939,14 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
2911
2939
  :where(.knobs-panel)[data-frame="flat"]{--k-group-divider:var(--_k-border);--k-group-divider-gap:var(--_k-gap)}
2912
2940
  :where(.knobs-panel.knobs-inline:not(.knobs-page))[data-frame="flat"]{--k-chrome:0;--k-pad:0px}
2913
2941
  .knobs-panel[data-frame="flat"] .knobs-card{background:transparent;border-radius:0}
2942
+ /* One surface: an inline panel on another card (a split's pane, anything .knobs-surface, or in
2943
+ [data-nested]) takes the flat frame by itself: no padding (its rows keep their inset, so their
2944
+ text sits on the card's, and its preset chips line up with them), groups as plain sections,
2945
+ rows on hairlines. */
2946
+ :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)}
2947
+ :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip,[data-nested]) .knobs-panel.knobs-inline:not(.knobs-page){padding:0}
2948
+ :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}
2949
+ :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)}
2914
2950
  .knobs-panel[data-frame="flat"][data-rows] .knobs-card>*,.knobs-panel[data-frame="flat"][data-rows] .knobs-sub{background:transparent;border-radius:0}
2915
2951
 
2916
2952
  /* Disabled rows dim and take no edits (focus still reaches them, to read); read-only rows keep
@@ -3063,6 +3099,10 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3063
3099
  .knobs-panel .knobs-tool[disabled]{opacity:var(--k-disabled-opacity);cursor:default}
3064
3100
  .knobs-tool .knobs-icon{flex:none;width:var(--k-icon-sm);height:var(--k-icon-sm)}
3065
3101
  .knobs-tool-label{min-width:0;overflow:hidden;text-overflow:ellipsis}
3102
+ /* Icons only (labels that don't fit one row, or labels: false): one row of equal buttons, each
3103
+ its icon centred, its label and shortcut in its tooltip. */
3104
+ .knobs-panel .knobs-toolbar[data-icons] .knobs-tool{justify-content:center;padding:0;cursor:default}
3105
+ .knobs-panel .knobs-toolbar[data-icons] :is(.knobs-tool-label,.knobs-tool-key){display:none}
3066
3106
  /* The shortcut sits at the button's end, so a column of buttons lines their chips up. */
3067
3107
  .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}
3068
3108
  .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}
@@ -3108,7 +3148,7 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3108
3148
 
3109
3149
  /* A focused row's options: tiny digits in hint mode's look, once focus settles (optkeys.ts). */
3110
3150
  .knobs-optkeys{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;z-index:5}
3111
- .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)));
3151
+ .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)));
3112
3152
  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)}
3113
3153
  .knobs-panel .knobs-optkey-range{transform:translate(-100%,-50%)}
3114
3154
  .knobs-panel .knobs-optkey[data-shown]{opacity:.85;transition-duration:.15s}
@@ -3150,7 +3190,18 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3150
3190
  .knobs-menu-item[data-separated]{margin-top:calc(var(--_k-unit) * 2 + 1px)}
3151
3191
  .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)}
3152
3192
 
3193
+ /* Text: a field beside its label, or (multiline) a box under it that grows with its lines */
3194
+ .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}
3195
+ .knobs-panel .knobs-text::placeholder{color:var(--_k-fg-3);opacity:1}
3196
+ .knobs-panel .knobs-text:is(:hover,:focus){background:color-mix(in srgb,var(--_k-fill-selected) 70%,var(--_k-segmented-track))}
3197
+ .knobs-panel .knobs-text::selection{background:color-mix(in srgb,var(--_k-accent) 24%,transparent);color:var(--_k-fg)}
3198
+ .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)}
3199
+ .knobs-panel .knobs-insp[data-multiline]>span:first-child{align-self:flex-start;max-width:100%}
3200
+ .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}
3201
+ .knobs-panel :is([data-disabled],[data-readonly]) .knobs-text{cursor:default}
3202
+
3153
3203
  /* Status & Transfer Textarea */
3204
+ .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}
3154
3205
  .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}
3155
3206
 
3156
3207
  .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)}
@@ -3182,11 +3233,11 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
3182
3233
  .knobs-panel[data-gpu-active="true"] .knobs-preset-chip::after,
3183
3234
  .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}
3184
3235
  /* Parts the canvas doesn't draw (shortcuts, status, editors) sit above it. */
3185
- .knobs-panel[data-gpu-active="true"] :where(.knobs-popover,.knobs-status,textarea,.knobs-apply-btn){position:relative;z-index:2147480003}
3236
+ .knobs-panel[data-gpu-active="true"] :where(.knobs-popover,.knobs-status,textarea,.knobs-text,.knobs-apply-btn){position:relative;z-index:2147480003}
3186
3237
  .knobs-panel[data-gpu-active="true"] :is(.knobs-number,.knobs-preset-name){z-index:2147480003}
3187
3238
  /* A row accessory is the host's own DOM, so it sits above the canvas too (its row then can't
3188
3239
  contain its layout, which would trap it in a stacking context under the canvas). */
3189
- .knobs-panel[data-gpu-active="true"] .knobs-row[data-accessory]{contain:style}
3240
+ .knobs-panel[data-gpu-active="true"] :is(.knobs-row[data-accessory],.knobs-insp[data-kind="text"]){contain:style}
3190
3241
  :where(.knobs-panel[data-gpu-active="true"] .knobs-row>.knobs-accessory){position:relative}
3191
3242
  .knobs-panel[data-gpu-active="true"] .knobs-row>.knobs-accessory{z-index:2147480003}
3192
3243
 
@@ -3568,6 +3619,8 @@ function changeCue(field, before, after) {
3568
3619
  let options = field.options ?? [];
3569
3620
  return { cue: "select", detail: { direction: options.indexOf(after) - options.indexOf(before) }, detents: 1, at: 1, was: 0 };
3570
3621
  }
3622
+ if (field.kind === "text")
3623
+ return null;
3571
3624
  if (field.kind === "keybind")
3572
3625
  return after ? { cue: "capture", detail: {}, detents: 1, at: 1, was: 0 } : { cue: "select", detail: { direction: -1 }, detents: 1, at: 1, was: 0 };
3573
3626
  let { min, max, step } = field, a = numbers(before), b = numbers(after);
@@ -4393,4 +4446,4 @@ function createLayer(doc) {
4393
4446
  };
4394
4447
  }
4395
4448
 
4396
- 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 };
4449
+ 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 };