@motion-proto/live-tokens 0.35.0 → 0.36.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.36.0 — ImageLightbox `capNatural`: stop upscaling small sources
4
+
5
+ ### Added
6
+
7
+ - **`ImageLightbox` gains a `capNatural` prop.** By default the modal opens
8
+ fitted to the viewport, which upscales a small source (e.g. a low-res GIF or
9
+ screenshot) until it looks soft. Set `capNatural` and the open fit is clamped
10
+ to the source's natural resolution (100%, 1 source px = 1 screen px), so small
11
+ images stay crisp while larger ones fit the viewport exactly as before. It only
12
+ bounds the initial open; pair it with `maxZoom={1}` to also stop the `extended`
13
+ zoom controls from magnifying past 100%. Needs the natural pixel size (from
14
+ `width`/`height` or the loaded image) — until that's known the image fits the
15
+ viewport, then snaps to the cap once measured.
16
+
3
17
  ## 0.35.0 — Dev routes moved to a reserved `/live-tokens/*` namespace
4
18
 
5
19
  ### Changed (breaking)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -28,12 +28,22 @@
28
28
  extended?: boolean;
29
29
  /** Maximum zoom, as a multiple of the image's natural resolution: `1` = 100%
30
30
  of the source's real pixels (1 source px = 1 screen px), `2` = 200%. The
31
- modal always opens fitted to the viewport; this only caps how far the
32
- `extended` zoom controls can magnify. Unset = the default 5x-the-fit cap.
33
- An image whose fitted size already exceeds the cap simply can't be zoomed
34
- in. Needs the natural pixel size (from `width`/`height`, or the loaded
35
- image); until that's known the default cap applies. */
31
+ modal opens fitted to the viewport (or to natural size when `capNatural`
32
+ is set); this only caps how far the `extended` zoom controls can magnify.
33
+ Unset = the default 5x-the-fit cap. An image whose fitted size already
34
+ exceeds the cap simply can't be zoomed in. Needs the natural pixel size
35
+ (from `width`/`height`, or the loaded image); until that's known the
36
+ default cap applies. */
36
37
  maxZoom?: number | undefined;
38
+ /** Cap the opened image at its natural resolution (100%): the modal still
39
+ opens centered, but never scales the source above 1:1, so a small source
40
+ (e.g. a low-res GIF) stays crisp instead of being upscaled to fill the
41
+ viewport. Larger images are unaffected — they fit the viewport as usual.
42
+ Only bounds the initial open fit; pair with `maxZoom={1}` to also stop the
43
+ `extended` controls zooming past 100%. Needs the natural pixel size (from
44
+ `width`/`height`, or the loaded image); until that's known the image fits
45
+ the viewport, then snaps to the cap once measured. */
46
+ capNatural?: boolean;
37
47
  }
38
48
 
39
49
  let {
@@ -46,6 +56,7 @@
46
56
  fit = 'contain',
47
57
  extended = false,
48
58
  maxZoom = undefined,
59
+ capNatural = false,
49
60
  }: Props = $props();
50
61
 
51
62
  const items = $derived(
@@ -151,7 +162,11 @@
151
162
  if (!aspect) {
152
163
  return { top: vh * 0.04, left: vw * 0.03, width: capW, height: capH };
153
164
  }
154
- const tileW = Math.min(capW, capH * aspect);
165
+ let tileW = Math.min(capW, capH * aspect);
166
+ if (capNatural) {
167
+ const nw = naturalWidthOf(current);
168
+ if (nw) tileW = Math.min(tileW, nw);
169
+ }
155
170
  const tileH = tileW / aspect;
156
171
  return {
157
172
  top: (vh - tileH) / 2,