@neo-ui-kit/react 1.3.13 → 1.3.15
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 +15 -1
- package/dist/chunks/{chunk-OCH27SNY.js → chunk-6VPAU3TV.js} +213 -20
- package/dist/chunks/{chunk-L6VCC7AS.js → chunk-TMCWA446.js} +1 -1
- package/dist/components/media/LevelMeter/styles.css +10 -0
- package/dist/core.js +1 -1
- package/dist/forms.js +2 -2
- package/dist/index.js +308 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,20 @@ 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.15
|
|
8
|
+
|
|
9
|
+
Animated borders use a shared WebGPU pipeline when available, combining the existing masks and glow textures in one pass. Rendering is limited to the moving lights. Canvas 2D and SVG remain fallback paths; speeds, colors and effects are retained. See the performance report for laptop measurements and pixel-comparison limits.
|
|
10
|
+
|
|
11
|
+
## 1.3.14
|
|
12
|
+
|
|
13
|
+
Animated borders composite their moving lights in bounded buffers instead of repainting full-size intermediate surfaces. The same masks, glow textures, speeds and overlapping-light composition are preserved.
|
|
14
|
+
|
|
15
|
+
LevelMeter computes each sample's SVG coordinates once, writes only the final contour after a delayed frame, and skips identical contours. Sampling, attack/release, SVG geometry, gradients and glow remain unchanged. CPU ×8/×16 measurements and limitations are recorded in the performance report; this does not establish a universal 60 FPS guarantee.
|
|
16
|
+
|
|
17
|
+
The unchanged SVG subtree is reused across signal updates. A committed value reaches the renderer before the next paint, including when waking from silence; accessible values still update with the signal.
|
|
18
|
+
|
|
19
|
+
LevelMeter renders its waveform in a shared worker when supported, retaining the SVG fallback and existing glow. Its accessible values remain on the main thread.
|
|
20
|
+
|
|
7
21
|
## 1.3.13
|
|
8
22
|
|
|
9
23
|
MelodyRoll skips repainting empty glow/voice/trail layers and a settled keyboard. A disappearing voice still clears its last frame, and keyboard hover, press and hit transitions retain their final frame. See the [performance report](https://github.com/Pro100Dimka/adui/blob/main/evidence/performance-report.md) for karaoke measurements and remaining limits under CPU ×16.
|
|
@@ -51,7 +65,7 @@ import "@neo-ui-kit/react/styles.css";
|
|
|
51
65
|
To test repository changes before publishing, run `npm run package` from the repository root and install the resulting archive in a separate application:
|
|
52
66
|
|
|
53
67
|
```bash
|
|
54
|
-
npm install /path/to/adui/release/neo-ui-kit-react-1.3.
|
|
68
|
+
npm install /path/to/adui/release/neo-ui-kit-react-1.3.15.tgz
|
|
55
69
|
```
|
|
56
70
|
|
|
57
71
|
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-пакет).
|
|
@@ -11,32 +11,207 @@ import {
|
|
|
11
11
|
|
|
12
12
|
// src/core/border-worker.js
|
|
13
13
|
function renderBorders() {
|
|
14
|
+
async function createGpuPainter() {
|
|
15
|
+
if (!navigator.gpu) return null;
|
|
16
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
17
|
+
if (!adapter) return null;
|
|
18
|
+
const device = await adapter.requestDevice();
|
|
19
|
+
const format = navigator.gpu.getPreferredCanvasFormat();
|
|
20
|
+
const shader = device.createShaderModule({ code: `
|
|
21
|
+
struct Uniforms { size: vec4f, canvas: vec4f, lights: vec4f, padding: vec4f };
|
|
22
|
+
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
23
|
+
@group(0) @binding(1) var linearSampler: sampler;
|
|
24
|
+
@group(0) @binding(2) var mask0: texture_2d<f32>;
|
|
25
|
+
@group(0) @binding(3) var glow0: texture_2d<f32>;
|
|
26
|
+
@group(0) @binding(4) var mask1: texture_2d<f32>;
|
|
27
|
+
@group(0) @binding(5) var glow1: texture_2d<f32>;
|
|
28
|
+
@vertex fn vertex(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
|
|
29
|
+
let positions = array<vec2f, 3>(vec2f(-1,-1), vec2f(3,-1), vec2f(-1,3));
|
|
30
|
+
return vec4f(positions[i], 0, 1);
|
|
31
|
+
}
|
|
32
|
+
fn spot(p: vec2f, origin: vec2f, texture: texture_2d<f32>) -> vec4f {
|
|
33
|
+
let uv = (p - origin) / (u.size.w * 2);
|
|
34
|
+
if (any(uv < vec2f(0)) || any(uv > vec2f(1))) { return vec4f(0); }
|
|
35
|
+
return textureSampleLevel(texture, linearSampler, uv, 0);
|
|
36
|
+
}
|
|
37
|
+
fn layer(p: vec2f, mask: texture_2d<f32>, glow: texture_2d<f32>) -> vec4f {
|
|
38
|
+
let alpha = textureSampleLevel(mask, linearSampler, p / u.size.xy, 0).a;
|
|
39
|
+
if (alpha == 0) { return vec4f(0); }
|
|
40
|
+
let first = spot(p, u.lights.xy, glow);
|
|
41
|
+
let second = spot(p, u.lights.zw, glow);
|
|
42
|
+
return (second + first * (1 - second.a)) * alpha;
|
|
43
|
+
}
|
|
44
|
+
fn filtered(q: vec2f, mask: texture_2d<f32>, glow: texture_2d<f32>) -> vec4f {
|
|
45
|
+
let base = floor(q - vec2f(.5)) + vec2f(.5);
|
|
46
|
+
let f = fract(q - vec2f(.5));
|
|
47
|
+
let a = layer(base / u.size.z, mask, glow);
|
|
48
|
+
let b = layer((base + vec2f(1,0)) / u.size.z, mask, glow);
|
|
49
|
+
let c = layer((base + vec2f(0,1)) / u.size.z, mask, glow);
|
|
50
|
+
let d = layer((base + vec2f(1,1)) / u.size.z, mask, glow);
|
|
51
|
+
return mix(mix(a,b,f.x),mix(c,d,f.x),f.y);
|
|
52
|
+
}
|
|
53
|
+
@fragment fn fragment(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
54
|
+
let q = p.xy * u.canvas.xy / (u.size.xy * u.size.z);
|
|
55
|
+
let aura = filtered(q, mask0, glow0) * u.canvas.z;
|
|
56
|
+
let core = filtered(q, mask1, glow1) * u.canvas.w;
|
|
57
|
+
return core + aura * (1-core.a);
|
|
58
|
+
}
|
|
59
|
+
` });
|
|
60
|
+
let pipeline;
|
|
61
|
+
try {
|
|
62
|
+
pipeline = await device.createRenderPipelineAsync({
|
|
63
|
+
layout: "auto",
|
|
64
|
+
vertex: { module: shader, entryPoint: "vertex" },
|
|
65
|
+
fragment: { module: shader, entryPoint: "fragment", targets: [{ format }] },
|
|
66
|
+
primitive: { topology: "triangle-list" }
|
|
67
|
+
});
|
|
68
|
+
} catch (error) {
|
|
69
|
+
device.destroy();
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
const sampler = device.createSampler({ magFilter: "linear", minFilter: "linear" });
|
|
73
|
+
device.addEventListener("uncapturederror", (event) => {
|
|
74
|
+
throw event.error;
|
|
75
|
+
});
|
|
76
|
+
device.lost.then((info) => {
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
throw new Error(info.message || "Border GPU device lost");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
return (scene) => {
|
|
82
|
+
const context = scene.canvas.getContext("webgpu");
|
|
83
|
+
context.configure({ device, format, alphaMode: "premultiplied" });
|
|
84
|
+
const textures = scene.layers.flatMap((layer) => [layer.mask, layer.texture]).map((image) => {
|
|
85
|
+
const texture = device.createTexture({
|
|
86
|
+
size: [image.width, image.height],
|
|
87
|
+
format: "rgba8unorm",
|
|
88
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
|
|
89
|
+
});
|
|
90
|
+
device.queue.copyExternalImageToTexture({ source: image }, { texture, premultipliedAlpha: true }, [image.width, image.height]);
|
|
91
|
+
return texture;
|
|
92
|
+
});
|
|
93
|
+
const buffer = device.createBuffer({ size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
94
|
+
const values = new Float32Array(16);
|
|
95
|
+
values.set([
|
|
96
|
+
scene.width,
|
|
97
|
+
scene.height,
|
|
98
|
+
scene.dpr,
|
|
99
|
+
scene.radius,
|
|
100
|
+
scene.canvas.width,
|
|
101
|
+
scene.canvas.height,
|
|
102
|
+
scene.layers[0].opacity,
|
|
103
|
+
scene.layers[1].opacity
|
|
104
|
+
]);
|
|
105
|
+
const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [
|
|
106
|
+
{ binding: 0, resource: { buffer } },
|
|
107
|
+
{ binding: 1, resource: sampler },
|
|
108
|
+
...textures.map((texture, index) => ({ binding: index + 2, resource: texture.createView() }))
|
|
109
|
+
] });
|
|
110
|
+
return {
|
|
111
|
+
draw(current) {
|
|
112
|
+
current.lights.forEach((light, index) => {
|
|
113
|
+
const at = (light.phase + current.elapsed * light.speed / current.length) % 1 * 1024;
|
|
114
|
+
const lo = Math.floor(at), hi = (lo + 1) % 1024, weight = at - lo;
|
|
115
|
+
for (let axis = 0; axis < 2; axis++) values[8 + index * 2 + axis] = current.points[lo * 2 + axis] * (1 - weight) + current.points[hi * 2 + axis] * weight - current.radius + 16;
|
|
116
|
+
});
|
|
117
|
+
device.queue.writeBuffer(buffer, 0, values);
|
|
118
|
+
const encoder = device.createCommandEncoder();
|
|
119
|
+
const pass = encoder.beginRenderPass({ colorAttachments: [{
|
|
120
|
+
view: context.getCurrentTexture().createView(),
|
|
121
|
+
loadOp: "clear",
|
|
122
|
+
storeOp: "store",
|
|
123
|
+
clearValue: [0, 0, 0, 0]
|
|
124
|
+
}] });
|
|
125
|
+
pass.setPipeline(pipeline);
|
|
126
|
+
pass.setBindGroup(0, bindGroup);
|
|
127
|
+
for (let i = 0; i < 2; i++) {
|
|
128
|
+
const sx = current.width * current.dpr / current.canvas.width, sy = current.height * current.dpr / current.canvas.height;
|
|
129
|
+
const x = Math.max(0, Math.floor((values[8 + i * 2] * current.dpr - 2) * sx));
|
|
130
|
+
const y = Math.max(0, Math.floor((values[9 + i * 2] * current.dpr - 2) * sy));
|
|
131
|
+
const r = Math.min(current.canvas.width, Math.ceil(((values[8 + i * 2] + current.radius * 2) * current.dpr + 2) * sx));
|
|
132
|
+
const b = Math.min(current.canvas.height, Math.ceil(((values[9 + i * 2] + current.radius * 2) * current.dpr + 2) * sy));
|
|
133
|
+
if (r > x && b > y) {
|
|
134
|
+
pass.setScissorRect(x, y, r - x, b - y);
|
|
135
|
+
pass.draw(3);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
pass.end();
|
|
139
|
+
device.queue.submit([encoder.finish()]);
|
|
140
|
+
},
|
|
141
|
+
dispose() {
|
|
142
|
+
context.unconfigure();
|
|
143
|
+
buffer.destroy();
|
|
144
|
+
textures.forEach((texture) => texture.destroy());
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
let gpu;
|
|
150
|
+
const pending = [];
|
|
151
|
+
onmessage = (event) => pending.push(event);
|
|
14
152
|
const scenes = /* @__PURE__ */ new Map();
|
|
15
153
|
let frame = null, fps = 60, last2 = -Infinity;
|
|
16
154
|
const release = (scene) => {
|
|
155
|
+
scene.gpu?.dispose();
|
|
17
156
|
for (const layer of scene.layers) {
|
|
18
157
|
layer.mask.close();
|
|
19
158
|
layer.texture.close();
|
|
20
159
|
}
|
|
21
160
|
};
|
|
22
161
|
const draw = (scene) => {
|
|
23
|
-
|
|
162
|
+
if (scene.gpu) {
|
|
163
|
+
scene.gpu.draw(scene);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const { ctx, width, height, points, length, radius, dpr } = scene;
|
|
167
|
+
const lights = scene.lights.map((light) => {
|
|
168
|
+
const index = (light.phase + scene.elapsed * light.speed / length) % 1 * 1024;
|
|
169
|
+
const lo = Math.floor(index), hi = (lo + 1) % 1024, weight = index - lo;
|
|
170
|
+
return {
|
|
171
|
+
x: points[lo * 2] * (1 - weight) + points[hi * 2] * weight - radius + 16,
|
|
172
|
+
y: points[lo * 2 + 1] * (1 - weight) + points[hi * 2 + 1] * weight - radius + 16
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
const regions = lights.map(({ x, y }) => ({
|
|
176
|
+
x: Math.max(0, Math.floor(x * dpr - 1) / dpr),
|
|
177
|
+
y: Math.max(0, Math.floor(y * dpr - 1) / dpr),
|
|
178
|
+
right: Math.min(scene.canvas.width, Math.ceil((x + radius * 2) * dpr + 1)) / dpr,
|
|
179
|
+
bottom: Math.min(scene.canvas.height, Math.ceil((y + radius * 2) * dpr + 1)) / dpr
|
|
180
|
+
}));
|
|
181
|
+
const [first, second] = regions;
|
|
182
|
+
if (second && first.x < second.right && second.x < first.right && first.y < second.bottom && second.y < first.bottom) {
|
|
183
|
+
regions.splice(0, 2, {
|
|
184
|
+
x: Math.min(first.x, second.x),
|
|
185
|
+
y: Math.min(first.y, second.y),
|
|
186
|
+
right: Math.max(first.right, second.right),
|
|
187
|
+
bottom: Math.max(first.bottom, second.bottom)
|
|
188
|
+
});
|
|
189
|
+
}
|
|
24
190
|
ctx.clearRect(0, 0, width, height);
|
|
25
191
|
for (const layer of scene.layers) {
|
|
26
192
|
const paint = layer.ctx;
|
|
27
|
-
paint.globalCompositeOperation = "source-over";
|
|
28
|
-
paint.clearRect(0, 0, width, height);
|
|
29
|
-
for (const light of scene.lights) {
|
|
30
|
-
const index = (light.phase + scene.elapsed * light.speed / length) % 1 * 1024;
|
|
31
|
-
const lo = Math.floor(index), hi = (lo + 1) % 1024, weight = index - lo;
|
|
32
|
-
const x = points[lo * 2] * (1 - weight) + points[hi * 2] * weight;
|
|
33
|
-
const y = points[lo * 2 + 1] * (1 - weight) + points[hi * 2 + 1] * weight;
|
|
34
|
-
paint.drawImage(layer.texture, x - radius + 16, y - radius + 16, radius * 2, radius * 2);
|
|
35
|
-
}
|
|
36
|
-
paint.globalCompositeOperation = "destination-in";
|
|
37
|
-
paint.drawImage(layer.mask, 0, 0, width, height);
|
|
38
193
|
ctx.globalAlpha = layer.opacity;
|
|
39
|
-
|
|
194
|
+
for (const { x, y, right, bottom } of regions) {
|
|
195
|
+
const w = right - x, h = bottom - y;
|
|
196
|
+
paint.globalCompositeOperation = "source-over";
|
|
197
|
+
paint.clearRect(0, 0, layer.buffer.width / dpr, layer.buffer.height / dpr);
|
|
198
|
+
for (const light of lights)
|
|
199
|
+
paint.drawImage(layer.texture, light.x - x, light.y - y, radius * 2, radius * 2);
|
|
200
|
+
paint.globalCompositeOperation = "destination-in";
|
|
201
|
+
paint.drawImage(
|
|
202
|
+
layer.mask,
|
|
203
|
+
x * layer.mask.width / width,
|
|
204
|
+
y * layer.mask.height / height,
|
|
205
|
+
w * layer.mask.width / width,
|
|
206
|
+
h * layer.mask.height / height,
|
|
207
|
+
0,
|
|
208
|
+
0,
|
|
209
|
+
w,
|
|
210
|
+
h
|
|
211
|
+
);
|
|
212
|
+
const scaleX = width * dpr / scene.canvas.width, scaleY = height * dpr / scene.canvas.height;
|
|
213
|
+
ctx.drawImage(layer.buffer, 0, 0, w * dpr, h * dpr, x * scaleX, y * scaleY, w * scaleX, h * scaleY);
|
|
214
|
+
}
|
|
40
215
|
}
|
|
41
216
|
ctx.globalAlpha = 1;
|
|
42
217
|
};
|
|
@@ -57,7 +232,7 @@ function renderBorders() {
|
|
|
57
232
|
}
|
|
58
233
|
schedule2();
|
|
59
234
|
}
|
|
60
|
-
|
|
235
|
+
const receive = ({ data }) => {
|
|
61
236
|
const scene = scenes.get(data.id);
|
|
62
237
|
if (data.type === "rate") fps = data.fps;
|
|
63
238
|
else if (data.type === "remove") {
|
|
@@ -73,12 +248,18 @@ function renderBorders() {
|
|
|
73
248
|
const next = { ...data, canvas: scene?.canvas ?? data.canvas, elapsed: scene?.elapsed ?? data.elapsed, previous: null };
|
|
74
249
|
next.canvas.width = Math.ceil(data.width * data.dpr);
|
|
75
250
|
next.canvas.height = Math.ceil(data.height * data.dpr);
|
|
76
|
-
next.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
layer
|
|
81
|
-
|
|
251
|
+
if (gpu) next.gpu = gpu(next);
|
|
252
|
+
else {
|
|
253
|
+
next.ctx = next.canvas.getContext("2d");
|
|
254
|
+
next.ctx.setTransform(data.dpr, 0, 0, data.dpr, 0, 0);
|
|
255
|
+
for (const layer of next.layers) {
|
|
256
|
+
layer.buffer = new OffscreenCanvas(
|
|
257
|
+
Math.min(next.canvas.width, Math.ceil(next.radius * 4 * data.dpr) + 4),
|
|
258
|
+
Math.min(next.canvas.height, Math.ceil(next.radius * 4 * data.dpr) + 4)
|
|
259
|
+
);
|
|
260
|
+
layer.ctx = layer.buffer.getContext("2d");
|
|
261
|
+
layer.ctx.setTransform(data.dpr, 0, 0, data.dpr, 0, 0);
|
|
262
|
+
}
|
|
82
263
|
}
|
|
83
264
|
scenes.set(data.id, next);
|
|
84
265
|
draw(next);
|
|
@@ -86,6 +267,18 @@ function renderBorders() {
|
|
|
86
267
|
}
|
|
87
268
|
schedule2();
|
|
88
269
|
};
|
|
270
|
+
createGpuPainter().catch((error) => {
|
|
271
|
+
console.warn("Border GPU initialization failed; using Canvas 2D", error);
|
|
272
|
+
return null;
|
|
273
|
+
}).then((value) => {
|
|
274
|
+
gpu = value;
|
|
275
|
+
onmessage = receive;
|
|
276
|
+
pending.splice(0).forEach(receive);
|
|
277
|
+
}).catch((error) => {
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
throw error;
|
|
280
|
+
});
|
|
281
|
+
});
|
|
89
282
|
}
|
|
90
283
|
var worker = null;
|
|
91
284
|
var workerUrl = null;
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
0 0 0.22rem color-mix(in srgb, var(--ad-red) 78%, transparent)
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
|
+
.ad-level-meter-canvas {
|
|
17
|
+
position: absolute;
|
|
18
|
+
inset: 0;
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 100%;
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
}
|
|
23
|
+
.ad-level-meter[data-ad-canvas] > svg {
|
|
24
|
+
visibility: hidden;
|
|
25
|
+
}
|
|
16
26
|
.ad-level-meter-axis {
|
|
17
27
|
stroke: color-mix(in srgb, var(--ad-red) 24%, transparent);
|
|
18
28
|
stroke-width: 0.7;
|
package/dist/core.js
CHANGED
package/dist/forms.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
defaultFieldRegistry,
|
|
5
5
|
useForm,
|
|
6
6
|
useFormContext
|
|
7
|
-
} from "./chunks/chunk-
|
|
8
|
-
import "./chunks/chunk-
|
|
7
|
+
} from "./chunks/chunk-TMCWA446.js";
|
|
8
|
+
import "./chunks/chunk-6VPAU3TV.js";
|
|
9
9
|
import "./chunks/chunk-BJSFN52W.js";
|
|
10
10
|
import "./chunks/chunk-FCOU3ODR.js";
|
|
11
11
|
export {
|
package/dist/index.js
CHANGED
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
useSvgId,
|
|
63
63
|
variantMaterial,
|
|
64
64
|
vectorElement
|
|
65
|
-
} from "./chunks/chunk-
|
|
65
|
+
} from "./chunks/chunk-TMCWA446.js";
|
|
66
66
|
import {
|
|
67
67
|
ThemeProvider,
|
|
68
68
|
createNeonRenderer,
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
useTabShape,
|
|
80
80
|
useThemePalette,
|
|
81
81
|
useTick
|
|
82
|
-
} from "./chunks/chunk-
|
|
82
|
+
} from "./chunks/chunk-6VPAU3TV.js";
|
|
83
83
|
import {
|
|
84
84
|
LocaleProvider,
|
|
85
85
|
addMessages,
|
|
@@ -5968,20 +5968,262 @@ function PlaybackTrack({ audio, playing, position: controlledPosition, disabled,
|
|
|
5968
5968
|
}
|
|
5969
5969
|
|
|
5970
5970
|
// src/components/media/LevelMeter/LevelMeter.tsx
|
|
5971
|
-
import { useEffect as useEffect13, useRef as useRef27 } from "react";
|
|
5971
|
+
import { useEffect as useEffect13, useLayoutEffect as useLayoutEffect5, useMemo as useMemo5, useRef as useRef27 } from "react";
|
|
5972
5972
|
import { jsx as jsx48, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
5973
5973
|
var SAMPLES = 64;
|
|
5974
5974
|
var WIDTH = 256;
|
|
5975
5975
|
var HEIGHT = 24;
|
|
5976
5976
|
var MID2 = HEIGHT / 2;
|
|
5977
5977
|
var STEP = WIDTH / (SAMPLES - 1);
|
|
5978
|
+
var X = Array.from({ length: SAMPLES + 1 }, (_, i) => (i * STEP).toFixed(2));
|
|
5978
5979
|
var SAMPLE_MS = 28;
|
|
5980
|
+
var waveSample = (level) => {
|
|
5981
|
+
const height = 1 + level ** 0.68 * (MID2 - 3);
|
|
5982
|
+
return { level, upper: (MID2 - height).toFixed(2), lower: (MID2 + height).toFixed(2) };
|
|
5983
|
+
};
|
|
5979
5984
|
var wavePath2 = (samples) => {
|
|
5980
|
-
const
|
|
5981
|
-
const
|
|
5982
|
-
const lower = samples.map((level, i) => point(i, level, 1)).reverse();
|
|
5985
|
+
const upper = samples.map((sample, i) => `${X[i]} ${sample.upper}`);
|
|
5986
|
+
const lower = samples.map((sample, i) => `${X[i]} ${sample.lower}`).reverse();
|
|
5983
5987
|
return `M${upper.join("L")}L${lower.join("L")}Z`;
|
|
5984
5988
|
};
|
|
5989
|
+
function renderMeters() {
|
|
5990
|
+
const scenes = /* @__PURE__ */ new Map();
|
|
5991
|
+
onmessage = ({ data }) => {
|
|
5992
|
+
if (data.remove !== void 0) {
|
|
5993
|
+
scenes.delete(data.remove);
|
|
5994
|
+
return;
|
|
5995
|
+
}
|
|
5996
|
+
const completed = [], failed = [];
|
|
5997
|
+
for (const frame of data.frames ?? []) {
|
|
5998
|
+
try {
|
|
5999
|
+
let scene = scenes.get(frame.id);
|
|
6000
|
+
if (frame.canvas) {
|
|
6001
|
+
const ctx2 = frame.canvas.getContext("2d");
|
|
6002
|
+
if (!ctx2 || !frame.style) throw new Error("Canvas unavailable");
|
|
6003
|
+
scene = { canvas: frame.canvas, ctx: ctx2, style: frame.style, fill: "", path: new Path2D(), revision: frame.revision };
|
|
6004
|
+
scenes.set(frame.id, scene);
|
|
6005
|
+
frame.canvas.addEventListener("contextlost", () => {
|
|
6006
|
+
scenes.delete(frame.id);
|
|
6007
|
+
postMessage({ failed: [frame.id] });
|
|
6008
|
+
});
|
|
6009
|
+
}
|
|
6010
|
+
if (!scene) continue;
|
|
6011
|
+
const { canvas, ctx } = scene;
|
|
6012
|
+
if (frame.style) {
|
|
6013
|
+
scene.style = frame.style;
|
|
6014
|
+
scene.revision = frame.revision;
|
|
6015
|
+
canvas.width = frame.style.width;
|
|
6016
|
+
canvas.height = frame.style.height;
|
|
6017
|
+
scene.fill = frame.style.fill;
|
|
6018
|
+
if (frame.style.stops.length) {
|
|
6019
|
+
const gradient = ctx.createLinearGradient(0, 0, 260.06, 0);
|
|
6020
|
+
for (const stop of frame.style.stops) gradient.addColorStop(stop.at, stop.color);
|
|
6021
|
+
scene.fill = gradient;
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
6024
|
+
if (frame.d !== void 0) scene.path = new Path2D(frame.d);
|
|
6025
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
6026
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
6027
|
+
ctx.scale(canvas.width / 256, canvas.height / 24);
|
|
6028
|
+
ctx.globalAlpha = scene.style.lineAlpha;
|
|
6029
|
+
ctx.strokeStyle = scene.style.stroke;
|
|
6030
|
+
ctx.lineWidth = scene.style.lineWidth;
|
|
6031
|
+
ctx.beginPath();
|
|
6032
|
+
ctx.moveTo(0, 12);
|
|
6033
|
+
ctx.lineTo(256, 12);
|
|
6034
|
+
ctx.stroke();
|
|
6035
|
+
ctx.translate(frame.offset, 0);
|
|
6036
|
+
ctx.globalAlpha = scene.style.fillAlpha;
|
|
6037
|
+
ctx.fillStyle = scene.fill;
|
|
6038
|
+
ctx.fill(scene.path);
|
|
6039
|
+
completed.push({ id: frame.id, revision: scene.revision });
|
|
6040
|
+
} catch {
|
|
6041
|
+
scenes.delete(frame.id);
|
|
6042
|
+
failed.push(frame.id);
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
postMessage({ ack: true, completed, failed });
|
|
6046
|
+
};
|
|
6047
|
+
}
|
|
6048
|
+
var meterWorker;
|
|
6049
|
+
var meterUrl;
|
|
6050
|
+
var meterSerial = 0;
|
|
6051
|
+
var meterBusy = false;
|
|
6052
|
+
var meterQueued = false;
|
|
6053
|
+
var meterClients = /* @__PURE__ */ new Map();
|
|
6054
|
+
var meterFrames = /* @__PURE__ */ new Map();
|
|
6055
|
+
function stopMeters() {
|
|
6056
|
+
meterWorker?.terminate();
|
|
6057
|
+
if (meterUrl) URL.revokeObjectURL(meterUrl);
|
|
6058
|
+
meterWorker = void 0;
|
|
6059
|
+
meterUrl = void 0;
|
|
6060
|
+
meterBusy = false;
|
|
6061
|
+
meterFrames.clear();
|
|
6062
|
+
}
|
|
6063
|
+
function failMeters() {
|
|
6064
|
+
for (const client of [...meterClients.values()]) client.dispose();
|
|
6065
|
+
stopMeters();
|
|
6066
|
+
}
|
|
6067
|
+
function sendMeter(frame) {
|
|
6068
|
+
if (frame) {
|
|
6069
|
+
const pending = meterFrames.get(frame.id);
|
|
6070
|
+
if (pending) Object.assign(pending, frame);
|
|
6071
|
+
else meterFrames.set(frame.id, frame);
|
|
6072
|
+
}
|
|
6073
|
+
if (meterQueued || meterBusy || !meterWorker || !meterFrames.size) return;
|
|
6074
|
+
meterQueued = true;
|
|
6075
|
+
queueMicrotask(() => {
|
|
6076
|
+
meterQueued = false;
|
|
6077
|
+
if (!meterWorker || meterBusy || !meterFrames.size) return;
|
|
6078
|
+
const frames = [...meterFrames.values()];
|
|
6079
|
+
meterFrames.clear();
|
|
6080
|
+
meterBusy = true;
|
|
6081
|
+
try {
|
|
6082
|
+
meterWorker.postMessage({ frames }, frames.flatMap((frame2) => frame2.canvas ? [frame2.canvas] : []));
|
|
6083
|
+
} catch {
|
|
6084
|
+
failMeters();
|
|
6085
|
+
}
|
|
6086
|
+
});
|
|
6087
|
+
}
|
|
6088
|
+
function startMeters() {
|
|
6089
|
+
if (meterWorker) return meterWorker;
|
|
6090
|
+
try {
|
|
6091
|
+
meterUrl = URL.createObjectURL(new Blob([`(${renderMeters.toString()})()`], { type: "text/javascript" }));
|
|
6092
|
+
const worker = new Worker(meterUrl);
|
|
6093
|
+
meterWorker = worker;
|
|
6094
|
+
worker.onerror = () => {
|
|
6095
|
+
if (meterWorker === worker) failMeters();
|
|
6096
|
+
};
|
|
6097
|
+
worker.onmessage = ({ data }) => {
|
|
6098
|
+
if (meterWorker !== worker) return;
|
|
6099
|
+
if (data.ack) meterBusy = false;
|
|
6100
|
+
for (const id of data.failed ?? []) meterClients.get(id)?.dispose();
|
|
6101
|
+
for (const item of data.completed ?? []) meterClients.get(item.id)?.ready(item.revision);
|
|
6102
|
+
sendMeter();
|
|
6103
|
+
};
|
|
6104
|
+
return worker;
|
|
6105
|
+
} catch {
|
|
6106
|
+
stopMeters();
|
|
6107
|
+
return void 0;
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
function waveCanvas(shape, initial) {
|
|
6111
|
+
if (typeof Worker === "undefined" || typeof OffscreenCanvas === "undefined" || typeof Path2D === "undefined" || typeof MutationObserver === "undefined" || typeof HTMLCanvasElement === "undefined" || !HTMLCanvasElement.prototype.transferControlToOffscreen) return null;
|
|
6112
|
+
const svg = shape.ownerSVGElement, root = svg?.parentElement;
|
|
6113
|
+
const axis = svg?.querySelector("line");
|
|
6114
|
+
if (!svg || !root || !axis) return null;
|
|
6115
|
+
const canvas = document.createElement("canvas");
|
|
6116
|
+
const worker = startMeters();
|
|
6117
|
+
if (!worker) return null;
|
|
6118
|
+
const id = ++meterSerial;
|
|
6119
|
+
canvas.className = "ad-level-meter-canvas";
|
|
6120
|
+
canvas.setAttribute("aria-hidden", "true");
|
|
6121
|
+
root.append(canvas);
|
|
6122
|
+
const ancestors = [];
|
|
6123
|
+
for (let node = root; node; node = node.parentElement) ancestors.push(node);
|
|
6124
|
+
let disposed = false, enabled = false, queued = false, ready = false, transferred = false, revision = 0;
|
|
6125
|
+
let current = initial, offset = 0, sent = "";
|
|
6126
|
+
const native = () => {
|
|
6127
|
+
root.removeAttribute("data-ad-canvas");
|
|
6128
|
+
canvas.hidden = true;
|
|
6129
|
+
shape.setAttribute("d", current);
|
|
6130
|
+
shape.setAttribute("transform", `translate(${offset} 0)`);
|
|
6131
|
+
};
|
|
6132
|
+
const draw = (d, shift) => {
|
|
6133
|
+
current = d;
|
|
6134
|
+
offset = shift;
|
|
6135
|
+
if (!enabled || disposed) return false;
|
|
6136
|
+
sendMeter({ id, revision, offset, ...sent !== d ? { d } : {} });
|
|
6137
|
+
sent = d;
|
|
6138
|
+
return ready;
|
|
6139
|
+
};
|
|
6140
|
+
function update() {
|
|
6141
|
+
if (disposed) return;
|
|
6142
|
+
const surface = getComputedStyle(svg), wave = getComputedStyle(shape), line2 = getComputedStyle(axis);
|
|
6143
|
+
const host = getComputedStyle(root);
|
|
6144
|
+
const gradient = svg.querySelector("linearGradient");
|
|
6145
|
+
const width = parseFloat(surface.width), height = parseFloat(surface.height);
|
|
6146
|
+
const unsupported = [surface.transform, surface.backgroundImage, wave.stroke, wave.filter, wave.clipPath, wave.maskImage, line2.strokeDasharray].some((value) => value !== "none") || line2.strokeLinecap !== "butt" || wave.fill.startsWith("url(") && (!gradient || wave.fill.match(/#([^"')]+)["']?\)$/)?.[1] !== gradient.id || ["gradientUnits", "gradientTransform", "x1", "x2", "y1", "y2"].some((name) => gradient.hasAttribute(name))) || [surface, host].some((style) => ["Top", "Right", "Bottom", "Left"].some((side) => parseFloat(style.getPropertyValue(`padding-${side.toLowerCase()}`)) > 0 || parseFloat(style.getPropertyValue(`border-${side.toLowerCase()}-width`)) > 0)) || [svg, ...ancestors].some((node) => node.getAnimations?.({ subtree: node === svg }).some((animation) => animation.playState === "running"));
|
|
6147
|
+
enabled = width > 0 && height > 0 && !unsupported;
|
|
6148
|
+
if (!enabled) {
|
|
6149
|
+
native();
|
|
6150
|
+
return;
|
|
6151
|
+
}
|
|
6152
|
+
const ratio = devicePixelRatio || 1;
|
|
6153
|
+
canvas.style.filter = surface.filter;
|
|
6154
|
+
canvas.style.opacity = surface.opacity;
|
|
6155
|
+
canvas.style.backgroundColor = surface.backgroundColor;
|
|
6156
|
+
const stops = wave.fill.startsWith("url(") ? [...svg.querySelectorAll("stop")].map((stop) => {
|
|
6157
|
+
const style = getComputedStyle(stop);
|
|
6158
|
+
return { at: stop.offset.baseVal, color: `color-mix(in srgb, ${style.stopColor} ${Number(style.stopOpacity) * 100}%, transparent)` };
|
|
6159
|
+
}) : [];
|
|
6160
|
+
try {
|
|
6161
|
+
const surface2 = transferred ? void 0 : canvas.transferControlToOffscreen();
|
|
6162
|
+
transferred = true;
|
|
6163
|
+
ready = false;
|
|
6164
|
+
revision++;
|
|
6165
|
+
native();
|
|
6166
|
+
sent = current;
|
|
6167
|
+
sendMeter({ id, revision, d: current, offset, ...surface2 ? { canvas: surface2 } : {}, style: {
|
|
6168
|
+
width: Math.ceil(width * ratio),
|
|
6169
|
+
height: Math.ceil(height * ratio),
|
|
6170
|
+
stroke: line2.stroke,
|
|
6171
|
+
lineWidth: parseFloat(line2.strokeWidth),
|
|
6172
|
+
lineAlpha: Number(line2.opacity) * Number(line2.strokeOpacity),
|
|
6173
|
+
fillAlpha: Number(wave.opacity) * Number(wave.fillOpacity),
|
|
6174
|
+
fill: wave.fill,
|
|
6175
|
+
stops
|
|
6176
|
+
} });
|
|
6177
|
+
} catch {
|
|
6178
|
+
dispose();
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
function queue() {
|
|
6182
|
+
if (queued || disposed) return;
|
|
6183
|
+
queued = true;
|
|
6184
|
+
queueMicrotask(() => {
|
|
6185
|
+
queued = false;
|
|
6186
|
+
if (!disposed) update();
|
|
6187
|
+
});
|
|
6188
|
+
}
|
|
6189
|
+
const resized = createResizeObserver(queue);
|
|
6190
|
+
resized.observe(svg);
|
|
6191
|
+
const styles = new MutationObserver(queue);
|
|
6192
|
+
for (const node of ancestors) styles.observe(node, { attributes: true, attributeFilter: ["style", "class", "data-ad-theme", "data-ad-color-scheme"] });
|
|
6193
|
+
styles.observe(svg, { subtree: true, childList: true, attributes: true, attributeFilter: ["style", "class", "fill", "stroke", "stop-color", "stop-opacity", "offset"] });
|
|
6194
|
+
styles.observe(document.head, { subtree: true, childList: true, characterData: true });
|
|
6195
|
+
const transition = (event) => {
|
|
6196
|
+
if (event.target instanceof Element && (event.target.contains(svg) || svg.contains(event.target))) queue();
|
|
6197
|
+
};
|
|
6198
|
+
const events = ["transitionrun", "transitionend", "transitioncancel", "animationstart", "animationend", "animationcancel"];
|
|
6199
|
+
for (const event of events) document.addEventListener(event, transition);
|
|
6200
|
+
window.addEventListener("resize", queue);
|
|
6201
|
+
canvas.addEventListener("contextlost", dispose);
|
|
6202
|
+
function dispose() {
|
|
6203
|
+
if (disposed) return;
|
|
6204
|
+
disposed = true;
|
|
6205
|
+
enabled = false;
|
|
6206
|
+
resized.disconnect();
|
|
6207
|
+
styles.disconnect();
|
|
6208
|
+
window.removeEventListener("resize", queue);
|
|
6209
|
+
canvas.removeEventListener("contextlost", dispose);
|
|
6210
|
+
for (const event of events) document.removeEventListener(event, transition);
|
|
6211
|
+
meterFrames.delete(id);
|
|
6212
|
+
meterClients.delete(id);
|
|
6213
|
+
if (meterWorker === worker) worker?.postMessage({ remove: id });
|
|
6214
|
+
if (!meterClients.size) stopMeters();
|
|
6215
|
+
native();
|
|
6216
|
+
canvas.remove();
|
|
6217
|
+
}
|
|
6218
|
+
meterClients.set(id, { dispose, ready(next) {
|
|
6219
|
+
if (disposed || !enabled || next !== revision || ready) return;
|
|
6220
|
+
ready = true;
|
|
6221
|
+
canvas.hidden = false;
|
|
6222
|
+
root.setAttribute("data-ad-canvas", "");
|
|
6223
|
+
} });
|
|
6224
|
+
update();
|
|
6225
|
+
return { draw, dispose };
|
|
6226
|
+
}
|
|
5985
6227
|
var loudness = (analyser, buffer) => {
|
|
5986
6228
|
analyser.getFloatTimeDomainData(buffer);
|
|
5987
6229
|
let sum = 0;
|
|
@@ -6002,15 +6244,16 @@ function LevelMeter({
|
|
|
6002
6244
|
const target = useRef27(0);
|
|
6003
6245
|
const wake = useRef27(() => void 0);
|
|
6004
6246
|
const level = clamp(value) / 100;
|
|
6005
|
-
|
|
6247
|
+
useLayoutEffect5(() => {
|
|
6006
6248
|
target.current = active ? level : 0;
|
|
6007
6249
|
if (active && level > 0) wake.current();
|
|
6008
6250
|
}, [active, level]);
|
|
6009
6251
|
useEffect13(() => {
|
|
6010
6252
|
const shape = path.current;
|
|
6011
6253
|
if (!shape) return;
|
|
6012
|
-
const samples = Array.from({ length: SAMPLES + 1 }, () => 0);
|
|
6013
|
-
|
|
6254
|
+
const samples = Array.from({ length: SAMPLES + 1 }, () => waveSample(0));
|
|
6255
|
+
let drawnPath = wavePath2(samples);
|
|
6256
|
+
shape.setAttribute("d", drawnPath);
|
|
6014
6257
|
wake.current = () => void 0;
|
|
6015
6258
|
if (!active) return;
|
|
6016
6259
|
let context;
|
|
@@ -6024,6 +6267,8 @@ function LevelMeter({
|
|
|
6024
6267
|
source.connect(analyser);
|
|
6025
6268
|
}
|
|
6026
6269
|
const buffer = new Float32Array(analyser?.fftSize ?? 0);
|
|
6270
|
+
const painter = waveCanvas(shape, drawnPath);
|
|
6271
|
+
let nativePath = drawnPath;
|
|
6027
6272
|
let envelope = 0;
|
|
6028
6273
|
let carry = 0;
|
|
6029
6274
|
let last = performance.now();
|
|
@@ -6032,24 +6277,32 @@ function LevelMeter({
|
|
|
6032
6277
|
carry += Math.min(250, now - last);
|
|
6033
6278
|
last = now;
|
|
6034
6279
|
const input = analyser ? loudness(analyser, buffer) : target.current;
|
|
6280
|
+
const sampled = carry >= SAMPLE_MS;
|
|
6035
6281
|
while (carry >= SAMPLE_MS) {
|
|
6036
6282
|
carry -= SAMPLE_MS;
|
|
6037
6283
|
envelope += (input - envelope) * (input > envelope ? 0.3 : 0.11);
|
|
6038
6284
|
if (envelope < 1e-3) envelope = 0;
|
|
6039
6285
|
samples.shift();
|
|
6040
|
-
samples.push(envelope);
|
|
6041
|
-
|
|
6286
|
+
samples.push(waveSample(envelope));
|
|
6287
|
+
}
|
|
6288
|
+
if (sampled) {
|
|
6289
|
+
const nextPath = wavePath2(samples);
|
|
6290
|
+
drawnPath = nextPath;
|
|
6291
|
+
}
|
|
6292
|
+
const quiet = !analyser && input === 0 && envelope === 0 && samples.every((sample) => sample.level === 0);
|
|
6293
|
+
const shift = quiet ? 0 : Number((-(carry / SAMPLE_MS) * STEP).toFixed(3));
|
|
6294
|
+
if (!painter?.draw(drawnPath, shift)) {
|
|
6295
|
+
if (nativePath !== drawnPath) {
|
|
6296
|
+
shape.setAttribute("d", drawnPath);
|
|
6297
|
+
nativePath = drawnPath;
|
|
6298
|
+
}
|
|
6299
|
+
shape.setAttribute("transform", `translate(${shift} 0)`);
|
|
6042
6300
|
}
|
|
6043
|
-
if (
|
|
6044
|
-
shape.setAttribute("transform", "translate(0 0)");
|
|
6301
|
+
if (quiet) {
|
|
6045
6302
|
stop?.();
|
|
6046
6303
|
stop = void 0;
|
|
6047
6304
|
return;
|
|
6048
6305
|
}
|
|
6049
|
-
shape.setAttribute(
|
|
6050
|
-
"transform",
|
|
6051
|
-
`translate(${(-(carry / SAMPLE_MS) * STEP).toFixed(3)} 0)`
|
|
6052
|
-
);
|
|
6053
6306
|
};
|
|
6054
6307
|
wake.current = () => {
|
|
6055
6308
|
if (stop) return;
|
|
@@ -6061,10 +6314,35 @@ function LevelMeter({
|
|
|
6061
6314
|
return () => {
|
|
6062
6315
|
wake.current = () => void 0;
|
|
6063
6316
|
stop?.();
|
|
6317
|
+
painter?.dispose();
|
|
6064
6318
|
source?.disconnect();
|
|
6065
6319
|
void context?.close();
|
|
6066
6320
|
};
|
|
6067
6321
|
}, [active, stream]);
|
|
6322
|
+
const waveform = useMemo5(() => /* @__PURE__ */ jsxs39(
|
|
6323
|
+
"svg",
|
|
6324
|
+
{
|
|
6325
|
+
viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
|
|
6326
|
+
preserveAspectRatio: "none",
|
|
6327
|
+
"aria-hidden": "true",
|
|
6328
|
+
children: [
|
|
6329
|
+
/* @__PURE__ */ jsx48("defs", { children: /* @__PURE__ */ jsxs39("linearGradient", { id: `${id}-wave`, children: [
|
|
6330
|
+
/* @__PURE__ */ jsx48("stop", { stopColor: "var(--ad-primary-700)" }),
|
|
6331
|
+
/* @__PURE__ */ jsx48("stop", { offset: "0.52", stopColor: "var(--ad-red)" }),
|
|
6332
|
+
/* @__PURE__ */ jsx48("stop", { offset: "1", stopColor: "var(--ad-secondary-100)" })
|
|
6333
|
+
] }) }),
|
|
6334
|
+
/* @__PURE__ */ jsx48("line", { className: "ad-level-meter-axis", x2: WIDTH, y1: MID2, y2: MID2 }),
|
|
6335
|
+
/* @__PURE__ */ jsx48(
|
|
6336
|
+
"path",
|
|
6337
|
+
{
|
|
6338
|
+
ref: path,
|
|
6339
|
+
className: "ad-level-meter-wave",
|
|
6340
|
+
fill: `url(#${id}-wave)`
|
|
6341
|
+
}
|
|
6342
|
+
)
|
|
6343
|
+
]
|
|
6344
|
+
}
|
|
6345
|
+
), [id]);
|
|
6068
6346
|
return /* @__PURE__ */ jsx48(
|
|
6069
6347
|
"div",
|
|
6070
6348
|
{
|
|
@@ -6076,30 +6354,7 @@ function LevelMeter({
|
|
|
6076
6354
|
"aria-valuenow": stream ? void 0 : Math.round(active ? level * 100 : 0),
|
|
6077
6355
|
"data-active": active,
|
|
6078
6356
|
"data-compact": compact || void 0,
|
|
6079
|
-
children:
|
|
6080
|
-
"svg",
|
|
6081
|
-
{
|
|
6082
|
-
viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
|
|
6083
|
-
preserveAspectRatio: "none",
|
|
6084
|
-
"aria-hidden": "true",
|
|
6085
|
-
children: [
|
|
6086
|
-
/* @__PURE__ */ jsx48("defs", { children: /* @__PURE__ */ jsxs39("linearGradient", { id: `${id}-wave`, children: [
|
|
6087
|
-
/* @__PURE__ */ jsx48("stop", { stopColor: "var(--ad-primary-700)" }),
|
|
6088
|
-
/* @__PURE__ */ jsx48("stop", { offset: "0.52", stopColor: "var(--ad-red)" }),
|
|
6089
|
-
/* @__PURE__ */ jsx48("stop", { offset: "1", stopColor: "var(--ad-secondary-100)" })
|
|
6090
|
-
] }) }),
|
|
6091
|
-
/* @__PURE__ */ jsx48("line", { className: "ad-level-meter-axis", x2: WIDTH, y1: MID2, y2: MID2 }),
|
|
6092
|
-
/* @__PURE__ */ jsx48(
|
|
6093
|
-
"path",
|
|
6094
|
-
{
|
|
6095
|
-
ref: path,
|
|
6096
|
-
className: "ad-level-meter-wave",
|
|
6097
|
-
fill: `url(#${id}-wave)`
|
|
6098
|
-
}
|
|
6099
|
-
)
|
|
6100
|
-
]
|
|
6101
|
-
}
|
|
6102
|
-
)
|
|
6357
|
+
children: waveform
|
|
6103
6358
|
}
|
|
6104
6359
|
);
|
|
6105
6360
|
}
|
|
@@ -6147,7 +6402,7 @@ var KaraokeLyrics = ({
|
|
|
6147
6402
|
);
|
|
6148
6403
|
|
|
6149
6404
|
// src/components/media/MelodyRoll/MelodyRoll.tsx
|
|
6150
|
-
import { useEffect as useEffect14, useLayoutEffect as
|
|
6405
|
+
import { useEffect as useEffect14, useLayoutEffect as useLayoutEffect6, useMemo as useMemo6, useRef as useRef28, useState as useState13 } from "react";
|
|
6151
6406
|
|
|
6152
6407
|
// src/components/media/MelodyRoll/melody-glow.js
|
|
6153
6408
|
function renderGlow() {
|
|
@@ -7254,7 +7509,7 @@ var MelodyRoll = ({
|
|
|
7254
7509
|
const glow3 = useRef28(null);
|
|
7255
7510
|
const [glowReady, setGlowReady] = useState13(false);
|
|
7256
7511
|
const [span, setSpan] = useControllable(windowProp, defaultWindow, onWindowChange);
|
|
7257
|
-
const indexed =
|
|
7512
|
+
const indexed = useMemo6(() => {
|
|
7258
7513
|
let low2 = 60, high2 = 64;
|
|
7259
7514
|
const blocks = [];
|
|
7260
7515
|
for (let i = 0; i < notes.length; i += 128) {
|
|
@@ -7278,7 +7533,7 @@ var MelodyRoll = ({
|
|
|
7278
7533
|
const x = (seconds) => (seconds - from) / span * 100;
|
|
7279
7534
|
const rangeStart = glowReady ? origin : from;
|
|
7280
7535
|
const rangeEnd = glowReady ? origin + span * 2 : from + span;
|
|
7281
|
-
const visible =
|
|
7536
|
+
const visible = useMemo6(() => indexed.blocks.filter((block) => block.end >= rangeStart && block.start <= rangeEnd).flatMap((block) => block.notes.filter((note) => note.end >= rangeStart && note.start <= rangeEnd)), [indexed, rangeStart, rangeEnd]);
|
|
7282
7537
|
const phases = glowReady ? "" : visible.map((note) => note.end < from || note.start > from + span ? "x" : position >= note.start && position <= note.end ? "n" : position > note.end ? "p" : "-").join("");
|
|
7283
7538
|
const hits = hitIds?.size ? visible.map((note) => hitIds.has(note.id) ? "h" : "-").join("") : "";
|
|
7284
7539
|
const glowState = { beat: clamp(beat, 0, 1), pitch: livePitch, high, rows, lead, accuracy: clamp(accuracy, 0, 1), level: clamp(level, 0, 1), hit, position, span, notes: visible, hits, showLyrics };
|
|
@@ -7290,11 +7545,11 @@ var MelodyRoll = ({
|
|
|
7290
7545
|
glow3.current = null;
|
|
7291
7546
|
};
|
|
7292
7547
|
}, []);
|
|
7293
|
-
|
|
7548
|
+
useLayoutEffect6(() => {
|
|
7294
7549
|
glow3.current?.update(glowState);
|
|
7295
7550
|
}, [beat, livePitch, high, rows, lead, accuracy, level, hit, position, span, visible, hits, showLyrics]);
|
|
7296
7551
|
const nativeHits = glowReady ? "" : hits;
|
|
7297
|
-
const noteElements =
|
|
7552
|
+
const noteElements = useMemo6(() => glowReady ? void 0 : visible.map((note, i) => {
|
|
7298
7553
|
const hidden = phases[i] === "x";
|
|
7299
7554
|
return /* @__PURE__ */ jsx50(
|
|
7300
7555
|
"span",
|
|
@@ -7334,7 +7589,7 @@ var MelodyRoll = ({
|
|
|
7334
7589
|
setSpan(clamp(span * (event.deltaY > 0 ? 1.12 : 1 / 1.12), 2, 20));
|
|
7335
7590
|
};
|
|
7336
7591
|
const nativeState = glowReady ? null : glowState;
|
|
7337
|
-
const content =
|
|
7592
|
+
const content = useMemo6(() => /* @__PURE__ */ jsxs41("div", { className: "ad-melody-roll-content", children: [
|
|
7338
7593
|
/* @__PURE__ */ jsx50(
|
|
7339
7594
|
PianoKeyboard,
|
|
7340
7595
|
{
|
|
@@ -7392,7 +7647,7 @@ var MelodyRoll = ({
|
|
|
7392
7647
|
import { useRef as useRef30 } from "react";
|
|
7393
7648
|
|
|
7394
7649
|
// src/components/layout/FloatingPanel/useFloatingPanel.ts
|
|
7395
|
-
import { useCallback as useCallback2, useEffect as useEffect15, useLayoutEffect as
|
|
7650
|
+
import { useCallback as useCallback2, useEffect as useEffect15, useLayoutEffect as useLayoutEffect7, useRef as useRef29, useState as useState14 } from "react";
|
|
7396
7651
|
var resizeEdges = ["n", "s", "e", "w", "ne", "nw", "se", "sw"];
|
|
7397
7652
|
var threshold = 3;
|
|
7398
7653
|
var panelControls = "button, a, input, select, textarea, [role=slider], [role=button], [role=switch], [role=listbox], [contenteditable=true], .ad-rotary-knob";
|
|
@@ -7404,7 +7659,7 @@ var useFloatingPanel = (frameRef, options = {}) => {
|
|
|
7404
7659
|
const latest = useRef29(options);
|
|
7405
7660
|
latest.current = options;
|
|
7406
7661
|
const saved = options.layout;
|
|
7407
|
-
|
|
7662
|
+
useLayoutEffect7(() => {
|
|
7408
7663
|
drag.current = null;
|
|
7409
7664
|
setLive(null);
|
|
7410
7665
|
}, [saved?.left, saved?.top, saved?.width, saved?.height]);
|
|
@@ -7566,7 +7821,7 @@ var FloatingPanel = ({ layout: saved, onLayoutChange, defaultSize, limits, onDra
|
|
|
7566
7821
|
};
|
|
7567
7822
|
|
|
7568
7823
|
// src/components/media/Sparkline/Sparkline.tsx
|
|
7569
|
-
import { useMemo as
|
|
7824
|
+
import { useMemo as useMemo7, useRef as useRef31 } from "react";
|
|
7570
7825
|
import { Fragment as Fragment5, jsx as jsx52, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
7571
7826
|
var DEMO3 = [12, 23, 17, 31, 43, 24, 28, 20, 41, 29, 51, 34, 38, 22, 31, 16, 23];
|
|
7572
7827
|
var Sparkline = (p) => {
|
|
@@ -7574,7 +7829,7 @@ var Sparkline = (p) => {
|
|
|
7574
7829
|
const id = useSvgId();
|
|
7575
7830
|
const ref = useRef31(null);
|
|
7576
7831
|
usePauseOffscreen(ref);
|
|
7577
|
-
const { line: line2, lastX, lastY } =
|
|
7832
|
+
const { line: line2, lastX, lastY } = useMemo7(() => {
|
|
7578
7833
|
if (!values.length) return { line: "", lastX: 0, lastY: 0 };
|
|
7579
7834
|
let min = p.fit ? Infinity : 0;
|
|
7580
7835
|
let max = p.fit ? -Infinity : 1;
|
|
@@ -7646,7 +7901,7 @@ import { useRef as useRef32 } from "react";
|
|
|
7646
7901
|
import { jsx as jsx53, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
7647
7902
|
var POINTS = 23;
|
|
7648
7903
|
var STEP2 = 65 / (POINTS - 1);
|
|
7649
|
-
var
|
|
7904
|
+
var X2 = Array.from({ length: POINTS }, (_, k) => k * STEP2 / 65 * 600);
|
|
7650
7905
|
var strand = (j, t) => {
|
|
7651
7906
|
const ys = Array.from({ length: POINTS + 2 }, (_, k) => {
|
|
7652
7907
|
const i = (k - 1) * STEP2;
|
|
@@ -7655,7 +7910,7 @@ var strand = (j, t) => {
|
|
|
7655
7910
|
let d = `M0 ${ys[1].toFixed(2)}`;
|
|
7656
7911
|
for (let k = 1; k < POINTS; k += 1) {
|
|
7657
7912
|
const y0 = ys[k - 1], y1 = ys[k], y2 = ys[k + 1], y3 = ys[k + 2];
|
|
7658
|
-
const x1 =
|
|
7913
|
+
const x1 = X2[k - 1], x2 = X2[k], third = (x2 - x1) / 3;
|
|
7659
7914
|
d += `C${(x1 + third).toFixed(2)} ${(y1 + (y2 - y0) / 6).toFixed(2)} ${(x2 - third).toFixed(2)} ${(y2 - (y3 - y1) / 6).toFixed(2)} ${x2.toFixed(2)} ${y2.toFixed(2)}`;
|
|
7660
7915
|
}
|
|
7661
7916
|
return d;
|