@neo-ui-kit/react 1.3.1 → 1.3.2

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 CHANGED
@@ -25,13 +25,19 @@ import "@neo-ui-kit/react/styles.css";
25
25
  To test repository changes before publishing, run `npm run package` from the repository root and install the resulting archive in a separate application:
26
26
 
27
27
  ```bash
28
- npm install /path/to/adui/release/neo-ui-kit-react-1.3.1.tgz
28
+ npm install /path/to/adui/release/neo-ui-kit-react-1.3.2.tgz
29
29
  ```
30
30
 
31
31
  Maintainers publish new versions from `Pro100Dimka/adui` using the `release.yml` GitHub Actions workflow, a matching `v<version>` tag, and the `npm-publication` environment. npm trusts its OIDC identity; no `NPM_TOKEN` is required. The workflow checks the project and packed consumer before publishing the tested archive. Published `1.0.0` cannot be overwritten: code and README changes in npm require a new package version. See the [repository release instructions](https://github.com/Pro100Dimka/adui#npm-пакет).
32
32
 
33
33
  ## Theme
34
34
 
35
+ Decorations inside `data-ad-paused` or `data-ad-offscreen` boundaries retain their
36
+ phase while paused. After changing either attribute yourself, call `refreshMotion()`
37
+ (imported from `@neo-ui-kit/react`) after the DOM update.
38
+ This refreshes both CSS and scripted decorations on the next shared frame.
39
+ `usePauseOffscreen` handles its own notifications.
40
+
35
41
  Two colours build the whole palette (a primary scale, a secondary scale and tinted neutrals):
36
42
 
37
43
  ```tsx
@@ -221,6 +227,20 @@ function Preferences() {
221
227
 
222
228
  Canvas-rendered volume knob. Drag near the edge to rotate, drag from the center to move linearly, click the outer scale to jump. Wheel and arrow keys change the value, Shift gives a fine step, PageUp/PageDown change by 10, Home/End set 0/100, Escape cancels a drag, double-click resets. `onValueChange` fires while adjusting, `onValueCommit` after the gesture. `readOnly` turns it into a gauge.
223
229
 
230
+ ## 1.3.2 release notes
231
+
232
+ - Waveform updates only its moving layers and pauses invisible sparks and fully clipped sheen.
233
+ - RotaryKnob completes all canvas layers after StrictMode restarts, avoids duplicate pending renders of the same size, and waits for square canvas dimensions before drawing its feedback ring.
234
+ - Scripted decorations respect pause/offscreen boundaries. `refreshMotion()` batches boundary changes without checking ancestors on every frame.
235
+ - FormFields helper selectors avoid invalidating unrelated descendants while keeping the same layout.
236
+ - Playground code windows can expand to the full browser viewport. The editor and preview resize together without resetting the draft.
237
+
238
+ Standalone profiling on an RTX 3060, Electron 42.11.8, at 1920×1050 with CPU ×4
239
+ showed 36.9 → 58.9 FPS over 45 seconds for 20 cards combining a waveform, knob,
240
+ and animated border. This is a measured scenario, not a guarantee of 60 FPS for
241
+ every application. CPU ×16/×32 and software compositing still have substantial
242
+ slowdowns; changing rasterization alone did not remove them.
243
+
224
244
  ## 1.3.1 release notes
225
245
 
226
246
  - ThemePicker gallery cards accept separate backgrounds and keep selected/unselected pictures at the same size, with readable captions and theme-coloured accent lines.
@@ -36,6 +36,7 @@ var pendingTargets = /* @__PURE__ */ new Set();
36
36
  var active = [];
37
37
  var lastScan = -Infinity;
38
38
  var needsScan = false;
39
+ var boundariesChanged = false;
39
40
  var tickers = /* @__PURE__ */ new Set();
40
41
  U.subscribeTick = (listener) => {
41
42
  tickers.add(listener);
@@ -85,6 +86,16 @@ function scan(now, animations = pendingAnimations) {
85
86
  }
86
87
  function tick(now) {
87
88
  raf = null;
89
+ if (boundariesChanged) {
90
+ boundariesChanged = false;
91
+ for (const scope of preferenceScopes) {
92
+ const runnable = scope.enabled && !scope.root.closest?.("[data-ad-offscreen], [data-ad-paused]");
93
+ if (runnable === running.has(scope)) continue;
94
+ scope.previous = null;
95
+ if (runnable) running.add(scope);
96
+ else running.delete(scope);
97
+ }
98
+ }
88
99
  if (needsScan || now - lastScan > 400) scan(now);
89
100
  const deadline = performance.now() + 8;
90
101
  for (const item of measurements) {
@@ -132,6 +143,7 @@ function schedule() {
132
143
  }
133
144
  function refreshMotion() {
134
145
  needsScan = true;
146
+ boundariesChanged = true;
135
147
  schedule();
136
148
  }
137
149
  var wake = () => {
@@ -247,7 +259,7 @@ U.createMotion = (root = document) => {
247
259
  this.previous = null;
248
260
  const target = root.dataset ? root : root.host || document.documentElement;
249
261
  target.dataset.adMotion = enabled ? "on" : "off";
250
- if (this.enabled) running.add(this);
262
+ if (this.enabled && !root.closest?.("[data-ad-offscreen], [data-ad-paused]")) running.add(this);
251
263
  else running.delete(this);
252
264
  schedule();
253
265
  return this.enabled;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  themes,
3
3
  usePauseOffscreen
4
- } from "./chunk-WOUWYJ6U.js";
4
+ } from "./chunk-ICRXRGIW.js";
5
5
  import {
6
6
  assignRef,
7
7
  clamp,
@@ -2635,6 +2635,7 @@ var RotaryKnob = (p) => {
2635
2635
  let latestHover = null;
2636
2636
  let previousFrame = 0;
2637
2637
  let disposed = false;
2638
+ let renderingSize = 0;
2638
2639
  async function render() {
2639
2640
  if (disposed || !painted) return;
2640
2641
  const cssSize = root.getBoundingClientRect().width;
@@ -2644,7 +2645,8 @@ var RotaryKnob = (p) => {
2644
2645
  cssSize * Math.max(2, Math.min(devicePixelRatio || 1, 2.5))
2645
2646
  )
2646
2647
  );
2647
- if (!size || canvas.width === size && canvas.height === size) return;
2648
+ if (!size || renderingSize === size || [canvas, rotor, feedback].every((layer) => layer.width === size && layer.height === size)) return;
2649
+ renderingSize = size;
2648
2650
  canvas.width = canvas.height = size;
2649
2651
  const center = size / 2;
2650
2652
  const radius = size * 0.445;
@@ -2788,6 +2790,7 @@ var RotaryKnob = (p) => {
2788
2790
  }
2789
2791
  });
2790
2792
  if (disposed || canvas.width !== size || canvas.height !== size) return;
2793
+ renderingSize = 0;
2791
2794
  ctx.drawImage(picture, 0, 0);
2792
2795
  rotor.width = rotor.height = size;
2793
2796
  feedback.width = feedback.height = size;
@@ -3316,6 +3319,8 @@ var RotaryKnob = (p) => {
3316
3319
  "canvas",
3317
3320
  {
3318
3321
  className: "knob__surface knob__feedback",
3322
+ width: 0,
3323
+ height: 0,
3319
3324
  "aria-hidden": "true",
3320
3325
  ref: feedbackRef
3321
3326
  }
@@ -47,7 +47,8 @@
47
47
  align-self: start;
48
48
  margin-block-start: calc(var(--ad-space-1) * 1.5);
49
49
  }
50
- .ad-form-field > :has(> .ad-field) > :not(.ad-field) {
50
+ .ad-form-field > * > .ad-field ~ :not(.ad-field),
51
+ .ad-form-field > * > :not(.ad-field):has(~ .ad-field) {
51
52
  grid-row: 4;
52
53
  }
53
54
  }
package/dist/core.d.ts CHANGED
@@ -2,7 +2,7 @@ export { ThemeProvider, themes, } from "./components/foundation/ThemeProvider/Th
2
2
  export type { ThemeName, ThemeProviderProps, } from "./components/foundation/ThemeProvider/ThemeProvider";
3
3
  export { useReducedMotion, useMotion } from "./core/providers/context";
4
4
  export { useBorder, usePauseOffscreen, useTick, useDecoration, useSmoothWheel, useTabShape, } from "./core/motion/hooks";
5
- export { getMotionStats, setMotionFrameRate, subscribeTick } from "./core/motion-engine.js";
5
+ export { getMotionStats, refreshMotion, setMotionFrameRate, subscribeTick } from "./core/motion-engine.js";
6
6
  export { LocaleProvider, addMessages, getLocale, plural, setLocale, tr, translate, useLocale, useTr } from "./core/i18n";
7
7
  export type { Locale, LocaleProviderProps, Messages } from "./core/i18n";
8
8
  export { messages as builtInMessages } from "./core/messages";
package/dist/core.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  ThemeProvider,
3
3
  getMotionStats,
4
+ refreshMotion,
4
5
  setMotionFrameRate,
5
6
  subscribeTick,
6
7
  themes,
@@ -10,7 +11,7 @@ import {
10
11
  useSmoothWheel,
11
12
  useTabShape,
12
13
  useTick
13
- } from "./chunks/chunk-WOUWYJ6U.js";
14
+ } from "./chunks/chunk-ICRXRGIW.js";
14
15
  import {
15
16
  LocaleProvider,
16
17
  addMessages,
@@ -35,6 +36,7 @@ export {
35
36
  getLocale,
36
37
  getMotionStats,
37
38
  plural,
39
+ refreshMotion,
38
40
  setLocale,
39
41
  setMotionFrameRate,
40
42
  subscribeTick,
package/dist/forms.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  defaultFieldRegistry,
5
5
  useForm,
6
6
  useFormContext
7
- } from "./chunks/chunk-TQWTM23C.js";
8
- import "./chunks/chunk-WOUWYJ6U.js";
7
+ } from "./chunks/chunk-TAQHE3C5.js";
8
+ import "./chunks/chunk-ICRXRGIW.js";
9
9
  import "./chunks/chunk-BJSFN52W.js";
10
10
  import "./chunks/chunk-FCOU3ODR.js";
11
11
  export {
package/dist/index.d.ts CHANGED
@@ -151,7 +151,7 @@ export { WaveDecoration } from "./components/media/WaveDecoration/WaveDecoration
151
151
  export { Waveform } from "./components/media/Waveform/Waveform";
152
152
  export { useWaveformPeaks } from "./components/media/Waveform/useWaveformPeaks";
153
153
  export * from "./components/media/shared";
154
- export { getMotionStats, setMotionFrameRate, subscribeTick } from "./core/motion-engine.js";
154
+ export { getMotionStats, refreshMotion, setMotionFrameRate, subscribeTick } from "./core/motion-engine.js";
155
155
  export { Router, useRouter, matchRoute, } from "./components/navigation/Router/Router";
156
156
  export type { RouterProps, RouterValue, RouteDefinition, RouteMatch, RouteAccessContext, } from "./components/navigation/Router/Router";
157
157
  export { Form, useForm, useFormContext } from "./components/forms/Form/Form";
package/dist/index.js CHANGED
@@ -61,7 +61,7 @@ import {
61
61
  useSvgId,
62
62
  variantMaterial,
63
63
  vectorElement
64
- } from "./chunks/chunk-TQWTM23C.js";
64
+ } from "./chunks/chunk-TAQHE3C5.js";
65
65
  import {
66
66
  ThemeProvider,
67
67
  getMotionStats,
@@ -76,7 +76,7 @@ import {
76
76
  useTabShape,
77
77
  useThemePalette,
78
78
  useTick
79
- } from "./chunks/chunk-WOUWYJ6U.js";
79
+ } from "./chunks/chunk-ICRXRGIW.js";
80
80
  import {
81
81
  LocaleProvider,
82
82
  addMessages,
@@ -5789,8 +5789,6 @@ function Waveform({
5789
5789
  "data-playing": playing || void 0,
5790
5790
  style: {
5791
5791
  ...p.style,
5792
- "--ad-wave-played": `${progress * 100}%`,
5793
- "--ad-wave-progress": progress,
5794
5792
  "--ad-wave-hover": hover === null ? void 0 : `${hover * 100}%`,
5795
5793
  ...color3 ? { "--ad-wave-color": color3 } : {}
5796
5794
  },
@@ -5836,7 +5834,7 @@ function Waveform({
5836
5834
  seek(clamp(next, 0, duration));
5837
5835
  },
5838
5836
  children: [
5839
- /* @__PURE__ */ jsx46("span", { className: "ad-waveform-floor", "aria-hidden": true }),
5837
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-floor", style: { transform: `scaleX(${progress})` }, "aria-hidden": true }),
5840
5838
  /* @__PURE__ */ jsxs37(
5841
5839
  "svg",
5842
5840
  {
@@ -5879,13 +5877,13 @@ function Waveform({
5879
5877
  /* @__PURE__ */ jsx46("path", { d: corePath })
5880
5878
  ] })
5881
5879
  ] }),
5882
- /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-bloom", style: { maskImage: masks.bloom, WebkitMaskImage: masks.bloom }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", {}) }),
5883
- /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-lit", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", {}) }),
5884
- /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-lit ad-waveform-lit-core", style: { maskImage: masks.core, WebkitMaskImage: masks.core }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", {}) }),
5885
- /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-sheen-clip", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-window", children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-window-inner", children: /* @__PURE__ */ jsx46("i", { className: "ad-waveform-sheen" }) }) }) }),
5880
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-bloom", style: { maskImage: masks.bloom, WebkitMaskImage: masks.bloom }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", { style: { transform: `scaleX(${progress})` } }) }),
5881
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-lit", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", { style: { transform: `scaleX(${progress})` } }) }),
5882
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-lit ad-waveform-lit-core", style: { maskImage: masks.core, WebkitMaskImage: masks.core }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("i", { style: { transform: `scaleX(${progress})` } }) }),
5883
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-sheen-clip", "data-ad-paused": progress === 0 ? "" : void 0, style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-window", style: { transform: `translateX(calc(${progress * 100}% - 100%))` }, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-window-inner", style: { transform: `translateX(calc(100% - ${progress * 100}%))` }, children: /* @__PURE__ */ jsx46("i", { className: "ad-waveform-sheen" }) }) }) }),
5886
5884
  /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-depth", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true }),
5887
- /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-shade", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-follow", children: /* @__PURE__ */ jsx46("i", { className: "ad-waveform-spot" }) }) }),
5888
- /* @__PURE__ */ jsx46("span", { className: "ad-waveform-follow", "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-cursor", children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-sparks", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx46("i", {}, i)) }) }) }),
5885
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-layer ad-waveform-shade", style: { maskImage: masks.peaks, WebkitMaskImage: masks.peaks }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-follow", style: { transform: `translateX(${progress * 100}%)` }, children: /* @__PURE__ */ jsx46("i", { className: "ad-waveform-spot" }) }) }),
5886
+ /* @__PURE__ */ jsx46("span", { className: "ad-waveform-follow", style: { transform: `translateX(${progress * 100}%)` }, "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-cursor", children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-sparks", "data-ad-paused": playing ? void 0 : "", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx46("i", {}, i)) }) }) }),
5889
5887
  hover !== null && !disabled && /* @__PURE__ */ jsx46("span", { className: "ad-waveform-hover", "aria-hidden": true, children: /* @__PURE__ */ jsx46("span", { className: "ad-waveform-tip", children: timeText(hover * duration) }) })
5890
5888
  ]
5891
5889
  }
@@ -6825,6 +6823,7 @@ export {
6825
6823
  quantumFieldExperienceControls,
6826
6824
  quantumFieldModes,
6827
6825
  quantumFieldResolution,
6826
+ refreshMotion,
6828
6827
  resizeEdges,
6829
6828
  resolveToken,
6830
6829
  resolveTokens,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo-ui-kit/react",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "NEO UI — React component library",
5
5
  "type": "module",
6
6
  "license": "MIT",