@kitlangton/knobs 0.3.0 → 0.4.0-next.1
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 +350 -45
- package/dist/dev/{chunk-jx1ktap3.js → chunk-1qzffbpa.js} +497 -422
- package/dist/dev/{chunk-7xgkerd2.js → chunk-3ygqgy1x.js} +23 -1
- package/dist/dev/{chunk-pmkvb9ep.js → chunk-8mabjeyk.js} +1920 -507
- package/dist/dev/{chunk-v8ssbcbk.js → chunk-ayb292zj.js} +13 -11
- package/dist/dev/index.js +10 -5
- package/dist/index.js +25 -1
- package/dist/panel.js +2954 -1291
- package/dist/react.js +294 -6
- package/dist/solid.js +292 -4
- package/dist/themes.js +1509 -7
- package/dist/types/core/adapter.d.ts +6 -2
- package/dist/types/core/appearance.d.ts +44 -0
- package/dist/types/core/glide.d.ts +7 -0
- package/dist/types/core/motion.d.ts +66 -0
- package/dist/types/core/root.d.ts +13 -0
- package/dist/types/core/store.d.ts +18 -5
- package/dist/types/core/theme.d.ts +10 -3
- package/dist/types/index.d.ts +2 -1
- package/dist/types/panel/gpu/color.d.ts +6 -0
- package/dist/types/panel/motion.d.ts +1 -1
- package/dist/types/panel/styles.d.ts +2 -3
- package/dist/types/themes/define.d.ts +104 -0
- package/dist/types/themes/index.d.ts +11 -0
- package/dist/types/themes/knobs.d.ts +5 -0
- package/dist/types/themes/void.d.ts +8 -0
- package/dist/types/tokens/color.d.ts +42 -0
- package/dist/types/tokens/css.d.ts +13 -0
- package/dist/types/tokens/evaluate.d.ts +8 -0
- package/dist/types/tokens/index.d.ts +4 -0
- package/dist/types/tokens/schema.d.ts +1100 -0
- package/dist/types/ui/bento-css.d.ts +1 -1
- package/dist/types/ui/compare-css.d.ts +1 -0
- package/dist/types/ui/compare-frames.d.ts +41 -0
- package/dist/types/ui/compare-gpu.d.ts +58 -0
- package/dist/types/ui/compare-model.d.ts +169 -0
- package/dist/types/ui/compare-mp4.d.ts +29 -0
- package/dist/types/ui/compare-strip.d.ts +28 -0
- package/dist/types/ui/compare.d.ts +103 -0
- package/dist/types/ui/diff-css.d.ts +1 -0
- package/dist/types/ui/diff-model.d.ts +128 -0
- package/dist/types/ui/diff-scroll.d.ts +29 -0
- package/dist/types/ui/diff.d.ts +158 -0
- package/dist/types/ui/index.d.ts +10 -2
- package/dist/types/ui/look.d.ts +18 -13
- package/dist/types/ui/overlay-css.d.ts +1 -1
- package/dist/types/ui/react.d.ts +66 -0
- package/dist/types/ui/readout-css.d.ts +1 -1
- package/dist/types/ui/readout-model.d.ts +5 -1
- package/dist/types/ui/rgba.d.ts +11 -0
- package/dist/types/ui/scrollbar.d.ts +2 -2
- package/dist/types/ui/solid.d.ts +52 -0
- package/dist/types/ui/timeline.d.ts +35 -2
- package/dist/types/ui/tree.d.ts +7 -2
- package/dist/types/ui/walkthrough-css.d.ts +1 -0
- package/dist/types/ui/walkthrough-media.d.ts +29 -0
- package/dist/types/ui/walkthrough-model.d.ts +447 -0
- package/dist/types/ui/walkthrough.d.ts +63 -0
- package/dist/ui-react.js +18352 -8462
- package/dist/ui-solid.js +17947 -8148
- package/dist/ui.js +19240 -9701
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ const detach = splat.attach(document.querySelector<HTMLElement>("#splat")!)
|
|
|
49
49
|
|
|
50
50
|
// Or mount an inline panel inside a container:
|
|
51
51
|
const unmount = splat.mount(document.querySelector<HTMLElement>("#sidebar")!, {
|
|
52
|
-
|
|
52
|
+
appearance: "system",
|
|
53
53
|
density: "compact",
|
|
54
54
|
})
|
|
55
55
|
|
|
@@ -856,7 +856,7 @@ A panel inside an app should look like the app's own controls. Four things get i
|
|
|
856
856
|
| `--k-shadow-strength` | `.48` (light: `.16`) | How dark the WebGPU renderer draws the panel's drop shadow. Set it beside `--k-shadow` when you change that, so both renderers match (a host blending a dark look into a light one moves both together) |
|
|
857
857
|
| `--k-frame` | `card` | `flat`: no card inside the host's own card (see below) |
|
|
858
858
|
| `--k-disabled-opacity` | `.32` | How far a disabled row dims |
|
|
859
|
-
| `--k-warn`, `--k-critical` | `#ff9f0a`, `#ff453a` (light: `#c56a00`, `#e0352b`) | Readouts' warning and critical colours (meters past `warn` / `critical`, log levels), and a destructive confirm |
|
|
859
|
+
| `--k-warn`, `--k-critical` | unset; knobs/ui falls back to `#ff9f0a`, `#ff453a` (light: `#c56a00`, `#e0352b`) | Readouts' warning and critical colours (meters past `warn` / `critical`, log levels), and a destructive confirm. Only knobs/ui pieces read them |
|
|
860
860
|
| `--k-control-h` | row − 2 × unit | Height of the controls inside a row: segments, menus, switches, number cells, key caps, toolbar buttons |
|
|
861
861
|
| `--k-value-size` | font size − `.5px` | Size of every readout (sliders, cells, ranges, dials, seeds, editor summaries) |
|
|
862
862
|
| `--k-button-bg`, `--k-button-line`, `--k-button-hover-bg`, `--k-button-hover-line` | each button's own | Fill and outline of custom actions, Export/Import, toolbar buttons, action rows and preset chips, at rest and hovered. With a fill or outline set, an action row is a sized button in its row and the presets strip a row of buttons (`data-buttons` on the panel) |
|
|
@@ -1049,14 +1049,14 @@ Both renderers draw the page. With `?gpu`, the overlay draws the header's conten
|
|
|
1049
1049
|
|
|
1050
1050
|
## Workspaces (`knobs/ui`)
|
|
1051
1051
|
|
|
1052
|
-
`knobs/ui` has the pieces a tool's workspace is built from: resizable split panes and docks, and an outliner tree. They are plain DOM and framework-neutral, use the same tokens as panels (`--k-*`,
|
|
1052
|
+
`knobs/ui` has the pieces a tool's workspace is built from: resizable split panes and docks, and an outliner tree. They are plain DOM and framework-neutral, use the same tokens as panels (`--k-*`, and a `theme` and `appearance`, as panels take), and work on their own or next to panels. A panel can mount inside a pane.
|
|
1053
1053
|
|
|
1054
1054
|
```ts
|
|
1055
|
-
import {
|
|
1055
|
+
import { themes } from "knobs/themes"
|
|
1056
1056
|
import { applyMove, split, tree } from "knobs/ui"
|
|
1057
1057
|
import { mountPanel } from "knobs/panel"
|
|
1058
1058
|
|
|
1059
|
-
const look = {
|
|
1059
|
+
const look = { theme: themes.void, appearance: "dark" as const }
|
|
1060
1060
|
const main = split(workspace, {
|
|
1061
1061
|
...look,
|
|
1062
1062
|
id: "workspace", // saves sizes and collapsed panes
|
|
@@ -1094,7 +1094,8 @@ Scrolling pane bodies, trees and the command palette's list fade their content o
|
|
|
1094
1094
|
|
|
1095
1095
|
**`tree(container, options)`** is an outliner. Rows are virtualised, so 10,000 of them scroll at full speed with about 35 in the DOM. When a branch opens, its children slide out from under it as one block with the rows below, like a drawer, and closing folds them back. The tree takes items with `id`, `label` and `children` (or `getId`, `getLabel` and `getChildren` for your own shape; an array, even empty, makes a parent that takes drops). Options:
|
|
1096
1096
|
|
|
1097
|
-
- `icon` and `detail` for a glyph and dimmed text, or `render` for your own row content
|
|
1097
|
+
- `icon` and `detail` for a glyph and dimmed text (or nodes of your own), or `render` for your own row content
|
|
1098
|
+
- `glide: true` to show the selected row as one highlight that glides to the next row picked (on the rows' .3 s spring) instead of each row lighting in place; several selected rows light in place
|
|
1098
1099
|
- `toggles` for row accessories such as `visible` and `locked`, which show at rest only in their unusual state
|
|
1099
1100
|
- `filter` for a query or a test; matching rows show with their ancestors, the match marked
|
|
1100
1101
|
- `selection`, `expanded` and `rowHeight` (`--k-row` by default)
|
|
@@ -1132,12 +1133,12 @@ import { Split, Tree } from "knobs/ui/react" // or "knobs/ui/solid"
|
|
|
1132
1133
|
|
|
1133
1134
|
### Overlays: popovers, tooltips, the command palette, confirms and toasts
|
|
1134
1135
|
|
|
1135
|
-
`knobs/ui` also has the floating layers a workspace needs. They sit in one fixed layer above the page and the GPU overlay, come in on a spring (scale and blur from where they open, interruptible both ways; opacity only under reduced motion), and use the same tokens. `overlayLook({
|
|
1136
|
+
`knobs/ui` also has the floating layers a workspace needs. They sit in one fixed layer above the page and the GPU overlay, come in on a spring (scale and blur from where they open, interruptible both ways; opacity only under reduced motion), and use the same tokens. `overlayLook({ theme, appearance })` sets the look they all take; each also takes its own `look`.
|
|
1136
1137
|
|
|
1137
1138
|
```ts
|
|
1138
1139
|
import { commandPalette, commandsFrom, confirm, overlayLook, popover, toast, tooltip, tooltips } from "knobs/ui"
|
|
1139
1140
|
|
|
1140
|
-
overlayLook({
|
|
1141
|
+
overlayLook({ theme: themes.void, appearance: "dark" })
|
|
1141
1142
|
|
|
1142
1143
|
const menu = popover(button, list, { placement: "bottom-end", role: "menu", label: "Games" })
|
|
1143
1144
|
tooltip(pause, { text: "Pause the live feed", shortcut: "Alt+P" })
|
|
@@ -1180,7 +1181,34 @@ const clock = timeline(dock, {
|
|
|
1180
1181
|
})
|
|
1181
1182
|
```
|
|
1182
1183
|
|
|
1183
|
-
**`timeline(container, options)`** is a scrubber for time-based work. The ruler counts in seconds or frames (`units`) with ticks that thin out as it zooms (`⌘` wheel, a pinch, `+`/`-`, `0` fits) and pans with the wheel or a trackpad. The playhead follows the hand 1:1, pulls softly toward markers and lands on frames (`snap`). Markers can be added (`M`), dragged, renamed (`Enter`) and removed, and `[`/`]` jump between them; `I` and `O` set a loop range. `Space` plays on one rAF clock that sleeps when nothing moves, and `←`/`→` step a frame (`⇧` ten). A right-click on the ruler, a marker or a range opens a context menu. It reports `onSeek(time, source)` (`"scrub" | "key" | "marker" | "play" | "code"`), `onPlay`, `onMarkers`, `onRanges` and `onCross(marker)`. The handle has `time`, `playing`, `markers`, `ranges`, `view`, `seek`, `play`, `pause`, `toggle`, `update`, `fit`, `zoom`, `focus` and `dispose`. `Timeline` in `knobs/ui/react` and `knobs/ui/solid` follows its props. The Lab's scene clock and Boom retrigger run from one under the viewport.
|
|
1184
|
+
**`timeline(container, options)`** is a scrubber for time-based work. The ruler counts in seconds or frames (`units`) with ticks that thin out as it zooms (`⌘` wheel, a pinch, `+`/`-`, `0` fits) and pans with the wheel or a trackpad. The playhead follows the hand 1:1, pulls softly toward markers and lands on frames (`snap`). Markers can be added (`M`), dragged, renamed (`Enter`) and removed, and `[`/`]` jump between them; `I` and `O` set a loop range. `Space` plays on one rAF clock that sleeps when nothing moves, and `←`/`→` step a frame (`⇧` ten; `arrows: { step: 0.5, shift: 2 }` makes them seconds, the playhead gliding there) and `,`/`.` always do; `seek(t, { glide: true })` glides the playhead too, the time landing at once. A right-click on the ruler, a marker or a range opens a context menu. It reports `onSeek(time, source)` (`"scrub" | "key" | "marker" | "play" | "code"`), `onPlay`, `onMarkers`, `onRanges` and `onCross(marker)`. The handle has `time`, `playing`, `duration`, `fps`, `markers`, `ranges`, `view`, `seek`, `play`, `pause`, `toggle`, `update`, `fit`, `zoom`, `focus` and `dispose`. Pieces bound to a timeline hear it with `listen({ onSeek, onPlay, onView, onScrub, onRanges })` (it returns a stop), and `drive(clock)` makes playback follow another clock, such as a video's `currentTime`, so the two never drift. `Timeline` in `knobs/ui/react` and `knobs/ui/solid` follows its props. The Lab's scene clock and Boom retrigger run from one under the viewport.
|
|
1185
|
+
|
|
1186
|
+
### Compare
|
|
1187
|
+
|
|
1188
|
+
```ts
|
|
1189
|
+
import { compare, compareStrip, timeline } from "knobs/ui"
|
|
1190
|
+
|
|
1191
|
+
// All in one: the viewer and a timeline of its own.
|
|
1192
|
+
const review = compare(el, {
|
|
1193
|
+
before: { src: "/runs/41.mp4" },
|
|
1194
|
+
after: { src: "/runs/42.mp4" },
|
|
1195
|
+
mode: "side", // the default; "wipe" | "difference" | "flicker"
|
|
1196
|
+
timeline: true,
|
|
1197
|
+
onView: ({ zoom, loop }) => log(zoom, loop), // { u, v, w, h }, { start, end } | null
|
|
1198
|
+
})
|
|
1199
|
+
review.seekBy(0.5)
|
|
1200
|
+
review.setLoop(true) // around the playhead; drag its ends on the timeline
|
|
1201
|
+
|
|
1202
|
+
// A long page of them: each loads near the view and lets go well off it.
|
|
1203
|
+
for (const step of steps) compare(step.el, { before: step.before, after: step.after, timeline: true })
|
|
1204
|
+
|
|
1205
|
+
// Or composed: the app places the pieces and binds them to one timeline.
|
|
1206
|
+
const clock = timeline(dock, { duration: 6, fps: 30 })
|
|
1207
|
+
compare(viewer, { before, after, timeline: clock })
|
|
1208
|
+
compareStrip(strip, { before, after, timeline: clock })
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
**`compare(container, options)`** is a before/after viewer for videos and images (by extension, or `kind`). Side by side (the default `mode`: a split screen down the middle, each side filling its half), a wipe, a difference (changes above `threshold` in one accent, `--k-compare-diff`) or a flicker, drawn by one WebGPU pass that copies a frame in only when one arrives, and draws only when something changed; where there's no WebGPU, two `<video>`s with a clip-path wipe stand in. Pinch, `⌘`/ctrl-wheel or double-click zooms into either side and both zoom to the same part (the wheel and a drag pan it, both 1:1); a small glass pill in the corner shows how far, with Reset (`0`, a double-click, `Escape`), which glides back. Overlaid, the sides are letterboxed, never cropped. Both videos run from one clock: paused, scrubbed or stepped, each seeks to the frame's midpoint, coalesced while a seek is in flight; playing, After leads, Before's rate leans by up to 25% to stay on it (it seeks past 90 ms), and a bound timeline follows After's time, so only one clock ever moves it (`Space` resumes from the frame showing). Scrubbing answers at once even on raw screen recordings with a keyframe every 8–18 s: frames decoded in the background (WebCodecs where the file is a plain MP4, else a hidden `<video>`) sit in a small cache shared by every viewer and strip of the same source, and the nearest shows while the exact seek lands (mid-drag, far from any cached frame, `fastSeek` answers first; letting go seeks exactly). Encoding with a keyframe every 15 frames makes every seek exact and near instant: `COMPARE_FFMPEG` holds the command (`ffmpeg -i in.mov -vf fps=30 -fps_mode cfr -c:v libx264 -crf 18 -g 15 … out.mp4`). Sources load only near the view and are let go 3 s after leaving it (`lazy: false` keeps them), every viewer shares one GPU device, and in a container with no height of its own it takes the height its sides need, half its width in their shape, so the split has no margins (`aspect` before they load); with a height of its own (a card going full-window, say), the pair fits as a whole and refits as it resizes, keeping the zoom. On a card (`.knobs-surface`, a split's pane) or marked `data-nested`, its stage drops its fill, so one surface shows. Bind an existing `timeline()` (`timeline: clock`), or pass `timeline: true` for its own. Loop (`L`) sets a loop range around the playhead on the timeline, whose ends drag, and playback stays inside it. The wipe follows the hand 1:1 and is a slider (`←`/`→`, `⇧` 10%); a mode change glides. Keys when focused: `Space` plays, `←`/`→` seek half a second (`⇧` two), the playhead gliding there while the videos land at once, `,`/`.` step a frame, `1`–`4` or `S` `W` `D` pick a mode, `X` held flicks to Before, `Z` held shows the loupe (or `loupe: true`), `L` loops, `=`/`-`/`0` zoom. It reports `onMode`, `onWipe` and `onView({ zoom, loop })`; the handle has `mode`, `wipe`, `zoom`, `loop`, `time`, `playing`, `duration`, `renderer`, `timeline`, `strip`, `update`, `seek`, `seekBy`, `play`, `pause`, `toggle`, `step`, `setZoom`, `setLoop`, `focus` and `dispose`. A frame strip is off by default (`strip: true` puts one over the timeline); **`compareStrip(container, { before, after, timeline })`** is the same piece on its own: it draws Before over After along the timeline's view from the same frame cache, decoded only while it's on screen, with a bar for how much each column differs; a press scrubs. `Compare` and `CompareStrip` are in `knobs/ui/react` and `knobs/ui/solid`. The Lab's View switch shows one on its scene timeline.
|
|
1184
1212
|
|
|
1185
1213
|
### Activity
|
|
1186
1214
|
|
|
@@ -1242,6 +1270,35 @@ import { KnobsPanel } from "knobs/solid"
|
|
|
1242
1270
|
|
|
1243
1271
|
The parameters are inset by the picker (`--k-picker-pad`, `0 8px 8px`), whatever a panel's own pad. They are real knobs panels: `body(option, element)` mounts whatever an option needs into `element` the first time it opens and returns a cleanup, and it stays mounted while closed, so a panel keeps its state. The picker is plain DOM chrome in `knobs/ui` and the panels come from `knobs/panel`, so `knobs/ui` stays free of the panel, and the parameters draw with the DOM or the WebGPU renderer as any panel does (nothing around them makes a stacking context at rest). `OptionPicker` in `knobs/ui/solid` and `knobs/ui/react` renders `children(option)` there instead, in the component's context. The handle has `set(options)`, `select(id)`, `selected`, `choose(id)`, `chosen`, `focus()` and `destroy()`. A `select(id)` (or a `selected` or `chosen`) for an option that isn't there yet is kept, and the `set()` that brings it picks it; a later `select` replaces it.
|
|
1244
1272
|
|
|
1273
|
+
### Code diff
|
|
1274
|
+
|
|
1275
|
+
```ts
|
|
1276
|
+
import { codeDiff, diffReview } from "knobs/ui"
|
|
1277
|
+
|
|
1278
|
+
// A patch's files in a tree beside the diff, in a split.
|
|
1279
|
+
const review = diffReview(container, { ...look, id: "review", patch: gitShowOutput })
|
|
1280
|
+
|
|
1281
|
+
// Or the diff alone, as a card: one file or many, from a patch or before and after text.
|
|
1282
|
+
const diff = codeDiff(el, {
|
|
1283
|
+
...look,
|
|
1284
|
+
files: [{ name: "src/spring.ts", before, after }],
|
|
1285
|
+
layout: "unified", // "split" by default
|
|
1286
|
+
height: "auto", // as tall as the file's diff, up to maxHeight; "fill" by default
|
|
1287
|
+
onFile: (name) => setFile(name),
|
|
1288
|
+
})
|
|
1289
|
+
|
|
1290
|
+
// Many down a long page (a walkthrough): each holds its height, draws when near the viewport.
|
|
1291
|
+
for (const step of steps) codeDiff(step.el, { ...look, patch: step.patch, height: "auto" })
|
|
1292
|
+
```
|
|
1293
|
+
|
|
1294
|
+
**`codeDiff(container, options)`** shows a code diff in a knobs card: a header with the file's path, its `+12 −3`, ‹ 2 of 5 › when there are several files (`nav: false` hides it), a wrap toggle and Split | Unified, over the diff. The diff itself is drawn by [`@pierre/diffs`](https://diffs.com) (Shiki highlighting, word-level changes, hunks that expand when whole files are known), an optional peer dependency: install it to use diffs (`npm i @pierre/diffs`). It loads on first use, so `knobs/ui` doesn't carry it. Its colours come from the pieces' tokens, so dark, light, presets and `restyle()` carry into it. `--k-diff-add`, `--k-diff-del` and `--k-diff-bg` tune it, and `syntax: { dark, light }` picks Shiki themes for the code. It takes `patch` (`git diff` or `git show` output, one file or many) or `files: [{ name, before, after, lang? }]`, plus `file`, `layout`, `wrap`, `onFile`, `onLayout`, `onWrap` and `onLines(lines, file)` (dragging over line numbers selects lines). Changing the file, the layout or the wrap fades the new view in over the old in place (.3 s in, .24 s out, a 3px blur while it fades; a .16 s fade under reduced motion). With `height: "fill"` the card fills its container and each file keeps its scroll. With `height: "auto"` it's a self-contained piece for a long page: it's as tall as the file's diff up to `maxHeight` (640, or 70% of the window when that's less) and scrolls inside past that, springing to each file's height (.4 s, no bounce). It holds the height the diff will take from the first frame, counted from the patch's rows before the renderer has loaded, so nothing below it jumps. It also draws only within a screen of the viewport (of the scroller it sits in) and lets go of its views four screens away, keeping its height; `lazy: false` draws it at once, and `lazy: true` does this with `height: "fill"`. Pass `viewed` (names) or `onViewed(viewed, file)` for a Viewed tick in the header. `stack: true` puts every file down one scroller, as GitHub's Files changed does: each under its own header (status, path, counts and their bar, Viewed) that sticks to the top until the next file's pushes it off: flush (square, no card around each file, a gap between files), opaque in the code's colour, with a hairline under it only while it's stuck. The card's header then sums the change, ‹ › and `show()` glide to a file (.4 s), the file on show follows the scroll, and each file draws only near the view.
|
|
1295
|
+
|
|
1296
|
+
Its scrollers fade their content out at an edge with more beyond it (a mask: at the top once scrolled, below a stuck header, and at the bottom while there's more below, `--k-diff-fade`, 20px), easing in and out on the .25 s spring. Its scrollbars are thin ones drawn over the code that take no room and stay hidden until the pointer is over the diff, or while you scroll or drag: `overlayScrollbar` up and down, and a bar per code column across, kept at the bottom of what shows of the column however far down a long file you are (the renderer's own, inside its shadow root, are hidden). One inset runs all round, `--k-diff-inset` (8px, the renderer's own gap): the header's controls sit it in from the top and right, the path, the separators and the line numbers it in from the left, and the code ends it above the bottom. Inner corners (separators, the header's controls) are the outer corner less that distance, so they're concentric with the card. On another surface the diff takes no inset across, so its header and code line up with the surface's own content (a walkthrough's caption), and its corners are measured from the surface's. The header's hairline shows only once the code has scrolled under it. The handle has `files`, `file`, `layout`, `wrap`, `viewed`, `show(name)`, `step(±1)`, `update({ patch?, files?, file?, layout?, wrap?, viewed?, height?, maxHeight? })`, `ready` (once drawn) and `dispose()`. `update({ height: "fill" | "auto" })` switches in place, keeping the views, their scroll and the sticky headers; the new height isn't sprung, so a container whose size is moving (a walkthrough card going full window) is followed, not fought.
|
|
1297
|
+
|
|
1298
|
+
**One surface.** Cards never nest. Whatever draws a card carries `.knobs-surface` (a walkthrough's step; put it on your own cards too), and a split's pane counts as one; a piece inside a surface draws no card of its own, so a diff in a step, a pane or your card shows no second fill or ring, and a `diffReview` inside one parts its panes on a hairline instead of drawing them as cards. The diff's code keeps an opaque fill under its sticky line numbers, the colour showing behind it, so it still matches.
|
|
1299
|
+
|
|
1300
|
+
**`diffReview(container, options)`** pairs it with a file list in one card. The list is a drawer at the card's left that springs open and shut between 0 and 256px (`listSize`; .4 s, no bounce), from a button at the start of the diff's header or `⌘B` / `Ctrl+B` while you're working in the review; the diff reflows as it moves, the list slides out from under it, and with an `id` it stays as you left it (`listOpen: false` starts it shut). The list is built on `tree()` and laid out as GitHub's, Graphite's and VS Code's are: a filter field on top (every word must be in the path: `ui diff`; `Esc` clears it, `↓` or `Enter` goes to the list), then a compact tree (28px rows, `--k-diff-row`) with folders folded into paths (`src/ui` is one row when `src` holds nothing else). Each file shows its change as a coloured letter (M amber, A green, D red and struck through, R blue; `--k-diff-mod` and `--k-diff-move` tune the two new ones), its `+12 −3` and a five-square bar of the share added and removed, and a tick to mark it viewed (on a folder, everything in it). Viewed files dim, the list's title counts them (`2/5`), and with an `id` they're remembered until their change moves on (each is saved with a fingerprint of its diff). The file on show has one highlight that glides to the next (the tree's new `glide` option, on the rows' .3 s spring). Picking a file shows it. The handle has the pieces, `tree` and `diff`, plus `listOpen`, `toggleList(open?)`, `viewed`, `setViewed(names, on)`, `filter(query)` and `update()`; `viewed` and `onViewed(viewed, file)` seed and report the marks, and `search: false` drops the field. To place the tree and the diff yourself, `diffFiles({ patch })` lists the files with their counts in tree order, `diffTree(files)` makes the tree's items, and `diffCounts(file, doc, { bar })` and `diffStatus(file)` give a row's counts and letter (a tree's `detail` and `icon`). `CodeDiff` and `DiffReview` in `knobs/ui/react` and `knobs/ui/solid` follow `patch`, `files`, `file`, `layout`, `wrap`, `viewed`, `height` and `maxHeight`. The demo is `examples/lab-diff.html`.
|
|
1301
|
+
|
|
1245
1302
|
### Ideas grid (bento)
|
|
1246
1303
|
|
|
1247
1304
|
```ts
|
|
@@ -1273,52 +1330,49 @@ The handle has `update(items)`, `zoom(id | null)`, `zoomed`, `active`, `focus(id
|
|
|
1273
1330
|
|
|
1274
1331
|
## Theming, Density & WebGPU Renderer
|
|
1275
1332
|
|
|
1276
|
-
Panels
|
|
1333
|
+
Panels take a `theme`, an `appearance`, `density: "compact" | "default" | "comfortable"` and `renderer: "gpu" | "dom" | "auto"` (or `?gpu` in the URL), on `defineControls`, `mountPanel` or `<KnobsPanel />`; every `knobs/ui` piece takes the same `theme` and `appearance`, and `restyle(target, look)` changes them on what's already on screen. The header title defaults to the controls id, written as words (`"synth-voice"` reads "Synth voice"); pass `title` to override it or `title: false` to hide it.
|
|
1334
|
+
|
|
1335
|
+
### Themes and appearance
|
|
1277
1336
|
|
|
1278
|
-
|
|
1337
|
+
Two themes ship in `knobs/themes`: **Knobs** (the default: a near-black glass panel, a white accent, alpha hairlines) and **Void** (true black and flat, solid hairlines, text capped at `#ededed`, a black stage behind compared media; for OLED screens and video review). Each has a dark and a light appearance.
|
|
1279
1338
|
|
|
1280
|
-
|
|
1339
|
+
```ts
|
|
1340
|
+
import { defineTheme, themes } from "knobs/themes"
|
|
1341
|
+
|
|
1342
|
+
mountPanel(controls, column, { theme: themes.void })
|
|
1343
|
+
split(workspace, { theme: themes.void, appearance: "dark", panes })
|
|
1344
|
+
|
|
1345
|
+
// A theme of your own from a few dials, both appearances generated (checked against APCA).
|
|
1346
|
+
const ocean = defineTheme({
|
|
1347
|
+
name: "ocean",
|
|
1348
|
+
extends: themes.knobs, // optional
|
|
1349
|
+
gray: { hue: 240, chroma: 0.008 },
|
|
1350
|
+
accent: "#4f8cff", // or { dark, light }
|
|
1351
|
+
contrast: 60, // 0–100
|
|
1352
|
+
radius: 6,
|
|
1353
|
+
density: "compact",
|
|
1354
|
+
material: "solid", // "glass" | "solid" | "flat"
|
|
1355
|
+
tokens: { dark: { "bg-page": "#05070a" } }, // any token, per appearance
|
|
1356
|
+
})
|
|
1357
|
+
```
|
|
1281
1358
|
|
|
1282
|
-
|
|
1359
|
+
`appearance` is `"dark"`, `"light"`, `"system"` (the OS setting) or `"inherit"`, the default: the page decides, through a `color-scheme`, a `.dark`/`.light` class, `[data-theme]` or `[data-appearance]` on an ancestor, or an outer knobs element, else the OS. A theme is a value: `theme.dark` and `theme.light` hold every token resolved, `theme.gpu` the colours as floats for WebGPU, and `theme.css()` its stylesheet. The old `theme: "dark" | "light" | "auto"` still works as an appearance, and `preset` (`labPreset`, `problemsPreset`) still lays its tokens over the theme; both are deprecated. `examples/themes.html` shows every piece in both themes and appearances, with a builder for `defineTheme`'s dials that copies the call as code.
|
|
1360
|
+
|
|
1361
|
+
### Overriding tokens
|
|
1362
|
+
|
|
1363
|
+
Set any token on `:root`, `.knobs-panel` or any ancestor; a host's value wins at whatever level it's set. Tokens derived from others (hover and selected fills from the surfaces, the slider handle from the accent, sizes from `--k-density`) are derived on each panel and knobs/ui piece, so an override anywhere above them carries through. The full list, with each theme's values, is under [Tokens](#tokens); the old names (`--k-surface`, `--k-text`, `--ku-*`) still work for now.
|
|
1283
1364
|
|
|
1284
1365
|
```css
|
|
1285
1366
|
:root {
|
|
1286
1367
|
--k-bg: #1c1c1e;
|
|
1287
|
-
--k-
|
|
1288
|
-
--k-
|
|
1289
|
-
--k-
|
|
1290
|
-
--k-text: rgba(255, 255, 255, 0.92);
|
|
1291
|
-
--k-label: rgba(255, 255, 255, 0.64);
|
|
1292
|
-
--k-accent: #fff;
|
|
1293
|
-
--k-modified: #ff9f0a;
|
|
1294
|
-
--k-width: 288px;
|
|
1295
|
-
--k-pad: 10px;
|
|
1296
|
-
--k-inset: 10px;
|
|
1297
|
-
--k-row: 32px;
|
|
1298
|
-
--k-gap: 4px;
|
|
1299
|
-
--k-radius: 8px;
|
|
1300
|
-
--k-panel-radius: 12px;
|
|
1301
|
-
--k-font-size: 12px;
|
|
1302
|
-
--k-label-weight: 500;
|
|
1303
|
-
--k-blur: 1; /* motion strength: readout blur and pill stretch */
|
|
1304
|
-
--k-motion-blur: 0; /* up to 1: moving fill edges and pills smear by their speed */
|
|
1305
|
-
--k-range-blur: .5; /* up to 1: a range band's gliding ends smear by their speed */
|
|
1306
|
-
--k-curve-blur: .5; /* up to 1: morphing curves, easings and gradients smear by their speed */
|
|
1307
|
-
--k-step-resist: 0.25; /* a stepped slider's pull off its stop (0 snaps, 1 follows) */
|
|
1308
|
-
--k-glow: 0.5;
|
|
1309
|
-
--k-roll: 420; /* readout roll (ms) */
|
|
1310
|
-
--k-drag-roll: 0; /* roll while dragging (ms; 0 snaps) */
|
|
1311
|
-
--k-cascade: 12; /* preset ripple between rows (ms; 0 moves them together) */
|
|
1312
|
-
--k-glass: 0.6; /* 0 is opaque; up to 1 thins the panel over a blurred backdrop */
|
|
1313
|
-
--k-glass-tone: 0.5; /* backdrop brightness under full glass (light theme: 1.08) */
|
|
1314
|
-
--k-spring: 0.24 0.16; /* duration and bounce values settle on (GPU); pair with: */
|
|
1315
|
-
--k-fill-ease: linear(...); /* springCss(value).easing, for CSS transitions */
|
|
1316
|
-
--k-fill-ms: 300; /* springCss(value).duration */
|
|
1317
|
-
--k-reveal-ease: cubic-bezier(.2, .8, .2, 1); /* how editors and messages open */
|
|
1318
|
-
--k-rim: rgba(255, 255, 255, 0.09);
|
|
1368
|
+
--k-accent: #4f8cff;
|
|
1369
|
+
--k-radius: 6px;
|
|
1370
|
+
--k-density: 0.875;
|
|
1319
1371
|
}
|
|
1320
1372
|
```
|
|
1321
1373
|
|
|
1374
|
+
The panel's motion dials are tokens too: `--k-blur` (readout blur and pill stretch), `--k-motion-blur`, `--k-range-blur`, `--k-curve-blur`, `--k-step-resist`, `--k-roll` and `--k-drag-roll` (readout roll, ms), `--k-cascade` (preset ripple, ms), `--k-glass-tone`, `--k-reveal-ease`, and the spring values settle on, `--k-spring` (GPU) with `--k-fill-ease` and `--k-fill-ms` (`springCss(value)`, for CSS).
|
|
1375
|
+
|
|
1322
1376
|
Changing a token glides: colours, lengths, weights, glass and glow move on the panel spring (`--k-fill-ease` over `--k-fill-ms`), so a theme switch or a preset of tokens animates in both renderers. While a control is dragged they follow it step for step.
|
|
1323
1377
|
|
|
1324
1378
|
A floating panel opens out of its chip like liquid glass. The press squeezes the cog (to 0.94, easing in over 60 ms); then each edge of the glass rides its own spring from the chip to the panel. The edges facing the way it travels lead on a stiff spring (as stiff as their axis carries the travel) and the others trail on a softer one, so the glass stretches toward the panel, overshoots a little and settles in under 300 ms, its corners going from the cog's circle to the panel's radius. The content follows a beat behind: blurred 8 px, faded and at 97%, revealed top first, landing exactly crisp. Closing runs it backwards and faster: the content blurs out first, then the glass pulls into the cog with its far edges trailing (about 200 ms). Esc, the close button or the chip itself folds it back; toggling mid-way reverses from wherever the shape is, keeping its velocity. The WebGPU overlay blurs, fades and scales the content in its shader, so both renderers match. A reload brings an open panel back in place, with no morph.
|
|
@@ -1343,6 +1397,254 @@ A reset that went back to the active preset rather than the defaults carries `ba
|
|
|
1343
1397
|
|
|
1344
1398
|
`patch` holds only the fields that changed. The die page (`examples/die.ts`) rolls its demo dice this way.
|
|
1345
1399
|
|
|
1400
|
+
## Tokens
|
|
1401
|
+
|
|
1402
|
+
Generated from the token schema (`bun run scripts/tokens-table.ts`), with the Knobs theme's values. Inputs are the dials a theme turns, semantic tokens are what pieces read, hooks style one component.
|
|
1403
|
+
|
|
1404
|
+
<!-- tokens:start -->
|
|
1405
|
+
|
|
1406
|
+
### Accent
|
|
1407
|
+
|
|
1408
|
+
| Token | Tier | Dark | Light | |
|
|
1409
|
+
|---|---|---|---|---|
|
|
1410
|
+
| `--k-accent` | input | `#ffffff` | `#1c1c1e` | The one accent: primary actions, handles, focus. Hover, soft and selection derive from it in CSS. |
|
|
1411
|
+
| `--k-accent-hover` | semantic | `#e5e5e5` | `#2e2e30` | Hovered accent. Derived: `color-mix(in oklch,var(--k-accent) 90%,var(--k-bg))`. |
|
|
1412
|
+
| `--k-accent-soft` | semantic | `#ffffff26` | `#1c1c1e26` | Accent tint behind accent content. Derived: `color-mix(in oklch,var(--k-accent) 15%,transparent)`. |
|
|
1413
|
+
| `--k-accent-fg` | semantic | `#ffffff` | `#1c1c1e` | Accent as text on bg (≥ Lc 60). |
|
|
1414
|
+
| `--k-on-accent` | semantic | `#111111` | `#ffffff` | Content placed on a solid accent. |
|
|
1415
|
+
|
|
1416
|
+
### Inputs
|
|
1417
|
+
|
|
1418
|
+
| Token | Tier | Dark | Light | |
|
|
1419
|
+
|---|---|---|---|---|
|
|
1420
|
+
| `--k-contrast` | input | `1` | `1` | Contrast multiplier for fills and alpha borders (1 = default; the theme's 0–100 dial maps 50 → 1). |
|
|
1421
|
+
|
|
1422
|
+
### Shape
|
|
1423
|
+
|
|
1424
|
+
| Token | Tier | Dark | Light | |
|
|
1425
|
+
|---|---|---|---|---|
|
|
1426
|
+
| `--k-radius` | input | `8px` | `8px` | Control corner radius. 0 squares everything, including capsules. Scales with density. |
|
|
1427
|
+
| `--k-radius-panel` | semantic | `calc(var(--k-radius) + var(--k-unit))` | `calc(var(--k-radius) + var(--k-unit))` | Panel and card radius, concentric with controls. Derived: `calc(var(--k-radius) + var(--k-unit))`. |
|
|
1428
|
+
| `--k-round` | semantic | `calc(var(--k-radius) * 999)` | `calc(var(--k-radius) * 999)` | Capsule radius that squares when --k-radius is 0. Use instead of 999px. Derived: `calc(var(--k-radius) * 999)`. |
|
|
1429
|
+
|
|
1430
|
+
### Size
|
|
1431
|
+
|
|
1432
|
+
| Token | Tier | Dark | Light | |
|
|
1433
|
+
|---|---|---|---|---|
|
|
1434
|
+
| `--k-density` | input | `1` | `1` | Size multiplier: compact .875, default 1, comfortable 1.1875. |
|
|
1435
|
+
| `--k-unit` | semantic | `4px` | `4px` | The spacing unit. |
|
|
1436
|
+
| `--k-row` | semantic | `round(calc(32px * var(--k-density)),2px)` | `round(calc(32px * var(--k-density)),2px)` | Row height. Derived: `round(calc(32px * var(--k-density)),2px)`. |
|
|
1437
|
+
| `--k-width` | semantic | `288px` | `288px` | Panel width. |
|
|
1438
|
+
|
|
1439
|
+
### Type
|
|
1440
|
+
|
|
1441
|
+
| Token | Tier | Dark | Light | |
|
|
1442
|
+
|---|---|---|---|---|
|
|
1443
|
+
| `--k-font` | input | `system-ui,-apple-system,"SF Pro Text",sans-serif` | `system-ui,-apple-system,"SF Pro Text",sans-serif` | UI font stack. |
|
|
1444
|
+
| `--k-mono` | input | `"SF Mono",SFMono-Regular,ui-monospace,Menlo,monospace` | `"SF Mono",SFMono-Regular,ui-monospace,Menlo,monospace` | Monospace font stack. |
|
|
1445
|
+
| `--k-font-size` | semantic | `round(calc(12px * (1 + (var(--k-density) - 1) * .5)),1px)` | `round(calc(12px * (1 + (var(--k-density) - 1) * .5)),1px)` | Body size; a 5-step scale derives privately. Derived: `round(calc(12px * (1 + (var(--k-density) - 1) * .5)),1px)`. |
|
|
1446
|
+
| `--k-value-font` | semantic | `var(--k-font)` | `var(--k-font)` | Font for readouts and values (labPreset: mono). Derived: `var(--k-font)`. |
|
|
1447
|
+
|
|
1448
|
+
### Surfaces
|
|
1449
|
+
|
|
1450
|
+
| Token | Tier | Dark | Light | |
|
|
1451
|
+
|---|---|---|---|---|
|
|
1452
|
+
| `--k-bg-page` | semantic | `#0a0a0b` | `#e6e6e8` | The page behind panels; workspace backdrops. |
|
|
1453
|
+
| `--k-bg-inset` | semantic | `#151516` | `#ededef` | Wells sunk into a panel: tracks, inputs, segmented backgrounds. |
|
|
1454
|
+
| `--k-bg` | semantic | `#1c1c1d` | `#f5f5f7` | The panel surface. |
|
|
1455
|
+
| `--k-bg-raised` | semantic | `#232324` | `#ffffff` | Raised within a panel: the active segment, cards on a panel. |
|
|
1456
|
+
| `--k-bg-overlay` | semantic | `#29292a` | `#ffffff` | Floating surfaces: menus, popovers, tooltips. |
|
|
1457
|
+
|
|
1458
|
+
### Fills
|
|
1459
|
+
|
|
1460
|
+
| Token | Tier | Dark | Light | |
|
|
1461
|
+
|---|---|---|---|---|
|
|
1462
|
+
| `--k-fill` | semantic | `#f4f5f90e` | `#1a1a1d0e` | Resting fill for controls. Derived: `color-mix(in oklch,var(--k-fg) calc(5.5% * var(--k-contrast)),transparent)`. |
|
|
1463
|
+
| `--k-fill-hover` | semantic | `#f4f5f914` | `#1a1a1d14` | Hovered fill. Derived: `color-mix(in oklch,var(--k-fg) calc(8% * var(--k-contrast)),transparent)`. |
|
|
1464
|
+
| `--k-fill-selected` | semantic | `#f4f5f91c` | `#1a1a1d1c` | Pressed or selected fill. Derived: `color-mix(in oklch,var(--k-fg) calc(11% * var(--k-contrast)),transparent)`. |
|
|
1465
|
+
|
|
1466
|
+
### Foreground
|
|
1467
|
+
|
|
1468
|
+
| Token | Tier | Dark | Light | |
|
|
1469
|
+
|---|---|---|---|---|
|
|
1470
|
+
| `--k-fg` | semantic | `#f4f5f9` | `#1a1a1d` | Primary text and icons (≥ Lc 90 on bg). |
|
|
1471
|
+
| `--k-fg-2` | semantic | `#d0d0d5` | `#59595d` | Secondary text: labels (≥ Lc 75). |
|
|
1472
|
+
| `--k-fg-3` | semantic | `#9e9ea3` | `#96969b` | Tertiary text: metadata, units, captions (≥ Lc 45). |
|
|
1473
|
+
| `--k-fg-4` | semantic | `#7d7e81` | `#b4b5b9` | Placeholder and disabled text (≥ Lc 30). |
|
|
1474
|
+
|
|
1475
|
+
### Borders
|
|
1476
|
+
|
|
1477
|
+
| Token | Tier | Dark | Light | |
|
|
1478
|
+
|---|---|---|---|---|
|
|
1479
|
+
| `--k-border-subtle` | semantic | `#f4f5f90f` | `#1a1a1d0f` | Hairline edges and dividers (below Lc 15 by design). Derived: `color-mix(in oklch,var(--k-fg) calc(6% * var(--k-contrast)),transparent)`. |
|
|
1480
|
+
| `--k-border` | semantic | `#f4f5f91a` | `#1a1a1d1a` | Default borders. Derived: `color-mix(in oklch,var(--k-fg) calc(10% * var(--k-contrast)),transparent)`. |
|
|
1481
|
+
| `--k-border-strong` | semantic | `#58585c` | `#d3d3d7` | Interactive edges (≥ Lc 15; solid). |
|
|
1482
|
+
|
|
1483
|
+
### Status
|
|
1484
|
+
|
|
1485
|
+
| Token | Tier | Dark | Light | |
|
|
1486
|
+
|---|---|---|---|---|
|
|
1487
|
+
| `--k-ok` | semantic | `#59d38c` | `#0a7e3a` | Success. |
|
|
1488
|
+
| `--k-warn` | semantic | `#f7bc50` | `#b66c00` | Warning. |
|
|
1489
|
+
| `--k-danger` | semantic | `#fa6863` | `#cc2827` | Error, destructive. Put text on danger-soft rather than bare bg. |
|
|
1490
|
+
| `--k-info` | semantic | `#55b8f8` | `#0071b3` | Informational. |
|
|
1491
|
+
| `--k-ok-soft` | semantic | `#59d38c26` | `#0a7e3a26` | Success tint. Derived: `color-mix(in oklch,var(--k-ok) 15%,transparent)`. |
|
|
1492
|
+
| `--k-warn-soft` | semantic | `#f7bc5026` | `#b66c0026` | Warning tint. Derived: `color-mix(in oklch,var(--k-warn) 15%,transparent)`. |
|
|
1493
|
+
| `--k-danger-soft` | semantic | `#fa686326` | `#cc282726` | Danger tint. Derived: `color-mix(in oklch,var(--k-danger) 15%,transparent)`. |
|
|
1494
|
+
| `--k-info-soft` | semantic | `#55b8f826` | `#0071b326` | Info tint. Derived: `color-mix(in oklch,var(--k-info) 15%,transparent)`. |
|
|
1495
|
+
|
|
1496
|
+
### Interaction
|
|
1497
|
+
|
|
1498
|
+
| Token | Tier | Dark | Light | |
|
|
1499
|
+
|---|---|---|---|---|
|
|
1500
|
+
| `--k-focus` | semantic | `#ffffff` | `#1c1c1e` | Focus ring colour (drawn as a 2px bg gap, then 2px of focus). Derived: `var(--k-accent)`. |
|
|
1501
|
+
| `--k-selection` | semantic | `#ffffff3d` | `#1c1c1e3d` | Selected rows and ranges. Derived: `color-mix(in oklch,var(--k-accent) 24%,transparent)`. |
|
|
1502
|
+
| `--k-scrim` | semantic | `#00000099` | `#00000040` | Dimming behind modal layers. |
|
|
1503
|
+
|
|
1504
|
+
### Data
|
|
1505
|
+
|
|
1506
|
+
| Token | Tier | Dark | Light | |
|
|
1507
|
+
|---|---|---|---|---|
|
|
1508
|
+
| `--k-diff-add` | semantic | `#59d38c` | `#0a7e3a` | Added lines; line and word tints derive privately (12% / 38%). Derived: `var(--k-ok)`. |
|
|
1509
|
+
| `--k-diff-del` | semantic | `#fa6863` | `#cc2827` | Deleted lines. Derived: `var(--k-danger)`. |
|
|
1510
|
+
| `--k-diff-mod` | semantic | `#f7bc50` | `#b66c00` | Modified lines. Derived: `var(--k-warn)`. |
|
|
1511
|
+
| `--k-series-1` | semantic | `#6db0f4` | `#2378c2` | Chart series 1. |
|
|
1512
|
+
| `--k-series-2` | semantic | `#66c189` | `#008b4f` | Chart series 2. |
|
|
1513
|
+
| `--k-series-3` | semantic | `#d79f4c` | `#9c6900` | Chart series 3. |
|
|
1514
|
+
| `--k-series-4` | semantic | `#ed8c84` | `#b84d49` | Chart series 4. |
|
|
1515
|
+
| `--k-series-5` | semantic | `#bc97e8` | `#885cb5` | Chart series 5. |
|
|
1516
|
+
| `--k-series-6` | semantic | `#25c2c2` | `#008686` | Chart series 6. |
|
|
1517
|
+
|
|
1518
|
+
### Material
|
|
1519
|
+
|
|
1520
|
+
| Token | Tier | Dark | Light | |
|
|
1521
|
+
|---|---|---|---|---|
|
|
1522
|
+
| `--k-shadow-raised` | semantic | `0 1px 2px light-dark(#0000001f,#00000029)` | `0 1px 2px light-dark(#0000001f,#00000029)` | Small lift for raised controls. |
|
|
1523
|
+
| `--k-shadow-overlay` | semantic | `0 12px 36px light-dark(#00000024,#00000085),0 1px 2px light-dark(#00000014,#0000004d)` | `0 12px 36px light-dark(#00000024,#00000085),0 1px 2px light-dark(#00000014,#0000004d)` | Floating panels, menus, popovers. |
|
|
1524
|
+
| `--k-glass` | semantic | `0.6` | `0.6` | Backdrop glass amount (0 = opaque). |
|
|
1525
|
+
| `--k-glow` | semantic | `0.5` | `0.5` | Specular glow amount. |
|
|
1526
|
+
| `--k-rim` | semantic | `#ffffff1a` | `#ffffff99` | Top-edge highlight hairline. |
|
|
1527
|
+
|
|
1528
|
+
### Motion
|
|
1529
|
+
|
|
1530
|
+
| Token | Tier | Dark | Light | |
|
|
1531
|
+
|---|---|---|---|---|
|
|
1532
|
+
| `--k-spring` | semantic | `0.3 0` | `0.3 0` | Default spring as `duration bounce` (the motion module's `default`). |
|
|
1533
|
+
| `--k-hover-in` | semantic | `0` | `0` | Hover wash fade-in, unitless milliseconds. |
|
|
1534
|
+
| `--k-hover-out` | semantic | `120` | `120` | Hover wash fade-out, unitless milliseconds. |
|
|
1535
|
+
| `--k-z` | semantic | `2147480000` | `2147480000` | Base of knobs' stacking ladder; layers add small offsets. |
|
|
1536
|
+
|
|
1537
|
+
### Hooks
|
|
1538
|
+
|
|
1539
|
+
| Token | Tier | Dark | Light | |
|
|
1540
|
+
|---|---|---|---|---|
|
|
1541
|
+
| `--k-slider-track` | hook | `#f4f5f90e` | `#1a1a1d0e` | Slider track. Derived: `var(--k-fill)`. |
|
|
1542
|
+
| `--k-slider-fill` | hook | `#fcfcfd49` | `#1b1b1e49` | Slider value fill. Derived: `color-mix(in oklch,var(--k-accent) 20%,var(--k-fill-selected))`. |
|
|
1543
|
+
| `--k-slider-handle` | hook | `#ffffff` | `#1c1c1e` | Slider handle and range thumbs. Derived: `var(--k-accent)`. |
|
|
1544
|
+
| `--k-toggle-thumb-off` | hook | `#ffffff` | `#ffffff` | A switch's thumb while off, and the ring around colour and gradient thumbs. Derived: `#ffffff`. |
|
|
1545
|
+
| `--k-toggle-track` | hook | `#f4f5f91c` | `#1a1a1d1c` | Toggle track, off. Derived: `var(--k-fill-selected)`. |
|
|
1546
|
+
| `--k-toggle-track-on` | hook | `#ffffff` | `#1c1c1e` | Toggle track, on. Derived: `var(--k-accent)`. |
|
|
1547
|
+
| `--k-toggle-thumb` | hook | `#111111` | `#ffffff` | Toggle thumb. Derived: `var(--k-on-accent)`. |
|
|
1548
|
+
| `--k-segmented-track` | hook | `#151516` | `#ededef` | Segmented control background. Derived: `var(--k-bg-inset)`. |
|
|
1549
|
+
| `--k-segmented-active` | hook | `#232324` | `#ffffff` | Active segment. Derived: `var(--k-bg-raised)`. |
|
|
1550
|
+
| `--k-timeline-playhead` | hook | `#ffffff` | `#1c1c1e` | Timeline playhead. Derived: `var(--k-accent)`. |
|
|
1551
|
+
| `--k-split-gutter` | hook | `#f4f5f90f` | `#1a1a1d0f` | Split pane divider. Derived: `var(--k-border-subtle)`. |
|
|
1552
|
+
| `--k-compare-divider` | hook | `#f4f5f9` | `#1a1a1d` | Before/after divider line. Derived: `var(--k-fg)`. |
|
|
1553
|
+
| `--k-compare-stage` | hook | `#f4f5f90e` | `#1a1a1d0e` | Compare stage background. Derived: `var(--k-fill)`. |
|
|
1554
|
+
| `--k-activity-rail` | hook | `#58585c` | `#d3d3d7` | Activity rail line. Derived: `var(--k-border-strong)`. |
|
|
1555
|
+
| `--k-picker-card` | hook | `#f4f5f90e` | `#1a1a1d0e` | Picker card. Derived: `var(--k-fill)`. |
|
|
1556
|
+
| `--k-pane-bg` | hook | `#1c1c1d` | `#f5f5f7` | knobs/ui pane card. Derived: `var(--k-bg)`. |
|
|
1557
|
+
| `--k-button-bg` | hook | `#f4f5f90e` | `#1a1a1d0e` | Button fill. Derived: `var(--k-fill)`. |
|
|
1558
|
+
| `--k-button-border` | hook | `#00000000` | `#00000000` | Button border. Derived: `transparent`. |
|
|
1559
|
+
| `--k-button-fg` | hook | `#f4f5f9` | `#1a1a1d` | Button text. Derived: `var(--k-fg)`. |
|
|
1560
|
+
| `--k-row-hover` | hook | `#f4f5f907` | `#1a1a1d07` | Row hover wash. Derived: `color-mix(in oklch,var(--k-fill) 50%,transparent)`. |
|
|
1561
|
+
| `--k-group-label` | hook | `#9e9ea3` | `#96969b` | Folder and group headings. Derived: `var(--k-fg-3)`. |
|
|
1562
|
+
| `--k-modified` | hook | `#9e9ea3` | `#96969b` | Marker for values changed from their default. Derived: `var(--k-fg-3)`. |
|
|
1563
|
+
|
|
1564
|
+
### Renamed
|
|
1565
|
+
|
|
1566
|
+
| Old | New |
|
|
1567
|
+
|---|---|
|
|
1568
|
+
| `--ku-accent` | `--k-accent` |
|
|
1569
|
+
| `--ku-radius` | `--k-radius` |
|
|
1570
|
+
| `--ku-font` | `--k-font` |
|
|
1571
|
+
| `--ku-mono` | `--k-mono` |
|
|
1572
|
+
| `--k-page` | `--k-bg-page` |
|
|
1573
|
+
| `--ku-page` | `--k-bg-page` |
|
|
1574
|
+
| `--ku-bg` | `--k-bg` |
|
|
1575
|
+
| `--k-surface` | `--k-fill` |
|
|
1576
|
+
| `--ku-surface` | `--k-fill` |
|
|
1577
|
+
| `--k-surface-hi` | `--k-fill-selected` |
|
|
1578
|
+
| `--ku-surface-hi` | `--k-fill-selected` |
|
|
1579
|
+
| `--k-text` | `--k-fg` |
|
|
1580
|
+
| `--ku-text` | `--k-fg` |
|
|
1581
|
+
| `--k-label` | `--k-fg-2` |
|
|
1582
|
+
| `--ku-label` | `--k-fg-2` |
|
|
1583
|
+
| `--k-dim` | `--k-fg-3` |
|
|
1584
|
+
| `--ku-dim` | `--k-fg-3` |
|
|
1585
|
+
| `--k-edge` | `--k-border-subtle` |
|
|
1586
|
+
| `--k-line` | `--k-border` |
|
|
1587
|
+
| `--ku-line` | `--k-border` |
|
|
1588
|
+
| `--k-line-hi` | `--k-border-strong` |
|
|
1589
|
+
| `--ku-line-hi` | `--k-border-strong` |
|
|
1590
|
+
| `--ku-warn` | `--k-warn` |
|
|
1591
|
+
| `--k-critical` | `--k-danger` |
|
|
1592
|
+
| `--ku-critical` | `--k-danger` |
|
|
1593
|
+
| `--k-ui-select` | `--k-selection` |
|
|
1594
|
+
| `--ku-select` | `--k-selection` |
|
|
1595
|
+
| `--k-panel-radius` | `--k-radius-panel` |
|
|
1596
|
+
| `--ku-panel-radius` | `--k-radius-panel` |
|
|
1597
|
+
| `--ku-row` | `--k-row` |
|
|
1598
|
+
| `--ku-font-size` | `--k-font-size` |
|
|
1599
|
+
| `--ku-value-font` | `--k-value-font` |
|
|
1600
|
+
| `--k-lift-shadow` | `--k-shadow-raised` |
|
|
1601
|
+
| `--k-shadow` | `--k-shadow-overlay` |
|
|
1602
|
+
| `--ku-shadow` | `--k-shadow-overlay` |
|
|
1603
|
+
| `--ku-rim` | `--k-rim` |
|
|
1604
|
+
| `--ku-hover-in` | `--k-hover-in` |
|
|
1605
|
+
| `--ku-hover-out` | `--k-hover-out` |
|
|
1606
|
+
| `--k-fill-rest` | `--k-slider-fill` |
|
|
1607
|
+
| `--k-handle` | `--k-slider-handle` |
|
|
1608
|
+
| `--k-thumb` | `--k-toggle-thumb-off` |
|
|
1609
|
+
| `--k-seg-bg` | `--k-segmented-track` |
|
|
1610
|
+
| `--ku-seg-bg` | `--k-segmented-track` |
|
|
1611
|
+
| `--k-seg-active` | `--k-segmented-active` |
|
|
1612
|
+
| `--k-ui-divider` | `--k-split-gutter` |
|
|
1613
|
+
| `--ku-divider` | `--k-split-gutter` |
|
|
1614
|
+
| `--k-compare-line` | `--k-compare-divider` |
|
|
1615
|
+
| `--k-activity-line` | `--k-activity-rail` |
|
|
1616
|
+
| `--k-ui-pane` | `--k-pane-bg` |
|
|
1617
|
+
| `--ku-pane-bg` | `--k-pane-bg` |
|
|
1618
|
+
| `--k-button-line` | `--k-button-border` |
|
|
1619
|
+
| `--k-button-color` | `--k-button-fg` |
|
|
1620
|
+
| `--k-group-ink` | `--k-group-label` |
|
|
1621
|
+
| `--k-group-color` | `--k-group-label` |
|
|
1622
|
+
| `--k-tray-pad` | `--_k-tray-pad` |
|
|
1623
|
+
| `--k-pad` | `--_k-pad` |
|
|
1624
|
+
| `--k-inset` | `--_k-inset` |
|
|
1625
|
+
| `--ku-inset` | `--_k-inset` |
|
|
1626
|
+
| `--k-gap` | `--_k-gap` |
|
|
1627
|
+
| `--k-radius-md` | `--_k-radius-md` |
|
|
1628
|
+
| `--k-radius-sm` | `--_k-radius-sm` |
|
|
1629
|
+
| `--k-control-h` | `--_k-control-h` |
|
|
1630
|
+
| `--ku-control-h` | `--_k-control-h` |
|
|
1631
|
+
| `--k-value-size` | `--_k-value-size` |
|
|
1632
|
+
| `--ku-value-size` | `--_k-value-size` |
|
|
1633
|
+
| `--k-text-title` | `--_k-text-title` |
|
|
1634
|
+
| `--k-text-body` | `--_k-text-body` |
|
|
1635
|
+
| `--k-text-control` | `--_k-text-control` |
|
|
1636
|
+
| `--k-text-meta` | `--_k-text-meta` |
|
|
1637
|
+
| `--ku-text-meta` | `--_k-text-meta` |
|
|
1638
|
+
| `--k-text-cap` | `--_k-text-cap` |
|
|
1639
|
+
| `--k-font-weight` | `--_k-weight` |
|
|
1640
|
+
| `--ku-font-weight` | `--_k-weight` |
|
|
1641
|
+
| `--k-label-weight` | `--_k-weight-label` |
|
|
1642
|
+
| `--ku-label-weight` | `--_k-weight-label` |
|
|
1643
|
+
| `--k-title-weight` | `--_k-weight-title` |
|
|
1644
|
+
| `--ku-title-weight` | `--_k-weight-title` |
|
|
1645
|
+
|
|
1646
|
+
<!-- tokens:end -->
|
|
1647
|
+
|
|
1346
1648
|
## Show, hide and unlock
|
|
1347
1649
|
|
|
1348
1650
|
**Backtick.** `` ` `` shows and hides every panel and chip on the page at once, the way a page's
|
|
@@ -1501,6 +1803,8 @@ The package has two core exports selected through the standard `development` con
|
|
|
1501
1803
|
| --- | --- |
|
|
1502
1804
|
| `split(container, { panes, direction?, id?, storage?, onLayout?, preset?, theme? })` | Resizable panes and docks that collapse to rails or headers; layouts saved by `id`. |
|
|
1503
1805
|
| `tree(container, { items, onSelect?, onMove?, onRename?, toggles?, filter?, typeahead?, … })` | A virtualised outliner with keyboard moves, drag reorder and rename; `typeahead: false \| (key) => boolean` leaves letters to the page. |
|
|
1806
|
+
| `codeDiff(container, { patch? \| files?, file?, layout?, wrap?, height?, onFile?, onLines?, … })` | A code diff in a card (needs `@pierre/diffs`); files and layouts fade in place. |
|
|
1807
|
+
| `diffReview(container, { patch? \| files?, id?, … })` | The changed files (a filter, viewed ticks) in a tree beside the diff, in a split. |
|
|
1504
1808
|
| `readout(field, { preset?, theme?, label? })` | A readout on its own: `{ element, push(value), value, dispose() }`. |
|
|
1505
1809
|
| `popover(anchor, content, { placement?, offset?, role?, label?, focus?, trap?, closeOnOutside?, onClose? })` | A floating layer that flips and shifts; `{ element, open, reposition(), close() }`. |
|
|
1506
1810
|
| `tooltip(el, text \| { text, shortcut?, placement?, delay? })` / `tooltips(doc)` | Delayed tooltips with instant hand-off and shortcut chips; `data-tip` attributes. |
|
|
@@ -1509,6 +1813,7 @@ The package has two core exports selected through the standard `development` con
|
|
|
1509
1813
|
| `toast(message, { tone?, linger?, action?, within? })` | A status pill at the bottom centre. |
|
|
1510
1814
|
| `menu(anchor, items, { placement?, label?, onSelect?, onClose? })` / `contextMenu(target, items, options?)` | Menus with icons, shortcut chips, checks, radios and submenus; a right-click or menu-key menu. |
|
|
1511
1815
|
| `timeline(container, { duration, time?, fps?, units?, markers?, ranges?, snap?, onSeek?, … })` | A zoomable ruler and playhead with markers, loop ranges and playback. |
|
|
1816
|
+
| `compare(container, { before, after, mode?, wipe?, timeline?, onMode?, onWipe?, onView?, … })` / `compareStrip(container, { before, after, timeline })` | A before/after viewer for videos and images (a split screen zoomed together, wipe, difference, flicker) on one clock, with a loop; a frame strip with difference bars, on its own. |
|
|
1512
1817
|
| `activity(container, { events, selected?, onSelect?, onActivate?, onToggle?, onPlay?, newest?, follow?, keys?, icons?, tints? })` | A rail of events on one continuous line, with groups that open in place; `{ set, add, select, expand, focus, destroy }`. |
|
|
1513
1818
|
| `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. |
|
|
1514
1819
|
| `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. |
|