@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.
- package/README.md +115 -13
- package/dist/dev/{chunk-3ygqgy1x.js → chunk-7kns3qe6.js} +43 -26
- package/dist/dev/{chunk-5pb7ymrd.js → chunk-9qtvxa6k.js} +1465 -1104
- package/dist/dev/{chunk-a7cjgy8x.js → chunk-ab7vfghg.js} +2 -5
- package/dist/dev/{chunk-dyq4m24m.js → chunk-nwfm1bfj.js} +92 -37
- package/dist/dev/index.js +71 -25
- package/dist/index.js +107 -23
- package/dist/panel.js +3091 -2528
- package/dist/review-react.js +8 -4
- package/dist/review-solid.js +10 -5
- package/dist/review.js +8 -4
- package/dist/types/core/remote.d.ts +10 -2
- package/dist/types/core/schema.d.ts +65 -5
- package/dist/types/core/store.d.ts +15 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/panel/dock.d.ts +9 -0
- package/dist/types/panel/gpu/renderer.d.ts +13 -1
- package/dist/types/panel/inspector.d.ts +11 -3
- package/dist/types/panel/presets.d.ts +21 -1
- package/dist/types/panel/scope.d.ts +15 -4
- package/dist/types/ui/canvas-css.d.ts +1 -0
- package/dist/types/ui/canvas-model.d.ts +116 -0
- package/dist/types/ui/canvas.d.ts +66 -0
- package/dist/types/ui/index.d.ts +2 -0
- package/dist/types/ui/react.d.ts +12 -0
- package/dist/types/ui/solid.d.ts +9 -0
- package/dist/ui-react.js +1020 -17
- package/dist/ui-solid.js +1032 -25
- package/dist/ui.js +986 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -202,10 +202,10 @@ keeps a gradient's stop count and walks its hue along a lightness ramp. On a cur
|
|
|
202
202
|
a built-in shape and jitters it. Mutate nudges colours as it does a `color` and moves stops and
|
|
203
203
|
points without letting them pass each other.
|
|
204
204
|
|
|
205
|
-
### Inspector fields: vectors, ranges, angles, seeds, key combinations and toolbars
|
|
205
|
+
### Inspector fields: vectors, ranges, angles, seeds, key combinations, text and toolbars
|
|
206
206
|
|
|
207
207
|
```ts
|
|
208
|
-
import { action, angle, defineControls, keybind, range, seed, toolbar, vec2, vec3 } from "knobs"
|
|
208
|
+
import { action, angle, defineControls, keybind, range, seed, text, toolbar, vec2, vec3 } from "knobs"
|
|
209
209
|
|
|
210
210
|
const node = defineControls("node", {
|
|
211
211
|
replay: action("Replay", { run: replay, shortcut: "Shift+K", global: true, rebind: true }),
|
|
@@ -222,6 +222,8 @@ const node = defineControls("node", {
|
|
|
222
222
|
yaw: angle(90), // degrees
|
|
223
223
|
seed: seed(1234), // a whole number
|
|
224
224
|
fire: keybind("Space"), // "Meta+Shift+K", "" for none
|
|
225
|
+
name: text("Lamp", { placeholder: "Untitled" }), // one line
|
|
226
|
+
notes: text("", { multiline: true }), // lines in a box
|
|
225
227
|
}, { onAction: (id) => run(id) })
|
|
226
228
|
```
|
|
227
229
|
|
|
@@ -246,7 +248,12 @@ const node = defineControls("node", {
|
|
|
246
248
|
Esc cancels and ⌫ clears. Not rolled by Random or Mutate. Letters follow the user's layout
|
|
247
249
|
(Dvorak's K records and matches as K), with ⌥ combinations read through the browser's layout
|
|
248
250
|
map; `comboFromEvent`, `matchesCombo` and `letterOf` do the same for a host's own keys.
|
|
249
|
-
- **`
|
|
251
|
+
- **`text(value?, { multiline?, placeholder? })`**: words typed into a field beside the label, or
|
|
252
|
+
with `multiline` a box under it that grows with its lines (up to eight, then it scrolls). Each
|
|
253
|
+
keystroke sets the value and one focus's typing is one undo step; Enter (`⌘Enter` in a box) or
|
|
254
|
+
Esc lets go. A one-line field turns a line break into a space. Presets, copy and remote
|
|
255
|
+
descriptors carry it like any string; Random and Mutate leave it alone.
|
|
256
|
+
- **`toolbar(items, { labels? })`**: a row of buttons with an icon (`play`, `pause`, `frame`,
|
|
250
257
|
`retrigger`, `clear`, `target`, `camera`, `sparkle`, … or SVG path data on a 256 grid), a label
|
|
251
258
|
and a shortcut chip. The buttons share equal columns, as many as fit, balanced across the rows
|
|
252
259
|
(four that don't fit in one row wrap as two and two), each chip at its button's end. A press calls the item's `run` and `onAction(id)`; `controls.action(key, id)`
|
|
@@ -255,7 +262,8 @@ const node = defineControls("node", {
|
|
|
255
262
|
`shortcut: { key, scope }` says where one works: `"panel"` (the default), `"page"` (as `global`)
|
|
256
263
|
or `"none"`, a chip that only shows a key the host binds itself (a canvas's `F`), so the panel's
|
|
257
264
|
own `f` and `g` still work beside it. The row holds no value; ←/→ move along it and Enter or
|
|
258
|
-
Space presses.
|
|
265
|
+
Space presses. When every button has an icon and the labels don't fit on one row, it's one row
|
|
266
|
+
of icons, each label and shortcut in its tooltip; `labels: false` always shows icons only.
|
|
259
267
|
- **`action(label, { run, shortcut?, global?, rebind? })`**: a button row, whose `shortcut` (a
|
|
260
268
|
combination such as `"Shift+K"`, or `{ key, scope }`) presses it under the same rules as a
|
|
261
269
|
toolbar button's: while the panel has focus, or anywhere on the page with `global: true` (not
|
|
@@ -268,9 +276,9 @@ const node = defineControls("node", {
|
|
|
268
276
|
button's too (by its id), which has no cap of its own to rebind it from.
|
|
269
277
|
|
|
270
278
|
Remote descriptors use the same names: `kind: "vec2" | "vec3" | "range" | "angle" | "seed" |
|
|
271
|
-
"keybind"` with `default`, `min`, `max`, `step`, `unit`, `decimals`, `link`, `linked` and `wrap`, and
|
|
272
|
-
`{ kind: "toolbar", items: [{ key, label, icon?, shortcut?, global?, disabled? }] }`; vectors take
|
|
273
|
-
`axes`. Each kind takes
|
|
279
|
+
"keybind" | "text"` with `default`, `min`, `max`, `step`, `unit`, `decimals`, `link`, `linked` and `wrap`, and
|
|
280
|
+
`{ kind: "toolbar", items: [{ key, label, icon?, shortcut?, global?, disabled? }], labels? }`; vectors take
|
|
281
|
+
`axes`, text `multiline` and `placeholder`. Each kind takes
|
|
274
282
|
`disabled`, `readOnly` and mixed values like the rest ("—" in place of the value).
|
|
275
283
|
|
|
276
284
|
Spring, point, easing, gradient and curve values compare by their contents everywhere (`isModified`, presets, undo, variants), and unchanged ones keep their identity across updates so memoized React children skip them. Random values stay in range people use (springs 0.2–0.9 s with bounce up to 0.45, mid-light colours of moderate chroma, any point, a named curve). Switches sit out of Random and Mutate unless a field says `random: true`: they usually show or hide things (a view, a scene's part), and a roll that hides the page isn't a new take on it.
|
|
@@ -317,6 +325,38 @@ live.watch("events", { level: "warn", text: "Gutter drip over budget" })
|
|
|
317
325
|
|
|
318
326
|
Readouts cost nothing at rest. They draw on one shared clock, only while on screen and only when something arrived, so a paused feed lets the page, and the GPU overlay, sleep. Graphics are canvases redrawn from a list of drawing ops (the GPU overlay draws the same ops). Text changes at most `hz` times a second (4 by default), in boxes that only grow, so nothing reflows as numbers change. Scales snap to round steps (1, 2 or 5 × 10ⁿ) and ease there on a spring, in log space, so a spike rescales smoothly. The store keeps each readout's history, so a panel mounted later starts with it. On their own, outside a panel: `readout(sparkline({ unit: "ms" }), { preset })` from `knobs/ui` returns `{ element, push(value), value, dispose() }`.
|
|
319
327
|
|
|
328
|
+
## Versions of a design: variants in the strip
|
|
329
|
+
|
|
330
|
+
When a page has several versions of one design (nine hero animations, three card layouts), the
|
|
331
|
+
preset strip at the top of the panel is where you switch them. Each version is a preset. Don't
|
|
332
|
+
add a `variant` choice beside it: two ways to switch the same thing is one too many.
|
|
333
|
+
|
|
334
|
+
When the page's code branches on which version is on, give that choice `variants: true`:
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
const controls = defineControls("upsell", {
|
|
338
|
+
variant: choice("Stand up", ["Stand up", "Anamorphic", "Roll", "Trace"], { variants: true }),
|
|
339
|
+
speed: knob(1, { min: 0.25, max: 2, step: 0.05 }),
|
|
340
|
+
}, {
|
|
341
|
+
presets: { Roll: { speed: 0.8 } }, // optional: values a variant changes beside itself
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
render(controls.get().variant) // "Stand up", until another chip is picked
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
- **Each option is a chip in the strip.** The default option is the `default` preset, named after
|
|
348
|
+
it; the others follow in order, then any other `presets`. Their ids are the options, and
|
|
349
|
+
`labels` name the chips.
|
|
350
|
+
- **The choice has no row.** The strip is the one place to switch, with a click, a sideways pan,
|
|
351
|
+
`[` / `]`, or `1`–`9` from anywhere in the panel but a number row.
|
|
352
|
+
- **Random leaves it be** (`random: false` unless you say otherwise). Random explores around the
|
|
353
|
+
active variant, and Reset goes back to it, as with any preset.
|
|
354
|
+
- **`presets` keyed by an option add to that variant.** The default variant's values are the
|
|
355
|
+
schema's defaults, so it takes none there.
|
|
356
|
+
|
|
357
|
+
Versions that differ only in their values need no choice at all: they're plain `presets`, with
|
|
358
|
+
`defaultLabel` naming the first.
|
|
359
|
+
|
|
320
360
|
## Presets: names and the base
|
|
321
361
|
|
|
322
362
|
The strip holds the schema's `presets` (and `default`) beside the variants you save. Saving (`+` or `⇧S`) puts a field where the new chip goes, its name ("Variant 3") selected so typing replaces it; `Enter` keeps what you typed, `Esc` keeps "Variant 3", and the field opens out to the chip's width without moving anything else. `F2` or `Enter` on the strip, or a double-click, renames a saved one the same way (`controls.variants.rename(id, name)`); names are kept with the variants in the store's storage. The schema's presets keep their names.
|
|
@@ -344,7 +384,7 @@ Panels and switchers are keyboard-first:
|
|
|
344
384
|
| Arrows on a pad or curve handle | Move the puck or handle (`⇧` ×10, `⌥` finest); `j` / `k` or `Tab` move on |
|
|
345
385
|
| On a gradient strip or curve graph | Arrows move the selected stop or point; `Tab` / `⇧Tab` and `[` / `]` pick another; `n` adds one, `Delete` removes it; `Enter` edits a stop's colour (`Esc` returns) |
|
|
346
386
|
| `[` / `]` | Cycle presets and saved variants from anywhere in the panel; with no preset strip, cycle tabs |
|
|
347
|
-
| `1`–`9` | Pick the focused row's Nth option: a choice (segmented or menu) or a curve's, easing's or gradient's presets.
|
|
387
|
+
| `1`–`9` | Pick the focused row's Nth option: a choice (segmented or menu) or a curve's, easing's or gradient's presets. Anywhere else in the panel, pick the preset strip's Nth chip (a design's variants). While you use keys, tiny digit hints show on the options once focus settles, and on the strip's chips while focus is on the strip or the panel. A number row keeps its digits for inline edits, and a page's card for its group presets |
|
|
348
388
|
| `⌥1`–`⌥9` | Show tab 1–9 |
|
|
349
389
|
| `/` | Focus the search field (`Esc` clears it, then hands focus back; `↓` or `Enter` go to the first row left) |
|
|
350
390
|
| `Home` / `End` | Jump slider to `min` or `max` |
|
|
@@ -395,8 +435,10 @@ Three things show which, in both renderers, on floating and inline panels and on
|
|
|
395
435
|
Cohesion`, the deepest crumb bright and its parents dim. Crumbs glide to their places as focus
|
|
396
436
|
moves, and one that changes fades out as the new one fades in. For a moment after a key below
|
|
397
437
|
the panel's level, a faint `esc ↑ Shape` follows it, then fades. Clicking a crumb takes focus to
|
|
398
|
-
its level; clicking the title takes it to the panel.
|
|
399
|
-
|
|
438
|
+
its level; clicking the title takes it to the panel. The line lives in the room between the
|
|
439
|
+
title and the header's buttons: when it doesn't fit, the first crumbs drop off, and without room
|
|
440
|
+
for the deepest it stays empty, so it never runs under the buttons and the title never moves.
|
|
441
|
+
`crumbs: false` on the mount drops the line (the lens and the armed frame stay).
|
|
400
442
|
|
|
401
443
|
Tune them with tokens on the panel or any ancestor:
|
|
402
444
|
|
|
@@ -709,6 +751,18 @@ mountPanel(station, column, {
|
|
|
709
751
|
the close X): a list picks which show, not their order. A button left out takes no slot; Undo,
|
|
710
752
|
Redo and Reset keep theirs while they're away (`data-shown="false"`, `disabled`, out of the
|
|
711
753
|
tab order and the accessibility tree).
|
|
754
|
+
- **On a surface.** An inline panel in a split's pane (or inside anything with the class
|
|
755
|
+
`knobs-surface`) is one surface with it: no card, padding or background of its own, its groups
|
|
756
|
+
plain sections on hairlines, its rows and preset chips keeping their inset so their text lines
|
|
757
|
+
up with the pane's. Without a title, its header buttons join the pane's header (left of the
|
|
758
|
+
collapse button) and it draws no header band; elsewhere an untitled header shows only while one
|
|
759
|
+
of its buttons does.
|
|
760
|
+
- **Presets as a list.** `presetDisplay: "auto"` (the default) shows the preset strip's chips
|
|
761
|
+
while they fit on two lines and a list once they'd take more (many presets, or long names):
|
|
762
|
+
left-aligned rows, like a menu's, with the selection gliding between them. `"list"` and
|
|
763
|
+
`"chips"` pick one.
|
|
764
|
+
- **Empty.** `empty: "This story has no controls"` shows that line, quietly, while the panel has
|
|
765
|
+
no rows. Without it an empty panel shows nothing.
|
|
712
766
|
- **Height-bounded panels.** The header (title and buttons) stays pinned while the rows scroll
|
|
713
767
|
under it; the preset strip, tabs and search scroll with the rows. Once rows are under it, and
|
|
714
768
|
only then, it frosts and softens its foot with a short fade, scroll-driven with no script, in
|
|
@@ -765,7 +819,36 @@ panel.tab("emitter") // shows a tab; panel.tab() answers the one showing
|
|
|
765
819
|
none show on every tab. The strip's underline slides to the chosen tab and the rows cross: what
|
|
766
820
|
leaves folds away as what arrives unfolds, a moment behind. `⌥1`–`⌥9` pick a tab, and `[` / `]`
|
|
767
821
|
cycle tabs when there's no preset strip. The tab last shown is remembered for these controls (and
|
|
768
|
-
in the session).
|
|
822
|
+
in the session). The preset strip sits under the tabs and crosses a tab change with the rows; a
|
|
823
|
+
tab that no preset or saved variant moves shows no strip at all (not even its `+`).
|
|
824
|
+
- **Pinned groups.** Groups that apply to every tab (a global speed, a slow-motion switch) sit at
|
|
825
|
+
the top of the panel, under its one header and above the tab strip, and a tab change never moves
|
|
826
|
+
them. Name them on the store, `pinned: ["Global"]`, or say `pinned: true` on a field (the first
|
|
827
|
+
field of a group that says wins; a field without a group pins itself). A pinned field's `tab` is
|
|
828
|
+
ignored. Pinned groups keep their own die and reset. Presets and saved variants leave them out:
|
|
829
|
+
applying one keeps their values, saving one doesn't record them, a preset's chip doesn't drift
|
|
830
|
+
when they change, and their modified marks are against their defaults. Random rolls them freely
|
|
831
|
+
even under an active preset. The header's Random and Reset still cover everything, pinned
|
|
832
|
+
groups included, as they cover every tab; give a field `random: false` to keep it out of
|
|
833
|
+
Random. Keyboard order and search follow the panel top to bottom: pinned rows, the tabs, the
|
|
834
|
+
preset strip, then the tab's rows. `controls.isPinned(key)` says whether a key is pinned.
|
|
835
|
+
A pinned `variants: true` choice is an ordinary choice with a row: pinned wins, and its options
|
|
836
|
+
make no presets. A tab whose rows no preset moves shows no preset strip; a variants choice moves
|
|
837
|
+
every tab (or its own, if it names one).
|
|
838
|
+
|
|
839
|
+
```ts
|
|
840
|
+
const motion = defineControls(
|
|
841
|
+
"motion",
|
|
842
|
+
{
|
|
843
|
+
speed: knob(1, { min: 0.25, max: 4, step: 0.05, unit: "×", group: "Global" }),
|
|
844
|
+
slow: toggle(false, { label: "Slow motion (0.25×)", group: "Global" }),
|
|
845
|
+
sidebars: spring({ duration: 0.3, bounce: 0 }, { group: "Sidebars", tab: "Sidebars" }),
|
|
846
|
+
hover: spring({ duration: 0.14, bounce: 0 }, { group: "Experiments", tab: "Experiments" }),
|
|
847
|
+
},
|
|
848
|
+
{ pinned: ["Global"], defaultLabel: "Shipped", presets: { snappy, smooth, calm } },
|
|
849
|
+
)
|
|
850
|
+
motion.mount(el, { title: "Knobs", tabs: ["Sidebars", "Experiments"] })
|
|
851
|
+
```
|
|
769
852
|
- **Sub-headings.** Fields with the same `subsection` in a group sit under a flat heading inside the
|
|
770
853
|
group's card, split off by a hairline (never a card inside a card). They're gathered: a group's
|
|
771
854
|
rows without a sub-heading come first, then each sub-heading's rows together, in the order the
|
|
@@ -1335,6 +1418,23 @@ Click or `Enter` zooms a tile to fill the view, aspect-fit and centred: one cont
|
|
|
1335
1418
|
|
|
1336
1419
|
The handle has `update(items)`, `zoom(id | null)`, `zoomed`, `active`, `focus(id?)`, `picks`, `pick(ids)`, `plan`, `moving`, `stats()` (the frame loop's script time) and `dispose()`; `onZoom` and `onActive` report changes. It takes the pieces' tokens and `restyle`, in dark and light; tiles round at `--k-ui-bento-radius` (3px), square under `--k-frame: flat`; `--k-ui-bento-bg` and `--k-ui-scrim` set the page under the tiles and the zoom. `planBento(aspects, { width, height, … })` is the layout on its own, with `bentoNeighbour`, `bentoWindow` and `fitRect`. `Bento` in `knobs/ui/react` and `knobs/ui/solid` follows `items` and `picks`. The demo is `examples/ideas.html` (`?n=500` for a big set, `?groups`, `?hero=3`, `?perf` for a frame-time readout, `?bench` to scroll and zoom on its own and report).
|
|
1337
1420
|
|
|
1421
|
+
### Infinite canvas
|
|
1422
|
+
|
|
1423
|
+
```ts
|
|
1424
|
+
import { canvas } from "knobs/ui"
|
|
1425
|
+
|
|
1426
|
+
const board = canvas(container, {
|
|
1427
|
+
items: stories.map((s) => ({ id: s.id, title: s.name, render: (el) => s.mount(el) })),
|
|
1428
|
+
})
|
|
1429
|
+
board.focus("button-primary")
|
|
1430
|
+
```
|
|
1431
|
+
|
|
1432
|
+
**`canvas(container, options)`** puts items on a dotted page that pans and zooms freely, as a components lab's grid. Items take `id`, `render(el)` (return a cleanup), `title` (shown above the card), `w`/`h` (the card's size; without them it takes its content's) and `x`/`y` (a place; items without one pack into rows under the placed ones, in order, the rows as wide as makes the set about the canvas's shape, or `width`). Each item is one hairline card; the dots scale with the zoom and fade out at the extremes; the edges fade over 16px (`--k-ui-canvas-fade`).
|
|
1433
|
+
|
|
1434
|
+
Dragging the page, two-finger scrolling, or holding Space or the middle button and dragging anywhere pans; a pinch or `⌘`/`Ctrl`-scroll zooms around the pointer. Both follow the hand 1:1 and a fling coasts. What the hand didn't do springs (no bounce): `0` fits, `1` is 100%, `=`/`-` step 1.5×, and a double-click on an item focuses it (again, or on the page, fits). A mouse wheel's notch under `⌘` zooms directly too, at most 1.25× an event. Each jump zooms around the one point both views share, so everything on screen travels in a straight line. Items stay live: clicks and keys reach them, and a dragged Space still presses a focused button if nothing moved. Nothing moves on load: the first fit is instant, and until someone touches the view a change in size or content fits again. The world is one transformed layer; at rest its translate snaps to device pixels and, after a zoom, it's drawn again at the new scale so text is crisp.
|
|
1435
|
+
|
|
1436
|
+
The handle has `fit()`, `focus(id)` (at most 200%), `zoomTo(scale, at?)`, `update(items)` (items that stay keep their content and glide to new places; new ones come in, gone ones soften out), `view`, `rects`, `moving`, `stats()` and `dispose()`; `onView` reports the view, `minZoom`/`maxZoom` (.05–8) bound it, `gap` (48) parts packed items, `padding` (48) frames a fit and `readout: false` drops the zoom pill. `layoutCanvas`, `packRows`, `fitView`, `zoomAround`, `viewPath` and `dotGrid` are the math on its own. `Canvas` in `knobs/ui/react` and `knobs/ui/solid` follows `items`. The demo is `examples/canvas.html` (`?n=200`, `?perf`).
|
|
1437
|
+
|
|
1338
1438
|
### Review pieces (`knobs/review`)
|
|
1339
1439
|
|
|
1340
1440
|
The walkthrough and what it shows (compare, player, code diff, history, facts, decision, taste test, `mediaSlot`) are their own subsystem: `knobs/review` (and `knobs/review/react`, `knobs/review/solid`). They have their own quiet look, `themes.review` (true black, flat, hairlines held to an APCA target, the platform's own face), one type scale and a short set of rules: [docs/review-design.md](docs/review-design.md), with every piece alone in `examples/review.html`. `knobs/ui` still exports them. `reviewFont(document, { href, sans: "Inter" })` gives the review look in a webfont.
|
|
@@ -1882,7 +1982,7 @@ The package has two core exports selected through the standard `development` con
|
|
|
1882
1982
|
| Function | Behavior |
|
|
1883
1983
|
| --- | --- |
|
|
1884
1984
|
| `knob(value, { min, max, step?, unit?, decimals?, detents?, shortcut?, label?, group?, groupLabel?, section?, sectionLabel? })` | Numeric slider control with rolling readout and inline editor. `detents: false` lets drags glide through the default, the base and the middle. |
|
|
1885
|
-
| `choice(value, ["a", "b"], { label?, group?, shortcut? }?)` | Nonempty string choices with literal union inference. |
|
|
1985
|
+
| `choice(value, ["a", "b"], { label?, group?, shortcut?, labels?, display?, variants? }?)` | Nonempty string choices with literal union inference. `variants: true` makes the options the preset strip's chips, with no row (see [Versions of a design](#versions-of-a-design-variants-in-the-strip)). |
|
|
1886
1986
|
| `toggle(value, { label?, group?, shortcut?, random? }?)` | Boolean switch control. Out of Random and Mutate unless `random: true`. |
|
|
1887
1987
|
| `spring({ duration?, bounce? }?, meta?)` | SwiftUI-style spring: two sliders beside its live curve. |
|
|
1888
1988
|
| `color("#hex" \| "oklch(L C H)" \| { l, c, h }, meta?)` | OKLCH colour kept inside sRGB, as an `oklch()` string; swatch, hex and hue/chroma/lightness strips. |
|
|
@@ -1896,7 +1996,8 @@ The package has two core exports selected through the standard `development` con
|
|
|
1896
1996
|
| `range([low, high], { min, max, step?, unit? })` | One slider with two thumbs. |
|
|
1897
1997
|
| `angle(degrees, { min?, max?, step?, wrap? })` | A dial, 0° up and clockwise; 0–360 wrapping by default. |
|
|
1898
1998
|
| `seed(value, { min?, max? })` / `keybind(combo?)` | A whole number with a die; a captured key combination. Both stay out of Random and Mutate by default. |
|
|
1899
|
-
| `
|
|
1999
|
+
| `text(value?, { multiline?, placeholder? })` | Words in a field, or lines in a box that grows with them. Out of Random and Mutate. |
|
|
2000
|
+
| `toolbar([{ id?, label, icon?, shortcut?, global?, disabled?, run? }], { labels? })` | A row of buttons with shortcut chips; presses call `onAction(id)`. `shortcut` is a key, or `{ key, scope: "page" \| "panel" \| "none" }`; `toolbarShortcut(item)` reads either. |
|
|
1900
2001
|
| `watch` / `sparkline` / `graph` / `meter` / `table` / `log` (`{ label?, unit?, decimals?, format?, hz?, get?, … }`) | Readout rows that hold no value; push with `controls.watch(key, value)`. See [Readouts](#readouts-live-values-sparklines-graphs-meters-tables-and-logs). |
|
|
1901
2002
|
| `springCss(spring)` / `springMotion(spring)` / `bezierCss(curve)` / `colorHex(color)` | Turn values into CSS, ω/ζ, or hex. |
|
|
1902
2003
|
| `fromDescriptors(descriptors)` | Converts runtime `ControlDescriptor[]` into a `Schema`. |
|
|
@@ -1969,6 +2070,7 @@ The package has two core exports selected through the standard `development` con
|
|
|
1969
2070
|
| `optionPicker(container, { options, selected?, chosen?, onSelect?, onChoose?, body?, digits? })` | Options 1–n on a gliding card, the selected one's parameters (a panel) open under it. |
|
|
1970
2071
|
| `bento(container, { items, gap?, tolerance?, rowHeight?, fit?, blur?, picks?, onPick?, onZoom?, onActive? })` | An ideas grid: a bento of images and live content that zooms an item to fill the view; picks with Space. |
|
|
1971
2072
|
| `planBento(aspects, { width, height, gap?, tolerance?, rowHeight?, groups?, hero? })` | The bento's layout on its own: rects, mode, coverage and crop. |
|
|
2073
|
+
| `canvas(container, { items, gap?, padding?, width?, minZoom?, maxZoom?, readout?, onView? })` | An infinite canvas: items as cards on a dotted page that pans and zooms freely; `fit`, `focus(id)`, `zoomTo`. |
|
|
1972
2074
|
| `walkthrough(container, { doc, onFeedback?, storage?, keys?, rail?, advance? })` / `mediaSlot(container, { media, steps? })` | The review page an agent writes (sections, steps, media from compares to histories, stacks and tables, notes and picks handed back); one step's media on its own. |
|
|
1973
2075
|
| `overlayLook({ preset?, theme? })` | The look every overlay takes. |
|
|
1974
2076
|
| `hints.register(root, () => targets, { listen?, rank? })` / `data-hint` | Joins the page's hint mode (`s`); `hints.start()`, `hints.stop()`. |
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
var __chunkGraphs, __chunkSeen, __chunkNonce, __preload = (id, seenOnly) => {
|
|
2
|
-
for (var [base, graph, ids] of __chunkGraphs || [])
|
|
3
|
-
for (var stack = [id], g = globalThis, d = g.document, head, j, node, k, link;j = stack.pop(); )
|
|
4
|
-
if (!__chunkSeen[j] && (node = graph[j])) {
|
|
5
|
-
__chunkSeen[j] = 1;
|
|
6
|
-
for (k = 1;k < node.length; k++)
|
|
7
|
-
stack.push(ids[node[k]]);
|
|
8
|
-
if (!seenOnly && j !== id && (head = d && d.head)) {
|
|
9
|
-
if (__chunkNonce === void 0)
|
|
10
|
-
__chunkNonce = (k = d.querySelector("meta[property=csp-nonce]")) && (k.nonce || k.getAttribute("nonce")) || "";
|
|
11
|
-
if (link = d.createElement("link"), link.rel = "modulepreload", link.crossOrigin = "", __chunkNonce)
|
|
12
|
-
link.nonce = __chunkNonce;
|
|
13
|
-
link.href = new g.URL(node[0], base), head.appendChild(link);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}, __chunks = (base, ids, nodes, entry) => {
|
|
17
|
-
for (var graph = {}, i = 0;i < ids.length; i++)
|
|
18
|
-
graph[ids[i]] = nodes[i];
|
|
19
|
-
(__chunkGraphs ||= []).push([base, graph, ids]), __chunkSeen ||= {}, __preload(ids[entry], 1);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
1
|
// src/core/color.ts
|
|
23
2
|
var round = (n, places) => Number(n.toFixed(places)), clamp01 = (n) => Math.min(1, Math.max(0, n)), gamma = (c) => c <= 0.0031308 ? 12.92 * c : 1.055 * c ** 0.4166666666666667 - 0.055, linear = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
24
3
|
function oklchToLinear({ l, c, h }) {
|
|
@@ -516,6 +495,7 @@ var META_KEYS = [
|
|
|
516
495
|
"folded",
|
|
517
496
|
"sectionFolded",
|
|
518
497
|
"tab",
|
|
498
|
+
"pinned",
|
|
519
499
|
"subsection"
|
|
520
500
|
];
|
|
521
501
|
function pickMeta(source, skip = []) {
|
|
@@ -527,7 +507,29 @@ function pickMeta(source, skip = []) {
|
|
|
527
507
|
}
|
|
528
508
|
return out;
|
|
529
509
|
}
|
|
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)
|
|
510
|
+
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);
|
|
511
|
+
function variantsOf(schema, isPinned = () => !1) {
|
|
512
|
+
let found;
|
|
513
|
+
for (let [key, field] of Object.entries(schema))
|
|
514
|
+
if (field.kind === "choice" && field.variants && !isPinned(key)) {
|
|
515
|
+
if (found)
|
|
516
|
+
throw Error(`Only one choice can hold the variants (${found.key}, ${key}).`);
|
|
517
|
+
found = { key, field };
|
|
518
|
+
}
|
|
519
|
+
return found;
|
|
520
|
+
}
|
|
521
|
+
function pinnedKeys(schema, groups = []) {
|
|
522
|
+
let byGroup = new Map(groups.map((g) => [g, !0]));
|
|
523
|
+
for (let f of Object.values(schema))
|
|
524
|
+
if (f.group !== void 0 && f.pinned !== void 0 && !byGroup.has(f.group))
|
|
525
|
+
byGroup.set(f.group, f.pinned);
|
|
526
|
+
let out = /* @__PURE__ */ new Set;
|
|
527
|
+
for (let [key, f] of Object.entries(schema))
|
|
528
|
+
if (f.group === void 0 ? f.pinned === !0 : byGroup.get(f.group) === !0)
|
|
529
|
+
out.add(key);
|
|
530
|
+
return out;
|
|
531
|
+
}
|
|
532
|
+
var SPRING2 = {
|
|
531
533
|
duration: { min: 0.05, max: 2, step: 0.01 },
|
|
532
534
|
bounce: { min: -0.5, max: 1, step: 0.01 }
|
|
533
535
|
};
|
|
@@ -558,8 +560,9 @@ function choice2(value, options, meta) {
|
|
|
558
560
|
value,
|
|
559
561
|
options: Object.freeze([...options]),
|
|
560
562
|
...labels && Object.keys(labels).length ? { labels } : {},
|
|
561
|
-
...meta?.display ? { display: meta.display } : {}
|
|
562
|
-
|
|
563
|
+
...meta?.display ? { display: meta.display } : {},
|
|
564
|
+
...meta?.variants ? { variants: !0 } : {}
|
|
565
|
+
}, meta?.variants ? { ...meta, random: meta.random ?? !1 } : meta);
|
|
563
566
|
}
|
|
564
567
|
function action2(label, meta) {
|
|
565
568
|
let { shortcut, global, rebind, run, ...rest } = meta ?? {}, key = shortcutOf({ ...shortcut ? { shortcut } : {}, ...global ? { global } : {} }), combo = key ? normalizeCombo2(key.key) : "";
|
|
@@ -704,6 +707,17 @@ function keybind2(value = "", meta) {
|
|
|
704
707
|
let field = { kind: "keybind", value: normalizeCombo2(value) };
|
|
705
708
|
return withMeta(field, { random: !1, ...meta });
|
|
706
709
|
}
|
|
710
|
+
function text2(value = "", options) {
|
|
711
|
+
let { multiline, placeholder, ...meta } = options ?? {}, field = {
|
|
712
|
+
kind: "text",
|
|
713
|
+
value: "",
|
|
714
|
+
...multiline ? { multiline: !0 } : {},
|
|
715
|
+
...placeholder ? { placeholder } : {}
|
|
716
|
+
};
|
|
717
|
+
return withMeta({ ...field, value: normalizeText(field, value) }, { random: !1, ...meta });
|
|
718
|
+
}
|
|
719
|
+
var normalizeText = (field, input) => field.multiline ? input.replace(/\r\n?/g, `
|
|
720
|
+
`) : input.replace(/\r\n?|\n/g, " ");
|
|
707
721
|
function gradient2(stops, options = {}) {
|
|
708
722
|
let value = normalizeStops(stops);
|
|
709
723
|
if (!value)
|
|
@@ -758,7 +772,8 @@ function toolbar2(items, meta) {
|
|
|
758
772
|
throw Error("A toolbar needs at least one item.");
|
|
759
773
|
return withMeta({
|
|
760
774
|
kind: "toolbar",
|
|
761
|
-
items: Object.freeze(items.map((item) => Object.freeze({ ...item })))
|
|
775
|
+
items: Object.freeze(items.map((item) => Object.freeze({ ...item }))),
|
|
776
|
+
...meta?.labels !== void 0 ? { labels: meta.labels } : {}
|
|
762
777
|
}, meta);
|
|
763
778
|
}
|
|
764
779
|
var readoutMeta = (o) => {
|
|
@@ -902,6 +917,8 @@ function normalizeField(field, input) {
|
|
|
902
917
|
return typeof input === "number" && Number.isFinite(input) ? Math.round(Math.min(field.max, Math.max(field.min, input))) : field.value;
|
|
903
918
|
case "keybind":
|
|
904
919
|
return typeof input === "string" ? normalizeCombo2(input) : field.value;
|
|
920
|
+
case "text":
|
|
921
|
+
return typeof input === "string" ? normalizeText(field, input) : field.value;
|
|
905
922
|
case "gradient":
|
|
906
923
|
return normalizeStops(input) ?? field.value;
|
|
907
924
|
case "curve":
|
|
@@ -1160,4 +1177,4 @@ function urlFlag2(name) {
|
|
|
1160
1177
|
}
|
|
1161
1178
|
var storageKey = (id, part) => `knobs:${encodeURIComponent(id)}:${part}:v1`;
|
|
1162
1179
|
|
|
1163
|
-
export {
|
|
1180
|
+
export { 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, variantsOf, pinnedKeys, SPRING2, valueKeys, knob2, choice2, action2, flatSpring2, toggle2, spring2, color2, point2, easing2, vec22, vec32, range2, angle2, seed2, keybind2, text2, 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 };
|