@neo-ui-kit/react 1.3.10 → 1.3.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.
Files changed (3) hide show
  1. package/README.md +10 -1
  2. package/dist/index.js +80 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,6 +4,15 @@ Migration: replace `@ad-voice/ui` with `@neo-ui-kit/react` in dependencies and i
4
4
 
5
5
  NEO UI — React + TypeScript UI kit: glass/ruby surfaces, animated neon borders, fields, tabs, dialogs, audio controls and a melody editor.
6
6
 
7
+ ## 1.3.12
8
+
9
+ - MelodyRoll avoids repeated full style measurements for unrelated animated CSS variables and native root shadows while its beat is idle. Theme aliases still trigger reconfiguration.
10
+ - Live-pitch measurements reached 59.7/59.0 main-thread FPS at CPU ×8/×16 in the library fixture. A karaoke-style animated native shadow still limits ×16 page updates; stable 60 FPS is not established for that scenario. See the [performance report](https://github.com/Pro100Dimka/adui/blob/main/evidence/performance-report.md).
11
+
12
+ ## 1.3.11
13
+
14
+ LevelMeter resumes its waveform when a signal arrives after silence, including when the first animation frame precedes the next sample.
15
+
7
16
  ## 1.3.10
8
17
 
9
18
  - MelodyRoll renders notes, lyrics, key effects and voice effects in a shared worker while keeping native keyboard input and focus.
@@ -38,7 +47,7 @@ import "@neo-ui-kit/react/styles.css";
38
47
  To test repository changes before publishing, run `npm run package` from the repository root and install the resulting archive in a separate application:
39
48
 
40
49
  ```bash
41
- npm install /path/to/adui/release/neo-ui-kit-react-1.3.10.tgz
50
+ npm install /path/to/adui/release/neo-ui-kit-react-1.3.12.tgz
42
51
  ```
43
52
 
44
53
  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-пакет).
package/dist/index.js CHANGED
@@ -6040,7 +6040,7 @@ function LevelMeter({
6040
6040
  samples.push(envelope);
6041
6041
  shape.setAttribute("d", wavePath2(samples));
6042
6042
  }
6043
- if (!analyser && envelope === 0 && samples.every((sample) => sample === 0)) {
6043
+ if (!analyser && input === 0 && envelope === 0 && samples.every((sample) => sample === 0)) {
6044
6044
  shape.setAttribute("transform", "translate(0 0)");
6045
6045
  stop?.();
6046
6046
  stop = void 0;
@@ -6757,18 +6757,88 @@ function createMelodyGlow(root, initialState, onReady) {
6757
6757
  for (const layer of [canvas, voiceCanvas, trailCanvas, noteCanvas, keyCanvas]) layer.setAttribute("aria-hidden", "true");
6758
6758
  let state = initialState, disposed = false, transferred = false, inView = true, revision = 0, signature = "";
6759
6759
  const visible = () => inView && !document.hidden && !root.closest("[data-ad-paused], [data-ad-offscreen]");
6760
- const visibility = () => worker.postMessage({ type: "visible", id, visible: visible() });
6760
+ let lastVisible;
6761
+ const visibility = () => {
6762
+ const next = visible();
6763
+ if (next === lastVisible) return;
6764
+ lastVisible = next;
6765
+ worker.postMessage({ type: "visible", id, visible: next });
6766
+ };
6761
6767
  const resize = new ResizeObserver(configure);
6762
6768
  const intersection = new IntersectionObserver(([entry]) => {
6763
6769
  inView = entry.isIntersecting;
6764
6770
  visibility();
6765
6771
  });
6772
+ let variableUses, variableSheets = [], nativeShadowChanged = false;
6773
+ const readVariableUses = () => {
6774
+ const uses = /* @__PURE__ */ new Map();
6775
+ const declarations = (style, selector) => {
6776
+ for (const property of style) for (const [, variable] of style.getPropertyValue(property).matchAll(/var\(\s*(--[\w-]+)/g)) {
6777
+ if (!uses.has(variable)) uses.set(variable, []);
6778
+ uses.get(variable).push({ property, selector });
6779
+ }
6780
+ };
6781
+ const visit = (rules) => {
6782
+ for (const rule of rules) {
6783
+ if (rule.selectorText?.includes("[style") || rule.conditionText?.includes("style(")) throw new Error("Style-dependent selectors require full measurement");
6784
+ if (rule.style) declarations(rule.style, rule.selectorText);
6785
+ if (rule.styleSheet) visit(rule.styleSheet.cssRules);
6786
+ else if (rule.cssRules) visit(rule.cssRules);
6787
+ }
6788
+ };
6789
+ variableSheets = [...document.styleSheets, ...document.adoptedStyleSheets ?? []];
6790
+ for (const sheet of variableSheets) visit(sheet.cssRules);
6791
+ for (let node = root; node; node = node.parentElement) declarations(node.style, node === root ? null : void 0);
6792
+ return uses;
6793
+ };
6794
+ const affectsRendering = (records) => {
6795
+ const changed = /* @__PURE__ */ new Set();
6796
+ for (const record of records) {
6797
+ if (["data-ad-paused", "data-ad-offscreen"].includes(record.attributeName)) continue;
6798
+ if (record.attributeName !== "style") return true;
6799
+ const old = document.createElement("div").style;
6800
+ old.cssText = record.oldValue ?? "";
6801
+ const current = record.target.style;
6802
+ for (const property of /* @__PURE__ */ new Set([...old, ...current])) {
6803
+ if (old.getPropertyValue(property) === current.getPropertyValue(property) && old.getPropertyPriority(property) === current.getPropertyPriority(property)) continue;
6804
+ if (["opacity", "transform", "translate", "rotate", "scale", "z-index"].includes(property)) continue;
6805
+ if (!property.startsWith("--")) return true;
6806
+ changed.add(property);
6807
+ if (current.getPropertyValue(property).includes("var(")) variableUses = void 0;
6808
+ }
6809
+ }
6810
+ if (!changed.size) return false;
6811
+ try {
6812
+ const sheets = [...document.styleSheets, ...document.adoptedStyleSheets ?? []];
6813
+ if (sheets.length !== variableSheets.length || sheets.some((sheet, i) => sheet !== variableSheets[i])) variableUses = void 0;
6814
+ variableUses ??= readVariableUses();
6815
+ for (const variable of changed) for (const use of variableUses.get(variable) ?? []) {
6816
+ if (use.property.startsWith("--")) {
6817
+ changed.add(use.property);
6818
+ continue;
6819
+ }
6820
+ if (use.property === "box-shadow" && state.beat === 0 && (use.selector === null || use.selector && !use.selector.includes(",") && root.matches(use.selector) && !root.querySelector(use.selector))) {
6821
+ nativeShadowChanged = true;
6822
+ continue;
6823
+ }
6824
+ return true;
6825
+ }
6826
+ return false;
6827
+ } catch {
6828
+ return true;
6829
+ }
6830
+ };
6766
6831
  const tokens = new MutationObserver((records) => {
6767
6832
  visibility();
6768
- if (records.some((record) => !["data-ad-paused", "data-ad-offscreen"].includes(record.attributeName))) configure();
6833
+ if (affectsRendering(records)) configure();
6769
6834
  });
6835
+ const stylesheets = new MutationObserver((records) => {
6836
+ variableUses = void 0;
6837
+ if (records.some((record) => record.target.nodeName === "STYLE" || record.target.parentElement?.nodeName === "STYLE" || [...record.addedNodes, ...record.removedNodes].some((node) => node.nodeType === 1 && node.matches('style,link[rel="stylesheet"]')))) configure();
6838
+ });
6839
+ if (document.head) stylesheets.observe(document.head, { childList: true, subtree: true, characterData: true });
6770
6840
  for (let node = root; node; node = node.parentElement)
6771
- tokens.observe(node, { attributes: true, attributeFilter: ["style", "class", "data-ad-theme", "data-ad-color-scheme", "data-ad-motion", "data-ad-paused", "data-ad-offscreen"] });
6841
+ tokens.observe(node, { attributes: true, attributeOldValue: true, attributeFilter: ["style", "class", "data-ad-theme", "data-ad-color-scheme", "data-ad-motion", "data-ad-paused", "data-ad-offscreen"] });
6772
6842
  resize.observe(root);
6773
6843
  resize.observe(lane);
6774
6844
  intersection.observe(root);
@@ -6866,6 +6936,7 @@ function createMelodyGlow(root, initialState, onReady) {
6866
6936
  };
6867
6937
  function configure() {
6868
6938
  if (disposed || !root.clientWidth || !root.clientHeight) return;
6939
+ variableUses = void 0;
6869
6940
  const temporary = [];
6870
6941
  try {
6871
6942
  const probe = document.createElement("div");
@@ -7074,6 +7145,7 @@ function createMelodyGlow(root, initialState, onReady) {
7074
7145
  resize.disconnect();
7075
7146
  intersection.disconnect();
7076
7147
  tokens.disconnect();
7148
+ stylesheets.disconnect();
7077
7149
  keyObserver.disconnect();
7078
7150
  keyboard.removeEventListener("pointerover", keysChanged);
7079
7151
  keyboard.removeEventListener("pointerout", keysChanged);
@@ -7104,6 +7176,10 @@ function createMelodyGlow(root, initialState, onReady) {
7104
7176
  if (next.notes === state.notes) delete sent.notes;
7105
7177
  if (next.hits === state.hits) delete sent.hits;
7106
7178
  state = next;
7179
+ if (nativeShadowChanged && next.beat > 0) {
7180
+ nativeShadowChanged = false;
7181
+ configure();
7182
+ }
7107
7183
  worker.postMessage({ type: "state", id, state: sent, time: performance.timeOrigin + performance.now() });
7108
7184
  }
7109
7185
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo-ui-kit/react",
3
- "version": "1.3.10",
3
+ "version": "1.3.12",
4
4
  "description": "NEO UI — React component library",
5
5
  "type": "module",
6
6
  "license": "MIT",