@ghchinoy/lit-audio-ui 0.1.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/LICENSE +202 -0
- package/README.md +148 -0
- package/dist/favicon.png +0 -0
- package/dist/scream-audio-ui.es.js +2121 -0
- package/dist/scream-audio-ui.umd.js +1217 -0
- package/package.json +54 -0
|
@@ -0,0 +1,2121 @@
|
|
|
1
|
+
import { LitElement as e, css as t, html as n } from "lit";
|
|
2
|
+
import { customElement as r, property as i, query as a, state as o } from "lit/decorators.js";
|
|
3
|
+
import "@material/web/button/filled-button.js";
|
|
4
|
+
import "@material/web/icon/icon.js";
|
|
5
|
+
import { classMap as s } from "lit/directives/class-map.js";
|
|
6
|
+
import "@material/web/button/outlined-button.js";
|
|
7
|
+
import "@material/web/iconbutton/filled-icon-button.js";
|
|
8
|
+
import "@material/web/progress/circular-progress.js";
|
|
9
|
+
import "@material/web/slider/slider.js";
|
|
10
|
+
import "@material/web/menu/menu.js";
|
|
11
|
+
import "@material/web/menu/menu-item.js";
|
|
12
|
+
import "@material/web/divider/divider.js";
|
|
13
|
+
import "@material/web/button/text-button.js";
|
|
14
|
+
import "@material/web/button/filled-tonal-button.js";
|
|
15
|
+
import "@material/web/textfield/outlined-text-field.js";
|
|
16
|
+
import * as c from "three";
|
|
17
|
+
import "@material/web/iconbutton/icon-button.js";
|
|
18
|
+
function l(e, t, n, r) {
|
|
19
|
+
var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
|
|
20
|
+
if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
|
|
21
|
+
else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
|
|
22
|
+
return i > 3 && a && Object.defineProperty(t, n, a), a;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Copyright 2026 Google LLC
|
|
26
|
+
*
|
|
27
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
28
|
+
* you may not use this file except in compliance with the License.
|
|
29
|
+
* You may obtain a copy of the License at
|
|
30
|
+
*
|
|
31
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
32
|
+
*
|
|
33
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
34
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
35
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
36
|
+
* See the License for the specific language governing permissions and
|
|
37
|
+
* limitations under the License.
|
|
38
|
+
*/
|
|
39
|
+
var u = class extends e {
|
|
40
|
+
constructor(...e) {
|
|
41
|
+
super(...e), this.state = "idle";
|
|
42
|
+
}
|
|
43
|
+
static {
|
|
44
|
+
this.styles = t`
|
|
45
|
+
:host {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
md-filled-button {
|
|
50
|
+
--md-filled-button-container-shape: 999px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
md-filled-button.recording {
|
|
54
|
+
--md-filled-button-container-color: var(--md-sys-color-error, #ba1a1a);
|
|
55
|
+
--md-filled-button-label-text-color: var(
|
|
56
|
+
--md-sys-color-on-error,
|
|
57
|
+
#ffffff
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
render() {
|
|
63
|
+
return n`
|
|
64
|
+
<md-filled-button class="${this.state}" @click="${this._handleClick}">
|
|
65
|
+
<md-icon slot="icon">
|
|
66
|
+
${this.state === "recording" ? "stop" : "mic"}
|
|
67
|
+
</md-icon>
|
|
68
|
+
|
|
69
|
+
${this.state === "recording" ? "Recording..." : "Speak"}
|
|
70
|
+
</md-filled-button>
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
_handleClick() {
|
|
74
|
+
this.state = this.state === "idle" ? "recording" : "idle", this.dispatchEvent(new CustomEvent("voice-toggle", {
|
|
75
|
+
bubbles: !0,
|
|
76
|
+
composed: !0,
|
|
77
|
+
detail: { state: this.state }
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
l([i({ type: String })], u.prototype, "state", void 0), u = l([r("scream-voice-button")], u);
|
|
82
|
+
/**
|
|
83
|
+
* Normalizes raw frequency data from the AnalyserNode into an array of values between 0.0 and 1.0.
|
|
84
|
+
*
|
|
85
|
+
* @param analyser The AnalyserNode to read from
|
|
86
|
+
* @param dataArray A pre-allocated Uint8Array to hold the raw byte data
|
|
87
|
+
* @returns An array of normalized numbers (0.0 to 1.0)
|
|
88
|
+
*/
|
|
89
|
+
function d(e, t) {
|
|
90
|
+
e.getByteFrequencyData(t);
|
|
91
|
+
let n = [];
|
|
92
|
+
for (let e = 0; e < t.length; e++) n.push(t[e] / 255);
|
|
93
|
+
return n;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Creates an edge-fade gradient over a canvas to smoothly blend the left and right edges.
|
|
97
|
+
*
|
|
98
|
+
* @param ctx The canvas 2D rendering context
|
|
99
|
+
* @param width The logical width of the canvas
|
|
100
|
+
* @param height The logical height of the canvas
|
|
101
|
+
* @param fadeWidth The physical width in pixels of the fade effect
|
|
102
|
+
*/
|
|
103
|
+
function f(e, t, n, r) {
|
|
104
|
+
if (r <= 0 || t <= 0) return;
|
|
105
|
+
let i = e.createLinearGradient(0, 0, t, 0), a = Math.min(.2, r / t);
|
|
106
|
+
i.addColorStop(0, "rgba(0,0,0,0)"), i.addColorStop(a, "rgba(0,0,0,1)"), i.addColorStop(1 - a, "rgba(0,0,0,1)"), i.addColorStop(1, "rgba(0,0,0,0)"), e.globalCompositeOperation = "destination-in", e.fillStyle = i, e.fillRect(0, 0, t, n), e.globalCompositeOperation = "source-over";
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Copyright 2026 Google LLC
|
|
110
|
+
*
|
|
111
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
112
|
+
* you may not use this file except in compliance with the License.
|
|
113
|
+
* You may obtain a copy of the License at
|
|
114
|
+
*
|
|
115
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
116
|
+
*
|
|
117
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
118
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
119
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
120
|
+
* See the License for the specific language governing permissions and
|
|
121
|
+
* limitations under the License.
|
|
122
|
+
*/
|
|
123
|
+
var p = class extends e {
|
|
124
|
+
constructor(...e) {
|
|
125
|
+
super(...e), this.active = !1, this.processing = !1, this.barWidth = 3, this.barHeight = 4, this.barGap = 1, this.barRadius = 1.5, this.fadeEdges = !0, this.fadeWidth = 24, this.height = 64, this.sensitivity = 1, this.updateRate = 30, this._animationFrameId = 0, this._lastUpdateTime = 0, this._currentBars = [], this._processingTime = 0, this._transitionProgress = 0, this._lastActiveData = [];
|
|
126
|
+
}
|
|
127
|
+
static {
|
|
128
|
+
this.styles = t`
|
|
129
|
+
:host {
|
|
130
|
+
display: block;
|
|
131
|
+
width: 100%;
|
|
132
|
+
}
|
|
133
|
+
.container {
|
|
134
|
+
position: relative;
|
|
135
|
+
width: 100%;
|
|
136
|
+
}
|
|
137
|
+
canvas {
|
|
138
|
+
position: absolute;
|
|
139
|
+
top: 0;
|
|
140
|
+
left: 0;
|
|
141
|
+
display: block;
|
|
142
|
+
height: 100%;
|
|
143
|
+
width: 100%;
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
render() {
|
|
148
|
+
return n`
|
|
149
|
+
<div class="container" style="height: ${this.height}px;">
|
|
150
|
+
<canvas></canvas>
|
|
151
|
+
</div>
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
firstUpdated() {
|
|
155
|
+
this._resizeObserver = new ResizeObserver(() => {
|
|
156
|
+
this._handleResize();
|
|
157
|
+
}), this._resizeObserver.observe(this._container), this._startAnimationLoop();
|
|
158
|
+
}
|
|
159
|
+
updated(e) {
|
|
160
|
+
super.updated(e), e.has("analyserNode") && this.analyserNode && (this._dataArray = new Uint8Array(this.analyserNode.frequencyBinCount)), e.has("processing") && this.processing && !this.active && (this._processingTime = 0, this._transitionProgress = 0);
|
|
161
|
+
}
|
|
162
|
+
disconnectedCallback() {
|
|
163
|
+
super.disconnectedCallback(), this._resizeObserver && this._resizeObserver.disconnect(), this._animationFrameId && cancelAnimationFrame(this._animationFrameId);
|
|
164
|
+
}
|
|
165
|
+
_handleResize() {
|
|
166
|
+
if (!this._canvas || !this._container) return;
|
|
167
|
+
let e = this._container.getBoundingClientRect(), t = window.devicePixelRatio || 1;
|
|
168
|
+
this._canvas.width = e.width * t, this._canvas.height = e.height * t, this._canvas.style.width = `${e.width}px`, this._canvas.style.height = `${e.height}px`;
|
|
169
|
+
let n = this._canvas.getContext("2d");
|
|
170
|
+
n && n.scale(t, t), this._renderFrame();
|
|
171
|
+
}
|
|
172
|
+
_startAnimationLoop() {
|
|
173
|
+
let e = (t) => {
|
|
174
|
+
this._updateData(t), this._renderFrame(), this._animationFrameId = requestAnimationFrame(e);
|
|
175
|
+
};
|
|
176
|
+
this._animationFrameId = requestAnimationFrame(e);
|
|
177
|
+
}
|
|
178
|
+
_updateData(e) {
|
|
179
|
+
if (!this._canvas) return;
|
|
180
|
+
let t = this._canvas.getBoundingClientRect(), n = Math.floor(t.width / (this.barWidth + this.barGap));
|
|
181
|
+
if (this.active && this.analyserNode && this._dataArray) {
|
|
182
|
+
if (e - this._lastUpdateTime > this.updateRate) {
|
|
183
|
+
this._lastUpdateTime = e;
|
|
184
|
+
let t = d(this.analyserNode, this._dataArray), r = Math.floor(t.length * .05), i = Math.floor(t.length * .4), a = t.slice(r, i), o = Math.floor(n / 2), s = Array(n).fill(.05), c = a.length - 1;
|
|
185
|
+
for (let e = 0; e <= o; e++) {
|
|
186
|
+
let t = e / o, r = a[Math.floor(t * c)] || 0;
|
|
187
|
+
t > .8 && (r *= 1 - (t - .8) * 5);
|
|
188
|
+
let i = Math.max(.05, Math.min(1, r * this.sensitivity)), l = o + e, u = o - e;
|
|
189
|
+
l < n && (s[l] = i), u >= 0 && (s[u] = i);
|
|
190
|
+
}
|
|
191
|
+
this._currentBars = s, this._lastActiveData = [...s];
|
|
192
|
+
}
|
|
193
|
+
} else if (this.processing && !this.active) {
|
|
194
|
+
this._processingTime += .03, this._transitionProgress = Math.min(1, this._transitionProgress + .02);
|
|
195
|
+
let e = Array(n).fill(.05), t = Math.floor(n / 2);
|
|
196
|
+
for (let r = 0; r < n; r++) {
|
|
197
|
+
let n = (r - t) / t, i = 1 - Math.abs(n) * .4, a = Math.sin(this._processingTime * 1.5 + n * 3) * .25, o = Math.sin(this._processingTime * .8 - n * 2) * .2, s = Math.cos(this._processingTime * 2 + n) * .15, c = (.2 + (a + o + s)) * i, l = c;
|
|
198
|
+
if (this._lastActiveData.length > 0 && this._transitionProgress < 1) {
|
|
199
|
+
let e = Math.min(r, this._lastActiveData.length - 1);
|
|
200
|
+
l = (this._lastActiveData[e] || 0) * (1 - this._transitionProgress) + c * this._transitionProgress;
|
|
201
|
+
}
|
|
202
|
+
e[r] = Math.max(.05, Math.min(1, l));
|
|
203
|
+
}
|
|
204
|
+
this._currentBars = e;
|
|
205
|
+
} else if (this._currentBars.length > 0) {
|
|
206
|
+
let e = !0;
|
|
207
|
+
for (let t = 0; t < this._currentBars.length; t++) this._currentBars[t] = Math.max(.05, this._currentBars[t] * .85), this._currentBars[t] > .06 && (e = !1);
|
|
208
|
+
e && (this._currentBars = []);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
_renderFrame() {
|
|
212
|
+
if (!this._canvas) return;
|
|
213
|
+
let e = this._canvas.getContext("2d");
|
|
214
|
+
if (!e) return;
|
|
215
|
+
let t = this._canvas.getBoundingClientRect();
|
|
216
|
+
e.clearRect(0, 0, t.width, t.height);
|
|
217
|
+
let n = getComputedStyle(this), r = this.barColor;
|
|
218
|
+
if (!r) {
|
|
219
|
+
let e = n.getPropertyValue("--md-sys-color-primary").trim(), t = n.getPropertyValue("color").trim();
|
|
220
|
+
r = e || t || "#0066cc";
|
|
221
|
+
}
|
|
222
|
+
let i = this.barWidth + this.barGap, a = Math.floor(t.width / i), o = t.height / 2;
|
|
223
|
+
for (let n = 0; n < a && n < this._currentBars.length; n++) {
|
|
224
|
+
let a = this._currentBars[n] || .05, s = n * i, c = Math.max(this.barHeight, a * t.height * .8), l = o - c / 2;
|
|
225
|
+
e.fillStyle = r, e.globalAlpha = .4 + a * .6, this.barRadius > 0 ? (e.beginPath(), e.roundRect(s, l, this.barWidth, c, this.barRadius), e.fill()) : e.fillRect(s, l, this.barWidth, c);
|
|
226
|
+
}
|
|
227
|
+
this.fadeEdges && f(e, t.width, t.height, this.fadeWidth), e.globalAlpha = 1;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
l([i({ type: Boolean })], p.prototype, "active", void 0), l([i({ type: Boolean })], p.prototype, "processing", void 0), l([i({ attribute: !1 })], p.prototype, "analyserNode", void 0), l([i({ type: Number })], p.prototype, "barWidth", void 0), l([i({ type: Number })], p.prototype, "barHeight", void 0), l([i({ type: Number })], p.prototype, "barGap", void 0), l([i({ type: Number })], p.prototype, "barRadius", void 0), l([i({ type: String })], p.prototype, "barColor", void 0), l([i({ type: Boolean })], p.prototype, "fadeEdges", void 0), l([i({ type: Number })], p.prototype, "fadeWidth", void 0), l([i({ type: Number })], p.prototype, "height", void 0), l([i({ type: Number })], p.prototype, "sensitivity", void 0), l([i({ type: Number })], p.prototype, "updateRate", void 0), l([a("canvas")], p.prototype, "_canvas", void 0), l([a(".container")], p.prototype, "_container", void 0), p = l([r("ui-live-waveform")], p);
|
|
231
|
+
/**
|
|
232
|
+
* Copyright 2026 Google LLC
|
|
233
|
+
*
|
|
234
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
235
|
+
* you may not use this file except in compliance with the License.
|
|
236
|
+
* You may obtain a copy of the License at
|
|
237
|
+
*
|
|
238
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
239
|
+
*
|
|
240
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
241
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
242
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
243
|
+
* See the License for the specific language governing permissions and
|
|
244
|
+
* limitations under the License.
|
|
245
|
+
*/
|
|
246
|
+
var m = class extends e {
|
|
247
|
+
constructor(...e) {
|
|
248
|
+
super(...e), this.state = "idle", this.disabled = !1, this._showFeedback = !1;
|
|
249
|
+
}
|
|
250
|
+
static {
|
|
251
|
+
this.styles = t`
|
|
252
|
+
:host {
|
|
253
|
+
display: inline-block;
|
|
254
|
+
--ui-waveform-height: 24px;
|
|
255
|
+
--ui-waveform-width: 96px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.wrapper {
|
|
259
|
+
display: flex;
|
|
260
|
+
align-items: center;
|
|
261
|
+
gap: 12px;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
md-filled-button,
|
|
265
|
+
md-outlined-button {
|
|
266
|
+
transition: all 0.2s ease-in-out;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Customize the button depending on the state */
|
|
270
|
+
md-filled-button.recording {
|
|
271
|
+
--md-filled-button-container-color: var(
|
|
272
|
+
--md-sys-color-error-container,
|
|
273
|
+
#ffdad6
|
|
274
|
+
);
|
|
275
|
+
--md-filled-button-label-text-color: var(
|
|
276
|
+
--md-sys-color-on-error-container,
|
|
277
|
+
#410002
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
md-filled-button.processing {
|
|
282
|
+
--md-filled-button-container-color: var(
|
|
283
|
+
--md-sys-color-secondary-container,
|
|
284
|
+
#cce5ff
|
|
285
|
+
);
|
|
286
|
+
--md-filled-button-label-text-color: var(
|
|
287
|
+
--md-sys-color-on-secondary-container,
|
|
288
|
+
#001d36
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
md-filled-button.success {
|
|
293
|
+
--md-filled-button-container-color: var(
|
|
294
|
+
--md-sys-color-primary-container,
|
|
295
|
+
#d1e4ff
|
|
296
|
+
);
|
|
297
|
+
--md-filled-button-label-text-color: var(
|
|
298
|
+
--md-sys-color-on-primary-container,
|
|
299
|
+
#001d36
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.waveform-slot {
|
|
304
|
+
position: relative;
|
|
305
|
+
width: var(--ui-waveform-width);
|
|
306
|
+
height: var(--ui-waveform-height);
|
|
307
|
+
border-radius: 4px;
|
|
308
|
+
overflow: hidden;
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
justify-content: center;
|
|
312
|
+
background: var(--md-sys-color-surface-container-highest, #e3e3e3);
|
|
313
|
+
border: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);
|
|
314
|
+
transition: background-color 0.3s ease;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.waveform-slot.recording {
|
|
318
|
+
background: var(--md-sys-color-error-container, #ffdad6);
|
|
319
|
+
border-color: transparent;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.waveform-slot.processing {
|
|
323
|
+
background: var(--md-sys-color-secondary-container, #cce5ff);
|
|
324
|
+
border-color: transparent;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.trailing-text {
|
|
328
|
+
font-family: monospace;
|
|
329
|
+
font-size: 11px;
|
|
330
|
+
color: var(--md-sys-color-on-surface-variant, #444);
|
|
331
|
+
font-weight: 500;
|
|
332
|
+
user-select: none;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.feedback-overlay {
|
|
336
|
+
position: absolute;
|
|
337
|
+
inset: 0;
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: center;
|
|
341
|
+
background: var(--md-sys-color-surface, #fff);
|
|
342
|
+
animation: fadeIn 0.3s ease forwards;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.feedback-icon {
|
|
346
|
+
font-size: 16px;
|
|
347
|
+
}
|
|
348
|
+
.feedback-icon.success {
|
|
349
|
+
color: var(--md-sys-color-primary, #0066cc);
|
|
350
|
+
}
|
|
351
|
+
.feedback-icon.error {
|
|
352
|
+
color: var(--md-sys-color-error, #ba1a1a);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
@keyframes fadeIn {
|
|
356
|
+
from {
|
|
357
|
+
opacity: 0;
|
|
358
|
+
}
|
|
359
|
+
to {
|
|
360
|
+
opacity: 0.9;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
`;
|
|
364
|
+
}
|
|
365
|
+
updated(e) {
|
|
366
|
+
super.updated(e), e.has("state") && (this.state === "success" || this.state === "error" ? (this._showFeedback = !0, this._feedbackTimeout && clearTimeout(this._feedbackTimeout), this._feedbackTimeout = setTimeout(() => {
|
|
367
|
+
this._showFeedback = !1, (this.state === "success" || this.state === "error") && (this.state = "idle");
|
|
368
|
+
}, 1500)) : this._showFeedback = !1);
|
|
369
|
+
}
|
|
370
|
+
render() {
|
|
371
|
+
let e = this.state === "recording", t = this.state === "processing", r = this.state === "success", i = this.state === "error", a = this.disabled || t, o = e || t, c = !o && !this._showFeedback && this.trailing, l = {
|
|
372
|
+
recording: e,
|
|
373
|
+
processing: t,
|
|
374
|
+
success: r && this._showFeedback,
|
|
375
|
+
error: i && this._showFeedback
|
|
376
|
+
}, u = {
|
|
377
|
+
"waveform-slot": !0,
|
|
378
|
+
recording: e,
|
|
379
|
+
processing: t
|
|
380
|
+
};
|
|
381
|
+
return n`
|
|
382
|
+
<md-filled-button
|
|
383
|
+
class=${s(l)}
|
|
384
|
+
?disabled=${a}
|
|
385
|
+
@click=${this._handleClick}
|
|
386
|
+
>
|
|
387
|
+
<div class="wrapper">
|
|
388
|
+
${this.label ? n`<span>${this.label}</span>` : ""}
|
|
389
|
+
|
|
390
|
+
<div class=${s(u)}>
|
|
391
|
+
${o ? n`
|
|
392
|
+
<ui-live-waveform
|
|
393
|
+
.active=${e}
|
|
394
|
+
.processing=${t}
|
|
395
|
+
.analyserNode=${this.analyserNode}
|
|
396
|
+
.barWidth=${2}
|
|
397
|
+
.barGap=${1}
|
|
398
|
+
.barRadius=${4}
|
|
399
|
+
.fadeEdges=${!1}
|
|
400
|
+
.sensitivity=${1.8}
|
|
401
|
+
height="20"
|
|
402
|
+
style="position: absolute; inset: 0;"
|
|
403
|
+
></ui-live-waveform>
|
|
404
|
+
` : ""}
|
|
405
|
+
${c ? n` <span class="trailing-text">${this.trailing}</span> ` : ""}
|
|
406
|
+
${this._showFeedback && r ? n`
|
|
407
|
+
<div class="feedback-overlay">
|
|
408
|
+
<md-icon class="feedback-icon success">check</md-icon>
|
|
409
|
+
</div>
|
|
410
|
+
` : ""}
|
|
411
|
+
${this._showFeedback && i ? n`
|
|
412
|
+
<div class="feedback-overlay">
|
|
413
|
+
<md-icon class="feedback-icon error">close</md-icon>
|
|
414
|
+
</div>
|
|
415
|
+
` : ""}
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
</md-filled-button>
|
|
419
|
+
`;
|
|
420
|
+
}
|
|
421
|
+
_handleClick(e) {
|
|
422
|
+
this.dispatchEvent(new CustomEvent("voice-button-click", {
|
|
423
|
+
bubbles: !0,
|
|
424
|
+
composed: !0,
|
|
425
|
+
detail: { state: this.state }
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
l([i({ type: String })], m.prototype, "state", void 0), l([i({ type: String })], m.prototype, "label", void 0), l([i({ type: String })], m.prototype, "trailing", void 0), l([i({ type: Boolean })], m.prototype, "disabled", void 0), l([i({ attribute: !1 })], m.prototype, "analyserNode", void 0), l([o()], m.prototype, "_showFeedback", void 0), m = l([r("ui-voice-button")], m);
|
|
430
|
+
/**
|
|
431
|
+
* Copyright 2026 Google LLC
|
|
432
|
+
*
|
|
433
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
434
|
+
* you may not use this file except in compliance with the License.
|
|
435
|
+
* You may obtain a copy of the License at
|
|
436
|
+
*
|
|
437
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
438
|
+
*
|
|
439
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
440
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
441
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
442
|
+
* See the License for the specific language governing permissions and
|
|
443
|
+
* limitations under the License.
|
|
444
|
+
*/
|
|
445
|
+
var h = class extends e {
|
|
446
|
+
constructor(...e) {
|
|
447
|
+
super(...e), this.data = [], this.barWidth = 4, this.barHeight = 4, this.barGap = 2, this.barRadius = 2, this.fadeEdges = !0, this.fadeWidth = 24, this.height = 128;
|
|
448
|
+
}
|
|
449
|
+
static {
|
|
450
|
+
this.styles = t`
|
|
451
|
+
:host {
|
|
452
|
+
display: block;
|
|
453
|
+
width: 100%;
|
|
454
|
+
}
|
|
455
|
+
.container {
|
|
456
|
+
position: relative;
|
|
457
|
+
width: 100%;
|
|
458
|
+
}
|
|
459
|
+
canvas {
|
|
460
|
+
position: absolute;
|
|
461
|
+
top: 0;
|
|
462
|
+
left: 0;
|
|
463
|
+
display: block;
|
|
464
|
+
height: 100%;
|
|
465
|
+
width: 100%;
|
|
466
|
+
}
|
|
467
|
+
`;
|
|
468
|
+
}
|
|
469
|
+
render() {
|
|
470
|
+
return n`
|
|
471
|
+
<div class="container" style="height: ${this.height}px;">
|
|
472
|
+
<canvas></canvas>
|
|
473
|
+
</div>
|
|
474
|
+
`;
|
|
475
|
+
}
|
|
476
|
+
firstUpdated() {
|
|
477
|
+
this._resizeObserver = new ResizeObserver(() => {
|
|
478
|
+
this._handleResize();
|
|
479
|
+
}), this._resizeObserver.observe(this._container);
|
|
480
|
+
}
|
|
481
|
+
updated(e) {
|
|
482
|
+
super.updated(e), (e.has("data") || e.has("barColor")) && this._renderWaveform();
|
|
483
|
+
}
|
|
484
|
+
disconnectedCallback() {
|
|
485
|
+
super.disconnectedCallback(), this._resizeObserver && this._resizeObserver.disconnect();
|
|
486
|
+
}
|
|
487
|
+
_handleResize() {
|
|
488
|
+
if (!this._canvas || !this._container) return;
|
|
489
|
+
let e = this._container.getBoundingClientRect(), t = window.devicePixelRatio || 1;
|
|
490
|
+
this._canvas.width = e.width * t, this._canvas.height = e.height * t, this._canvas.style.width = `${e.width}px`, this._canvas.style.height = `${e.height}px`;
|
|
491
|
+
let n = this._canvas.getContext("2d");
|
|
492
|
+
n && (n.scale(t, t), this._renderWaveform());
|
|
493
|
+
}
|
|
494
|
+
_renderWaveform() {
|
|
495
|
+
if (!this._canvas) return;
|
|
496
|
+
let e = this._canvas.getContext("2d");
|
|
497
|
+
if (!e) return;
|
|
498
|
+
let t = this._canvas.getBoundingClientRect();
|
|
499
|
+
e.clearRect(0, 0, t.width, t.height);
|
|
500
|
+
let n = getComputedStyle(this), r = this.barColor;
|
|
501
|
+
if (!r) {
|
|
502
|
+
let e = n.getPropertyValue("--md-sys-color-primary").trim(), t = n.getPropertyValue("color").trim();
|
|
503
|
+
r = e || t || "#0066cc";
|
|
504
|
+
}
|
|
505
|
+
let i = Math.floor(t.width / (this.barWidth + this.barGap)), a = t.height / 2;
|
|
506
|
+
for (let n = 0; n < i; n++) {
|
|
507
|
+
let o = Math.floor(n / i * this.data.length), s = this.data[o] || 0, c = Math.max(this.barHeight, s * t.height * .8), l = n * (this.barWidth + this.barGap), u = a - c / 2;
|
|
508
|
+
e.fillStyle = r, e.globalAlpha = .3 + s * .7, this.barRadius > 0 ? (e.beginPath(), e.roundRect(l, u, this.barWidth, c, this.barRadius), e.fill()) : e.fillRect(l, u, this.barWidth, c);
|
|
509
|
+
}
|
|
510
|
+
this.fadeEdges && f(e, t.width, t.height, this.fadeWidth), e.globalAlpha = 1;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
l([i({ type: Array })], h.prototype, "data", void 0), l([i({ type: Number })], h.prototype, "barWidth", void 0), l([i({ type: Number })], h.prototype, "barHeight", void 0), l([i({ type: Number })], h.prototype, "barGap", void 0), l([i({ type: Number })], h.prototype, "barRadius", void 0), l([i({ type: String })], h.prototype, "barColor", void 0), l([i({ type: Boolean })], h.prototype, "fadeEdges", void 0), l([i({ type: Number })], h.prototype, "fadeWidth", void 0), l([i({ type: Number })], h.prototype, "height", void 0), l([a("canvas")], h.prototype, "_canvas", void 0), l([a(".container")], h.prototype, "_container", void 0), h = l([r("ui-waveform")], h);
|
|
514
|
+
/**
|
|
515
|
+
* @license
|
|
516
|
+
* Copyright 2021 Google LLC
|
|
517
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
518
|
+
*/
|
|
519
|
+
var g = class extends Event {
|
|
520
|
+
constructor(e, t, n, r) {
|
|
521
|
+
super("context-request", {
|
|
522
|
+
bubbles: !0,
|
|
523
|
+
composed: !0
|
|
524
|
+
}), this.context = e, this.contextTarget = t, this.callback = n, this.subscribe = r ?? !1;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* @license
|
|
529
|
+
* Copyright 2021 Google LLC
|
|
530
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
531
|
+
*/
|
|
532
|
+
function _(e) {
|
|
533
|
+
return e;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* @license
|
|
537
|
+
* Copyright 2021 Google LLC
|
|
538
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
539
|
+
*/ var v = class {
|
|
540
|
+
constructor(e, t, n, r) {
|
|
541
|
+
if (this.subscribe = !1, this.provided = !1, this.value = void 0, this.t = (e, t) => {
|
|
542
|
+
this.unsubscribe && (this.unsubscribe !== t && (this.provided = !1, this.unsubscribe()), this.subscribe || this.unsubscribe()), this.value = e, this.host.requestUpdate(), this.provided && !this.subscribe || (this.provided = !0, this.callback && this.callback(e, t)), this.unsubscribe = t;
|
|
543
|
+
}, this.host = e, t.context !== void 0) {
|
|
544
|
+
let e = t;
|
|
545
|
+
this.context = e.context, this.callback = e.callback, this.subscribe = e.subscribe ?? !1;
|
|
546
|
+
} else this.context = t, this.callback = n, this.subscribe = r ?? !1;
|
|
547
|
+
this.host.addController(this);
|
|
548
|
+
}
|
|
549
|
+
hostConnected() {
|
|
550
|
+
this.dispatchRequest();
|
|
551
|
+
}
|
|
552
|
+
hostDisconnected() {
|
|
553
|
+
this.unsubscribe &&= (this.unsubscribe(), void 0);
|
|
554
|
+
}
|
|
555
|
+
dispatchRequest() {
|
|
556
|
+
this.host.dispatchEvent(new g(this.context, this.host, this.t, this.subscribe));
|
|
557
|
+
}
|
|
558
|
+
}, y = class {
|
|
559
|
+
get value() {
|
|
560
|
+
return this.o;
|
|
561
|
+
}
|
|
562
|
+
set value(e) {
|
|
563
|
+
this.setValue(e);
|
|
564
|
+
}
|
|
565
|
+
setValue(e, t = !1) {
|
|
566
|
+
let n = t || !Object.is(e, this.o);
|
|
567
|
+
this.o = e, n && this.updateObservers();
|
|
568
|
+
}
|
|
569
|
+
constructor(e) {
|
|
570
|
+
this.subscriptions = /* @__PURE__ */ new Map(), this.updateObservers = () => {
|
|
571
|
+
for (let [e, { disposer: t }] of this.subscriptions) e(this.o, t);
|
|
572
|
+
}, e !== void 0 && (this.value = e);
|
|
573
|
+
}
|
|
574
|
+
addCallback(e, t, n) {
|
|
575
|
+
if (!n) return void e(this.value);
|
|
576
|
+
this.subscriptions.has(e) || this.subscriptions.set(e, {
|
|
577
|
+
disposer: () => {
|
|
578
|
+
this.subscriptions.delete(e);
|
|
579
|
+
},
|
|
580
|
+
consumerHost: t
|
|
581
|
+
});
|
|
582
|
+
let { disposer: r } = this.subscriptions.get(e);
|
|
583
|
+
e(this.value, r);
|
|
584
|
+
}
|
|
585
|
+
clearCallbacks() {
|
|
586
|
+
this.subscriptions.clear();
|
|
587
|
+
}
|
|
588
|
+
}, b = class extends Event {
|
|
589
|
+
constructor(e, t) {
|
|
590
|
+
super("context-provider", {
|
|
591
|
+
bubbles: !0,
|
|
592
|
+
composed: !0
|
|
593
|
+
}), this.context = e, this.contextTarget = t;
|
|
594
|
+
}
|
|
595
|
+
}, x = class extends y {
|
|
596
|
+
constructor(e, t, n) {
|
|
597
|
+
super(t.context === void 0 ? n : t.initialValue), this.onContextRequest = (e) => {
|
|
598
|
+
if (e.context !== this.context) return;
|
|
599
|
+
let t = e.contextTarget ?? e.composedPath()[0];
|
|
600
|
+
t !== this.host && (e.stopPropagation(), this.addCallback(e.callback, t, e.subscribe));
|
|
601
|
+
}, this.onProviderRequest = (e) => {
|
|
602
|
+
if (e.context !== this.context || (e.contextTarget ?? e.composedPath()[0]) === this.host) return;
|
|
603
|
+
let t = /* @__PURE__ */ new Set();
|
|
604
|
+
for (let [e, { consumerHost: n }] of this.subscriptions) t.has(e) || (t.add(e), n.dispatchEvent(new g(this.context, n, e, !0)));
|
|
605
|
+
e.stopPropagation();
|
|
606
|
+
}, this.host = e, t.context === void 0 ? this.context = t : this.context = t.context, this.attachListeners(), this.host.addController?.(this);
|
|
607
|
+
}
|
|
608
|
+
attachListeners() {
|
|
609
|
+
this.host.addEventListener("context-request", this.onContextRequest), this.host.addEventListener("context-provider", this.onProviderRequest);
|
|
610
|
+
}
|
|
611
|
+
hostConnected() {
|
|
612
|
+
this.host.dispatchEvent(new b(this.context, this.host));
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
/**
|
|
616
|
+
* @license
|
|
617
|
+
* Copyright 2017 Google LLC
|
|
618
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
619
|
+
*/ function S({ context: e }) {
|
|
620
|
+
return (t, n) => {
|
|
621
|
+
let r = /* @__PURE__ */ new WeakMap();
|
|
622
|
+
if (typeof n == "object") return {
|
|
623
|
+
get() {
|
|
624
|
+
return t.get.call(this);
|
|
625
|
+
},
|
|
626
|
+
set(e) {
|
|
627
|
+
return r.get(this).setValue(e), t.set.call(this, e);
|
|
628
|
+
},
|
|
629
|
+
init(t) {
|
|
630
|
+
return r.set(this, new x(this, {
|
|
631
|
+
context: e,
|
|
632
|
+
initialValue: t
|
|
633
|
+
})), t;
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
{
|
|
637
|
+
t.constructor.addInitializer(((t) => {
|
|
638
|
+
r.set(t, new x(t, { context: e }));
|
|
639
|
+
}));
|
|
640
|
+
let i = Object.getOwnPropertyDescriptor(t, n), a;
|
|
641
|
+
if (i === void 0) {
|
|
642
|
+
let e = /* @__PURE__ */ new WeakMap();
|
|
643
|
+
a = {
|
|
644
|
+
get() {
|
|
645
|
+
return e.get(this);
|
|
646
|
+
},
|
|
647
|
+
set(t) {
|
|
648
|
+
r.get(this).setValue(t), e.set(this, t);
|
|
649
|
+
},
|
|
650
|
+
configurable: !0,
|
|
651
|
+
enumerable: !0
|
|
652
|
+
};
|
|
653
|
+
} else {
|
|
654
|
+
let e = i.set;
|
|
655
|
+
a = {
|
|
656
|
+
...i,
|
|
657
|
+
set(t) {
|
|
658
|
+
r.get(this).setValue(t), e?.call(this, t);
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
Object.defineProperty(t, n, a);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* @license
|
|
669
|
+
* Copyright 2022 Google LLC
|
|
670
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
671
|
+
*/ function C({ context: e, subscribe: t }) {
|
|
672
|
+
return (n, r) => {
|
|
673
|
+
typeof r == "object" ? r.addInitializer((function() {
|
|
674
|
+
new v(this, {
|
|
675
|
+
context: e,
|
|
676
|
+
callback: (e) => {
|
|
677
|
+
n.set.call(this, e);
|
|
678
|
+
},
|
|
679
|
+
subscribe: t
|
|
680
|
+
});
|
|
681
|
+
})) : n.constructor.addInitializer(((n) => {
|
|
682
|
+
new v(n, {
|
|
683
|
+
context: e,
|
|
684
|
+
callback: (e) => {
|
|
685
|
+
n[r] = e;
|
|
686
|
+
},
|
|
687
|
+
subscribe: t
|
|
688
|
+
});
|
|
689
|
+
}));
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* A unique token to identify our context.
|
|
694
|
+
* Any component that uses @consume({context: audioPlayerContext})
|
|
695
|
+
* will automatically receive updates when the nearest <ui-audio-provider> changes its state.
|
|
696
|
+
*/
|
|
697
|
+
const w = _("ui-audio-player-context");
|
|
698
|
+
var T = class extends e {
|
|
699
|
+
constructor(...e) {
|
|
700
|
+
super(...e), this.src = "", this._animationFrameId = 0, this.state = {
|
|
701
|
+
src: "",
|
|
702
|
+
isPlaying: !1,
|
|
703
|
+
isBuffering: !1,
|
|
704
|
+
currentTime: 0,
|
|
705
|
+
duration: 0,
|
|
706
|
+
volume: 1,
|
|
707
|
+
muted: !1,
|
|
708
|
+
analyserNode: void 0,
|
|
709
|
+
play: () => this.play(),
|
|
710
|
+
pause: () => this.pause(),
|
|
711
|
+
togglePlay: () => this._togglePlay(),
|
|
712
|
+
seek: (e) => this._seek(e),
|
|
713
|
+
setVolume: (e) => this._setVolume(e),
|
|
714
|
+
toggleMute: () => this._toggleMute()
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
static {
|
|
718
|
+
this.styles = t`
|
|
719
|
+
:host {
|
|
720
|
+
display: contents; /* We are completely invisible, just wrapping children */
|
|
721
|
+
}
|
|
722
|
+
audio {
|
|
723
|
+
display: none;
|
|
724
|
+
}
|
|
725
|
+
`;
|
|
726
|
+
}
|
|
727
|
+
render() {
|
|
728
|
+
return n`
|
|
729
|
+
<audio
|
|
730
|
+
crossorigin="anonymous"
|
|
731
|
+
src="${this.src}"
|
|
732
|
+
preload="metadata"
|
|
733
|
+
@loadedmetadata="${this._handleLoadedMetadata}"
|
|
734
|
+
@ended="${this._handleEnded}"
|
|
735
|
+
@playing="${this._handlePlaying}"
|
|
736
|
+
@pause="${this._handlePause}"
|
|
737
|
+
@waiting="${() => this._updateState({ isBuffering: !0 })}"
|
|
738
|
+
@canplay="${() => this._updateState({ isBuffering: !1 })}"
|
|
739
|
+
@error="${this._handleError}"
|
|
740
|
+
></audio>
|
|
741
|
+
<slot></slot>
|
|
742
|
+
`;
|
|
743
|
+
}
|
|
744
|
+
willUpdate(e) {
|
|
745
|
+
e.has("src") && this._updateState({
|
|
746
|
+
src: this.src,
|
|
747
|
+
isPlaying: !1,
|
|
748
|
+
currentTime: 0,
|
|
749
|
+
error: void 0
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
updated(e) {
|
|
753
|
+
e.has("src") && this._audioEl && this._audioEl.load();
|
|
754
|
+
}
|
|
755
|
+
disconnectedCallback() {
|
|
756
|
+
super.disconnectedCallback(), this._animationFrameId && cancelAnimationFrame(this._animationFrameId), this._audioContext && this._audioContext.state !== "closed" && this._audioContext.close();
|
|
757
|
+
}
|
|
758
|
+
_updateState(e) {
|
|
759
|
+
this.state = {
|
|
760
|
+
...this.state,
|
|
761
|
+
...e
|
|
762
|
+
}, this.dispatchEvent(new CustomEvent("state-change", {
|
|
763
|
+
detail: this.state,
|
|
764
|
+
bubbles: !0,
|
|
765
|
+
composed: !0
|
|
766
|
+
}));
|
|
767
|
+
}
|
|
768
|
+
_setupAudioContext() {
|
|
769
|
+
if (!(this._audioContext || !this._audioEl)) try {
|
|
770
|
+
this._audioContext = new (window.AudioContext || window.webkitAudioContext)(), this._analyserNode = this._audioContext.createAnalyser(), this._analyserNode.fftSize = 256, this._analyserNode.smoothingTimeConstant = .8, this._mediaSource = this._audioContext.createMediaElementSource(this._audioEl), this._mediaSource.connect(this._analyserNode), this._analyserNode.connect(this._audioContext.destination), this._updateState({ analyserNode: this._analyserNode });
|
|
771
|
+
} catch (e) {
|
|
772
|
+
console.warn("Failed to set up AudioContext for visualizer:", e);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
play() {
|
|
776
|
+
this._audioEl.src && (this._setupAudioContext(), this._audioContext?.state === "suspended" && this._audioContext.resume(), this._audioEl.play().catch((e) => {
|
|
777
|
+
console.error("Error playing audio", e), this._updateState({ error: "Playback failed" });
|
|
778
|
+
}));
|
|
779
|
+
}
|
|
780
|
+
pause() {
|
|
781
|
+
this._audioEl && this._audioEl.pause();
|
|
782
|
+
}
|
|
783
|
+
_togglePlay() {
|
|
784
|
+
this.state.isPlaying ? this.pause() : this.play();
|
|
785
|
+
}
|
|
786
|
+
_seek(e) {
|
|
787
|
+
this._audioEl && (this._audioEl.currentTime = e, this._updateState({ currentTime: e }));
|
|
788
|
+
}
|
|
789
|
+
_setVolume(e) {
|
|
790
|
+
this._audioEl && (this._audioEl.volume = e, this._updateState({
|
|
791
|
+
volume: e,
|
|
792
|
+
muted: e === 0
|
|
793
|
+
}));
|
|
794
|
+
}
|
|
795
|
+
_toggleMute() {
|
|
796
|
+
this._audioEl && (this._audioEl.muted = !this._audioEl.muted, this._updateState({ muted: this._audioEl.muted }));
|
|
797
|
+
}
|
|
798
|
+
_handleLoadedMetadata() {
|
|
799
|
+
this._updateState({ duration: this._audioEl.duration });
|
|
800
|
+
}
|
|
801
|
+
_handleEnded() {
|
|
802
|
+
this._updateState({
|
|
803
|
+
isPlaying: !1,
|
|
804
|
+
currentTime: 0
|
|
805
|
+
}), this._audioEl.currentTime = 0;
|
|
806
|
+
}
|
|
807
|
+
_handlePlaying() {
|
|
808
|
+
this._updateState({
|
|
809
|
+
isPlaying: !0,
|
|
810
|
+
isBuffering: !1,
|
|
811
|
+
error: void 0
|
|
812
|
+
}), this._startTrackingTime();
|
|
813
|
+
}
|
|
814
|
+
_handlePause() {
|
|
815
|
+
this._updateState({ isPlaying: !1 }), this._animationFrameId && cancelAnimationFrame(this._animationFrameId);
|
|
816
|
+
}
|
|
817
|
+
_handleError() {
|
|
818
|
+
this._updateState({
|
|
819
|
+
error: "Error loading audio",
|
|
820
|
+
isPlaying: !1,
|
|
821
|
+
isBuffering: !1
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
_startTrackingTime() {
|
|
825
|
+
let e = () => {
|
|
826
|
+
this._audioEl && this.state.isPlaying && (Math.abs(this.state.currentTime - this._audioEl.currentTime) > .05 && this._updateState({ currentTime: this._audioEl.currentTime }), this._animationFrameId = requestAnimationFrame(e));
|
|
827
|
+
};
|
|
828
|
+
this._animationFrameId = requestAnimationFrame(e);
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
l([i({ type: String })], T.prototype, "src", void 0), l([a("audio")], T.prototype, "_audioEl", void 0), l([S({ context: w }), o()], T.prototype, "state", void 0), T = l([r("ui-audio-provider")], T);
|
|
832
|
+
var E = class extends e {
|
|
833
|
+
static {
|
|
834
|
+
this.styles = t`
|
|
835
|
+
:host {
|
|
836
|
+
display: inline-flex;
|
|
837
|
+
position: relative;
|
|
838
|
+
align-items: center;
|
|
839
|
+
justify-content: center;
|
|
840
|
+
font-family: inherit;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
md-filled-icon-button {
|
|
844
|
+
--md-filled-icon-button-container-color: var(
|
|
845
|
+
--md-sys-color-primary,
|
|
846
|
+
#0066cc
|
|
847
|
+
);
|
|
848
|
+
--md-filled-icon-button-icon-color: var(
|
|
849
|
+
--md-sys-color-on-primary,
|
|
850
|
+
#ffffff
|
|
851
|
+
);
|
|
852
|
+
--md-filled-icon-button-hover-icon-color: var(
|
|
853
|
+
--md-sys-color-on-primary,
|
|
854
|
+
#ffffff
|
|
855
|
+
);
|
|
856
|
+
--md-filled-icon-button-focus-icon-color: var(
|
|
857
|
+
--md-sys-color-on-primary,
|
|
858
|
+
#ffffff
|
|
859
|
+
);
|
|
860
|
+
--md-filled-icon-button-pressed-icon-color: var(
|
|
861
|
+
--md-sys-color-on-primary,
|
|
862
|
+
#ffffff
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
--md-filled-icon-button-toggle-icon-color: var(
|
|
866
|
+
--md-sys-color-on-primary,
|
|
867
|
+
#ffffff
|
|
868
|
+
);
|
|
869
|
+
--md-filled-icon-button-selected-container-color: var(
|
|
870
|
+
--md-sys-color-primary,
|
|
871
|
+
#0066cc
|
|
872
|
+
);
|
|
873
|
+
--md-filled-icon-button-selected-icon-color: var(
|
|
874
|
+
--md-sys-color-on-primary,
|
|
875
|
+
#ffffff
|
|
876
|
+
);
|
|
877
|
+
color: var(--md-sys-color-on-primary, #ffffff);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
md-circular-progress {
|
|
881
|
+
position: absolute;
|
|
882
|
+
--md-circular-progress-size: 48px;
|
|
883
|
+
}
|
|
884
|
+
`;
|
|
885
|
+
}
|
|
886
|
+
render() {
|
|
887
|
+
let e = this.playerState?.isPlaying ?? !1, t = this.playerState?.isBuffering ?? !1;
|
|
888
|
+
return n`
|
|
889
|
+
<md-filled-icon-button
|
|
890
|
+
part="button"
|
|
891
|
+
@click="${this._handleClick}"
|
|
892
|
+
?disabled="${!this.playerState?.src}"
|
|
893
|
+
>
|
|
894
|
+
<md-icon>${e ? "pause" : "play_arrow"}</md-icon>
|
|
895
|
+
</md-filled-icon-button>
|
|
896
|
+
${t && e ? n`<md-circular-progress indeterminate></md-circular-progress>` : ""}
|
|
897
|
+
`;
|
|
898
|
+
}
|
|
899
|
+
_handleClick() {
|
|
900
|
+
this.playerState && this.playerState.togglePlay();
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
l([C({
|
|
904
|
+
context: w,
|
|
905
|
+
subscribe: !0
|
|
906
|
+
}), i({ attribute: !1 })], E.prototype, "playerState", void 0), E = l([r("ui-audio-play-button")], E);
|
|
907
|
+
var D = class extends e {
|
|
908
|
+
constructor(...e) {
|
|
909
|
+
super(...e), this._isDragging = !1, this._dragValue = 0;
|
|
910
|
+
}
|
|
911
|
+
static {
|
|
912
|
+
this.styles = t`
|
|
913
|
+
:host {
|
|
914
|
+
display: flex;
|
|
915
|
+
width: 100%;
|
|
916
|
+
align-items: center;
|
|
917
|
+
min-width: 100px;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
md-slider {
|
|
921
|
+
width: 100%;
|
|
922
|
+
/* Give the slider track better contrast against backgrounds */
|
|
923
|
+
--md-slider-inactive-track-color: var(--md-sys-color-outline, #79747e);
|
|
924
|
+
}
|
|
925
|
+
`;
|
|
926
|
+
}
|
|
927
|
+
render() {
|
|
928
|
+
let e = this.playerState?.duration || 0, t = e === 0 || !this.playerState?.src, r = this._isDragging ? this._dragValue : this.playerState?.currentTime || 0;
|
|
929
|
+
return n`
|
|
930
|
+
<md-slider
|
|
931
|
+
min="0"
|
|
932
|
+
max="${e || 100}"
|
|
933
|
+
value="${r}"
|
|
934
|
+
step="0.1"
|
|
935
|
+
?disabled="${t}"
|
|
936
|
+
@input="${this._handleInput}"
|
|
937
|
+
@change="${this._handleChange}"
|
|
938
|
+
></md-slider>
|
|
939
|
+
`;
|
|
940
|
+
}
|
|
941
|
+
_handleInput(e) {
|
|
942
|
+
this._isDragging = !0, this._dragValue = e.target.value;
|
|
943
|
+
}
|
|
944
|
+
_handleChange(e) {
|
|
945
|
+
this._dragValue = e.target.value, this.playerState && this.playerState.seek(this._dragValue), this._isDragging = !1;
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
l([C({
|
|
949
|
+
context: w,
|
|
950
|
+
subscribe: !0
|
|
951
|
+
}), i({ attribute: !1 })], D.prototype, "playerState", void 0), D = l([r("ui-audio-progress-slider")], D);
|
|
952
|
+
var O = class extends e {
|
|
953
|
+
constructor(...e) {
|
|
954
|
+
super(...e), this.format = "full";
|
|
955
|
+
}
|
|
956
|
+
static {
|
|
957
|
+
this.styles = t`
|
|
958
|
+
:host {
|
|
959
|
+
display: inline-block;
|
|
960
|
+
font-variant-numeric: tabular-nums;
|
|
961
|
+
font-size: 14px;
|
|
962
|
+
color: var(--md-sys-color-on-surface-variant, #444);
|
|
963
|
+
font-family: inherit;
|
|
964
|
+
}
|
|
965
|
+
`;
|
|
966
|
+
}
|
|
967
|
+
render() {
|
|
968
|
+
let e = this.playerState?.currentTime || 0, t = this.playerState?.duration || 0;
|
|
969
|
+
if (this.format === "elapsed") return n`${this._formatTime(e)}`;
|
|
970
|
+
if (this.format === "remaining") {
|
|
971
|
+
let r = Math.max(0, t - e);
|
|
972
|
+
return n`-${this._formatTime(r)}`;
|
|
973
|
+
} else return n`${this._formatTime(e)} /
|
|
974
|
+
${t ? this._formatTime(t) : "--:--"}`;
|
|
975
|
+
}
|
|
976
|
+
_formatTime(e) {
|
|
977
|
+
if (!e || isNaN(e)) return "0:00";
|
|
978
|
+
let t = Math.floor(e / 3600), n = Math.floor(e % 3600 / 60), r = Math.floor(e % 60), i = "";
|
|
979
|
+
return t > 0 && (i += "" + t + ":" + (n < 10 ? "0" : "")), i += "" + n + ":" + (r < 10 ? "0" : ""), i += "" + r, i;
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
l([C({
|
|
983
|
+
context: w,
|
|
984
|
+
subscribe: !0
|
|
985
|
+
}), i({ attribute: !1 })], O.prototype, "playerState", void 0), l([i({ type: String })], O.prototype, "format", void 0), O = l([r("ui-audio-time-display")], O);
|
|
986
|
+
var k = class extends e {
|
|
987
|
+
static {
|
|
988
|
+
this.styles = t`
|
|
989
|
+
:host {
|
|
990
|
+
display: inline-block;
|
|
991
|
+
width: 100%;
|
|
992
|
+
max-width: 400px;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.player-pill {
|
|
996
|
+
display: flex;
|
|
997
|
+
align-items: center;
|
|
998
|
+
gap: 16px;
|
|
999
|
+
padding: 12px 24px;
|
|
1000
|
+
background: var(--md-sys-color-surface-container-high, #e2e2e2);
|
|
1001
|
+
border-radius: 999px; /* Pill shape */
|
|
1002
|
+
width: fit-content;
|
|
1003
|
+
font-family: inherit;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.time-container {
|
|
1007
|
+
min-width: 85px; /* prevent jitter when times change */
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
.slider-container {
|
|
1011
|
+
width: 200px;
|
|
1012
|
+
display: flex;
|
|
1013
|
+
align-items: center;
|
|
1014
|
+
}
|
|
1015
|
+
`;
|
|
1016
|
+
}
|
|
1017
|
+
render() {
|
|
1018
|
+
return n`
|
|
1019
|
+
<ui-audio-provider .src="${this.item?.src || ""}">
|
|
1020
|
+
<div class="player-pill" part="container">
|
|
1021
|
+
<!-- Atomic Play/Pause Button -->
|
|
1022
|
+
<ui-audio-play-button></ui-audio-play-button>
|
|
1023
|
+
|
|
1024
|
+
<!-- Atomic Time Display (Full format: 0:00 / 0:00) -->
|
|
1025
|
+
<div class="time-container">
|
|
1026
|
+
<ui-audio-time-display format="full"></ui-audio-time-display>
|
|
1027
|
+
</div>
|
|
1028
|
+
|
|
1029
|
+
<!-- Atomic Slider -->
|
|
1030
|
+
<div class="slider-container">
|
|
1031
|
+
<ui-audio-progress-slider></ui-audio-progress-slider>
|
|
1032
|
+
</div>
|
|
1033
|
+
</div>
|
|
1034
|
+
</ui-audio-provider>
|
|
1035
|
+
`;
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
l([i({ type: Object })], k.prototype, "item", void 0), k = l([r("ui-audio-player")], k);
|
|
1039
|
+
/**
|
|
1040
|
+
* Copyright 2026 Google LLC
|
|
1041
|
+
*
|
|
1042
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1043
|
+
* you may not use this file except in compliance with the License.
|
|
1044
|
+
* You may obtain a copy of the License at
|
|
1045
|
+
*
|
|
1046
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1047
|
+
*
|
|
1048
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1049
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1050
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1051
|
+
* See the License for the specific language governing permissions and
|
|
1052
|
+
* limitations under the License.
|
|
1053
|
+
*/
|
|
1054
|
+
var A = class extends e {
|
|
1055
|
+
constructor(...e) {
|
|
1056
|
+
super(...e), this.muted = !1, this.disabled = !1, this._devices = [], this._loading = !0, this._error = null, this._hasPermission = !1, this._isMenuOpen = !1, this._handleDeviceChange = () => {
|
|
1057
|
+
this._hasPermission ? this._loadDevicesWithPermission() : this._loadDevicesWithoutPermission();
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
static {
|
|
1061
|
+
this.styles = t`
|
|
1062
|
+
:host {
|
|
1063
|
+
display: inline-block;
|
|
1064
|
+
position: relative;
|
|
1065
|
+
font-family: inherit;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
.anchor-button {
|
|
1069
|
+
display: flex;
|
|
1070
|
+
align-items: center;
|
|
1071
|
+
gap: 8px;
|
|
1072
|
+
padding: 8px 16px;
|
|
1073
|
+
background: var(--md-sys-color-surface-container-high, #e2e2e2);
|
|
1074
|
+
color: var(--md-sys-color-on-surface, #1e1e1e);
|
|
1075
|
+
border-radius: 999px;
|
|
1076
|
+
cursor: pointer;
|
|
1077
|
+
border: none;
|
|
1078
|
+
font-family: inherit;
|
|
1079
|
+
font-size: 14px;
|
|
1080
|
+
font-weight: 500;
|
|
1081
|
+
transition: background-color 0.2s;
|
|
1082
|
+
max-width: 250px;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
.anchor-button:hover:not(:disabled) {
|
|
1086
|
+
background: var(--md-sys-color-surface-container-highest, #e3e3e3);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.anchor-button:disabled {
|
|
1090
|
+
opacity: 0.5;
|
|
1091
|
+
cursor: not-allowed;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
.label-text {
|
|
1095
|
+
flex: 1;
|
|
1096
|
+
white-space: nowrap;
|
|
1097
|
+
overflow: hidden;
|
|
1098
|
+
text-overflow: ellipsis;
|
|
1099
|
+
text-align: left;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
md-menu {
|
|
1103
|
+
--md-menu-container-color: var(
|
|
1104
|
+
--md-sys-color-surface-container,
|
|
1105
|
+
var(--md-sys-color-surface, #ffffff)
|
|
1106
|
+
);
|
|
1107
|
+
--md-menu-container-shape: 12px;
|
|
1108
|
+
min-width: 280px;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.menu-footer {
|
|
1112
|
+
display: flex;
|
|
1113
|
+
align-items: center;
|
|
1114
|
+
justify-content: space-between;
|
|
1115
|
+
padding: 8px 12px;
|
|
1116
|
+
gap: 12px;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
.preview-waveform {
|
|
1120
|
+
flex: 1;
|
|
1121
|
+
height: 24px;
|
|
1122
|
+
background: var(
|
|
1123
|
+
--md-sys-color-surface-variant,
|
|
1124
|
+
var(--md-sys-color-surface-container-highest, #e1e2e1)
|
|
1125
|
+
);
|
|
1126
|
+
border-radius: 6px;
|
|
1127
|
+
overflow: hidden;
|
|
1128
|
+
display: flex;
|
|
1129
|
+
align-items: center;
|
|
1130
|
+
padding: 0 4px;
|
|
1131
|
+
}
|
|
1132
|
+
`;
|
|
1133
|
+
}
|
|
1134
|
+
connectedCallback() {
|
|
1135
|
+
super.connectedCallback(), this._loadDevicesWithoutPermission(), navigator.mediaDevices.addEventListener("devicechange", this._handleDeviceChange);
|
|
1136
|
+
}
|
|
1137
|
+
disconnectedCallback() {
|
|
1138
|
+
super.disconnectedCallback(), navigator.mediaDevices.removeEventListener("devicechange", this._handleDeviceChange), this._stopPreview();
|
|
1139
|
+
}
|
|
1140
|
+
updated(e) {
|
|
1141
|
+
super.updated(e), e.has("_isMenuOpen") && this._isMenuOpen && (!this._hasPermission && !this._loading ? this._loadDevicesWithPermission() : this._hasPermission && !this.muted && this._startPreview()), (e.has("_isMenuOpen") && !this._isMenuOpen || e.has("muted") && this.muted) && this._stopPreview(), e.has("muted") && !this.muted && this._isMenuOpen && this._hasPermission && this._startPreview();
|
|
1142
|
+
}
|
|
1143
|
+
render() {
|
|
1144
|
+
let e = this._devices.find((e) => e.deviceId === this.value) || this._devices[0] || { label: this._loading ? "Loading..." : "No microphone" };
|
|
1145
|
+
return n`
|
|
1146
|
+
<!-- Anchor Button -->
|
|
1147
|
+
<button
|
|
1148
|
+
id="anchor-button"
|
|
1149
|
+
class="anchor-button"
|
|
1150
|
+
?disabled=${this._loading || this.disabled}
|
|
1151
|
+
@click=${this._toggleMenu}
|
|
1152
|
+
>
|
|
1153
|
+
<md-icon>${this.muted ? "mic_off" : "mic"}</md-icon>
|
|
1154
|
+
<span class="label-text">${e.label}</span>
|
|
1155
|
+
<md-icon style="font-size: 18px;">unfold_more</md-icon>
|
|
1156
|
+
</button>
|
|
1157
|
+
|
|
1158
|
+
<!-- Dropdown Menu -->
|
|
1159
|
+
<md-menu
|
|
1160
|
+
id="device-menu"
|
|
1161
|
+
anchor="anchor-button"
|
|
1162
|
+
positioning="popover"
|
|
1163
|
+
@closed=${() => this._isMenuOpen = !1}
|
|
1164
|
+
@opened=${() => this._isMenuOpen = !0}
|
|
1165
|
+
>
|
|
1166
|
+
${this._loading ? n`<md-menu-item disabled
|
|
1167
|
+
><div slot="headline">Loading devices...</div></md-menu-item
|
|
1168
|
+
>` : this._error ? n`<md-menu-item disabled
|
|
1169
|
+
><div slot="headline" style="color: var(--md-sys-color-error)">
|
|
1170
|
+
${this._error}
|
|
1171
|
+
</div></md-menu-item
|
|
1172
|
+
>` : this._devices.map((e) => n`
|
|
1173
|
+
<md-menu-item
|
|
1174
|
+
@click=${() => this._selectDevice(e.deviceId)}
|
|
1175
|
+
?selected=${this.value === e.deviceId || !this.value && this._devices[0]?.deviceId === e.deviceId}
|
|
1176
|
+
>
|
|
1177
|
+
<div slot="headline">${e.label}</div>
|
|
1178
|
+
${this.value === e.deviceId || !this.value && this._devices[0]?.deviceId === e.deviceId ? n`<md-icon slot="end">check</md-icon>` : ""}
|
|
1179
|
+
</md-menu-item>
|
|
1180
|
+
`)}
|
|
1181
|
+
${this._devices.length > 0 ? n`
|
|
1182
|
+
<md-divider></md-divider>
|
|
1183
|
+
<div class="menu-footer">
|
|
1184
|
+
<md-text-button @click=${this._toggleMute}>
|
|
1185
|
+
<md-icon slot="icon"
|
|
1186
|
+
>${this.muted ? "mic_off" : "mic"}</md-icon
|
|
1187
|
+
>
|
|
1188
|
+
${this.muted ? "Unmute" : "Mute"}
|
|
1189
|
+
</md-text-button>
|
|
1190
|
+
|
|
1191
|
+
<div class="preview-waveform">
|
|
1192
|
+
<ui-live-waveform
|
|
1193
|
+
.active=${this._isMenuOpen && !this.muted && this._hasPermission}
|
|
1194
|
+
.processing=${!1}
|
|
1195
|
+
.analyserNode=${this._previewAnalyser}
|
|
1196
|
+
.barWidth=${3}
|
|
1197
|
+
.barGap=${1}
|
|
1198
|
+
.fadeEdges=${!1}
|
|
1199
|
+
height="16"
|
|
1200
|
+
></ui-live-waveform>
|
|
1201
|
+
</div>
|
|
1202
|
+
</div>
|
|
1203
|
+
` : ""}
|
|
1204
|
+
</md-menu>
|
|
1205
|
+
`;
|
|
1206
|
+
}
|
|
1207
|
+
_toggleMenu() {
|
|
1208
|
+
this._menuEl && (this._menuEl.open = !this._menuEl.open, this._isMenuOpen = this._menuEl.open);
|
|
1209
|
+
}
|
|
1210
|
+
_selectDevice(e) {
|
|
1211
|
+
this.value = e, this.dispatchEvent(new CustomEvent("device-change", {
|
|
1212
|
+
detail: { deviceId: e },
|
|
1213
|
+
bubbles: !0,
|
|
1214
|
+
composed: !0
|
|
1215
|
+
})), this._isMenuOpen && !this.muted && this._hasPermission && this._startPreview();
|
|
1216
|
+
}
|
|
1217
|
+
_toggleMute(e) {
|
|
1218
|
+
e.stopPropagation(), this.muted = !this.muted, this.dispatchEvent(new CustomEvent("mute-change", {
|
|
1219
|
+
detail: { muted: this.muted },
|
|
1220
|
+
bubbles: !0,
|
|
1221
|
+
composed: !0
|
|
1222
|
+
}));
|
|
1223
|
+
}
|
|
1224
|
+
async _loadDevicesWithoutPermission() {
|
|
1225
|
+
try {
|
|
1226
|
+
this._loading = !0, this._error = null;
|
|
1227
|
+
let e = await navigator.mediaDevices.enumerateDevices();
|
|
1228
|
+
this._parseDeviceList(e);
|
|
1229
|
+
} catch (e) {
|
|
1230
|
+
this._error = e instanceof Error ? e.message : "Failed to get audio devices";
|
|
1231
|
+
} finally {
|
|
1232
|
+
this._loading = !1;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
async _loadDevicesWithPermission() {
|
|
1236
|
+
if (!this._loading) try {
|
|
1237
|
+
this._loading = !0, this._error = null, (await navigator.mediaDevices.getUserMedia({ audio: !0 })).getTracks().forEach((e) => e.stop());
|
|
1238
|
+
let e = await navigator.mediaDevices.enumerateDevices();
|
|
1239
|
+
this._parseDeviceList(e), this._hasPermission = !0, this._isMenuOpen && !this.muted && this._startPreview();
|
|
1240
|
+
} catch (e) {
|
|
1241
|
+
this._error = e instanceof Error ? e.message : "Permission denied";
|
|
1242
|
+
} finally {
|
|
1243
|
+
this._loading = !1;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
_parseDeviceList(e) {
|
|
1247
|
+
let t = e.filter((e) => e.kind === "audioinput").map((e) => {
|
|
1248
|
+
let t = e.label || "Microphone ${device.deviceId.slice(0, 8)}";
|
|
1249
|
+
return t = t.replace(/\s*\([^)]*\)/g, "").trim(), {
|
|
1250
|
+
deviceId: e.deviceId,
|
|
1251
|
+
label: t,
|
|
1252
|
+
groupId: e.groupId
|
|
1253
|
+
};
|
|
1254
|
+
});
|
|
1255
|
+
this._devices = t, !this.value && t.length > 0 && (this.value = t[0].deviceId, this.dispatchEvent(new CustomEvent("device-change", {
|
|
1256
|
+
detail: { deviceId: this.value },
|
|
1257
|
+
bubbles: !0,
|
|
1258
|
+
composed: !0
|
|
1259
|
+
})));
|
|
1260
|
+
}
|
|
1261
|
+
async _startPreview() {
|
|
1262
|
+
if (this._stopPreview(), this.value) try {
|
|
1263
|
+
this._previewStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: this.value } } }), this._previewAudioContext = new (window.AudioContext || window.webkitAudioContext)(), this._previewAnalyser = this._previewAudioContext.createAnalyser(), this._previewAnalyser.fftSize = 256, this._previewAnalyser.smoothingTimeConstant = .8, this._previewAudioContext.createMediaStreamSource(this._previewStream).connect(this._previewAnalyser);
|
|
1264
|
+
} catch (e) {
|
|
1265
|
+
console.warn("Failed to start preview stream", e);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
_stopPreview() {
|
|
1269
|
+
this._previewStream &&= (this._previewStream.getTracks().forEach((e) => e.stop()), void 0), this._previewAudioContext && this._previewAudioContext.state !== "closed" && (this._previewAudioContext.close(), this._previewAudioContext = void 0), this._previewAnalyser = void 0;
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
l([i({ type: String })], A.prototype, "value", void 0), l([i({ type: Boolean })], A.prototype, "muted", void 0), l([i({ type: Boolean })], A.prototype, "disabled", void 0), l([o()], A.prototype, "_devices", void 0), l([o()], A.prototype, "_loading", void 0), l([o()], A.prototype, "_error", void 0), l([o()], A.prototype, "_hasPermission", void 0), l([o()], A.prototype, "_isMenuOpen", void 0), l([o()], A.prototype, "_previewAnalyser", void 0), l([a("md-menu")], A.prototype, "_menuEl", void 0), A = l([r("ui-mic-selector")], A);
|
|
1273
|
+
/**
|
|
1274
|
+
* Copyright 2026 Google LLC
|
|
1275
|
+
*
|
|
1276
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1277
|
+
* you may not use this file except in compliance with the License.
|
|
1278
|
+
* You may obtain a copy of the License at
|
|
1279
|
+
*
|
|
1280
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1281
|
+
*
|
|
1282
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1283
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1284
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1285
|
+
* See the License for the specific language governing permissions and
|
|
1286
|
+
* limitations under the License.
|
|
1287
|
+
*/
|
|
1288
|
+
var j = class extends e {
|
|
1289
|
+
constructor(...e) {
|
|
1290
|
+
super(...e), this.voices = [], this.placeholder = "Select a voice...", this.idKey = "voiceId", this.titleKey = "name", this.subtitleKey = "category", this.previewUrlKey = "previewUrl", this.useOrbs = !1, this.colorKey = "colors", this._searchQuery = "";
|
|
1291
|
+
}
|
|
1292
|
+
static {
|
|
1293
|
+
this.styles = t`
|
|
1294
|
+
:host {
|
|
1295
|
+
display: inline-block;
|
|
1296
|
+
width: 100%;
|
|
1297
|
+
font-family: inherit;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.anchor-button {
|
|
1301
|
+
display: flex;
|
|
1302
|
+
align-items: center;
|
|
1303
|
+
justify-content: space-between;
|
|
1304
|
+
width: 100%;
|
|
1305
|
+
padding: 8px 16px;
|
|
1306
|
+
background: transparent;
|
|
1307
|
+
border: 1px solid var(--md-sys-color-outline, #79747e);
|
|
1308
|
+
border-radius: 8px;
|
|
1309
|
+
color: var(--md-sys-color-on-surface, #1e1e1e);
|
|
1310
|
+
cursor: pointer;
|
|
1311
|
+
font-family: inherit;
|
|
1312
|
+
font-size: 14px;
|
|
1313
|
+
min-height: 48px;
|
|
1314
|
+
transition:
|
|
1315
|
+
background-color 0.2s,
|
|
1316
|
+
border-color 0.2s;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
.anchor-button:hover {
|
|
1320
|
+
background: var(--md-sys-color-surface-container-highest, #e3e3e3);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
.anchor-button:focus-visible {
|
|
1324
|
+
outline: none;
|
|
1325
|
+
border-color: var(--md-sys-color-primary, #0066cc);
|
|
1326
|
+
box-shadow: 0 0 0 1px var(--md-sys-color-primary, #0066cc);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
.trigger-content {
|
|
1330
|
+
display: flex;
|
|
1331
|
+
align-items: center;
|
|
1332
|
+
justify-content: space-between;
|
|
1333
|
+
width: 100%;
|
|
1334
|
+
min-width: 100%;
|
|
1335
|
+
padding: 4px 0;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
.trigger-left {
|
|
1339
|
+
display: flex;
|
|
1340
|
+
align-items: center;
|
|
1341
|
+
gap: 12px;
|
|
1342
|
+
overflow: hidden;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
.trigger-icon {
|
|
1346
|
+
width: 24px;
|
|
1347
|
+
height: 24px;
|
|
1348
|
+
border-radius: 50%;
|
|
1349
|
+
background: var(--md-sys-color-primary-container);
|
|
1350
|
+
color: var(--md-sys-color-on-primary-container);
|
|
1351
|
+
display: flex;
|
|
1352
|
+
align-items: center;
|
|
1353
|
+
justify-content: center;
|
|
1354
|
+
flex-shrink: 0;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.trigger-text {
|
|
1358
|
+
white-space: nowrap;
|
|
1359
|
+
overflow: hidden;
|
|
1360
|
+
text-overflow: ellipsis;
|
|
1361
|
+
color: var(--md-sys-color-on-surface);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
md-menu {
|
|
1365
|
+
--md-menu-container-shape: 12px;
|
|
1366
|
+
--md-menu-container-color: var(--md-sys-color-surface-container, #f3f3f3);
|
|
1367
|
+
max-width: 400px;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
.search-container {
|
|
1371
|
+
padding: 8px 12px;
|
|
1372
|
+
background: var(--md-sys-color-surface-container, #f3f3f3);
|
|
1373
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
md-outlined-text-field {
|
|
1377
|
+
width: 100%;
|
|
1378
|
+
--md-outlined-text-field-container-shape: 8px;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
md-menu-item {
|
|
1382
|
+
--md-menu-item-hover-state-layer-color: var(
|
|
1383
|
+
--md-sys-color-on-surface-variant
|
|
1384
|
+
);
|
|
1385
|
+
--md-menu-item-focus-state-layer-color: var(
|
|
1386
|
+
--md-sys-color-on-surface-variant
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
.voice-item-content {
|
|
1391
|
+
display: flex;
|
|
1392
|
+
align-items: center;
|
|
1393
|
+
gap: 16px;
|
|
1394
|
+
width: 100%;
|
|
1395
|
+
padding: 8px 0;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
.voice-avatar {
|
|
1399
|
+
position: relative;
|
|
1400
|
+
width: 32px;
|
|
1401
|
+
height: 32px;
|
|
1402
|
+
border-radius: 50%;
|
|
1403
|
+
background: var(--md-sys-color-surface-variant);
|
|
1404
|
+
display: flex;
|
|
1405
|
+
align-items: center;
|
|
1406
|
+
justify-content: center;
|
|
1407
|
+
flex-shrink: 0;
|
|
1408
|
+
cursor: pointer;
|
|
1409
|
+
overflow: hidden;
|
|
1410
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
1411
|
+
z-index: 2; /* Keep above the menu item ripple */
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
.voice-avatar:hover .play-overlay {
|
|
1415
|
+
opacity: 1;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
.play-overlay {
|
|
1419
|
+
position: absolute;
|
|
1420
|
+
inset: 0;
|
|
1421
|
+
background: rgba(0, 0, 0, 0.4);
|
|
1422
|
+
display: flex;
|
|
1423
|
+
align-items: center;
|
|
1424
|
+
justify-content: center;
|
|
1425
|
+
color: white;
|
|
1426
|
+
opacity: 0;
|
|
1427
|
+
transition: opacity 0.2s;
|
|
1428
|
+
border-radius: 50%;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
.play-overlay.active {
|
|
1432
|
+
opacity: 1;
|
|
1433
|
+
background: rgba(0, 0, 0, 0.6);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
.voice-info {
|
|
1437
|
+
display: flex;
|
|
1438
|
+
flex-direction: column;
|
|
1439
|
+
gap: 2px;
|
|
1440
|
+
flex: 1;
|
|
1441
|
+
overflow: hidden;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
.voice-name {
|
|
1445
|
+
font-weight: 500;
|
|
1446
|
+
font-size: 14px;
|
|
1447
|
+
white-space: nowrap;
|
|
1448
|
+
overflow: hidden;
|
|
1449
|
+
text-overflow: ellipsis;
|
|
1450
|
+
color: var(--md-sys-color-on-surface);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
.voice-labels {
|
|
1454
|
+
display: flex;
|
|
1455
|
+
align-items: center;
|
|
1456
|
+
gap: 6px;
|
|
1457
|
+
font-size: 12px;
|
|
1458
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
1459
|
+
white-space: nowrap;
|
|
1460
|
+
overflow: hidden;
|
|
1461
|
+
text-overflow: ellipsis;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
.label-dot {
|
|
1465
|
+
font-size: 8px;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
.empty-state {
|
|
1469
|
+
padding: 24px;
|
|
1470
|
+
text-align: center;
|
|
1471
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
1472
|
+
font-size: 14px;
|
|
1473
|
+
}
|
|
1474
|
+
`;
|
|
1475
|
+
}
|
|
1476
|
+
render() {
|
|
1477
|
+
let e = this.voices.find((e) => e[this.idKey] === this.value), t = this.voices.filter((e) => {
|
|
1478
|
+
if (!this._searchQuery) return !0;
|
|
1479
|
+
let t = this._searchQuery.toLowerCase();
|
|
1480
|
+
return e[this.titleKey].toLowerCase().includes(t) || e.labels?.accent?.toLowerCase().includes(t) || e.labels?.gender?.toLowerCase().includes(t) || e.labels?.age?.toLowerCase().includes(t);
|
|
1481
|
+
});
|
|
1482
|
+
return n`
|
|
1483
|
+
<!-- Hidden audio player for previews -->
|
|
1484
|
+
<audio
|
|
1485
|
+
crossorigin="anonymous"
|
|
1486
|
+
@ended=${() => this._previewingVoiceId = void 0}
|
|
1487
|
+
@pause=${() => this._previewingVoiceId = void 0}
|
|
1488
|
+
></audio>
|
|
1489
|
+
|
|
1490
|
+
<!-- Anchor Button -->
|
|
1491
|
+
<button
|
|
1492
|
+
class="anchor-button"
|
|
1493
|
+
part="button"
|
|
1494
|
+
id="voice-anchor"
|
|
1495
|
+
@click=${this._toggleMenu}
|
|
1496
|
+
>
|
|
1497
|
+
<div class="trigger-content">
|
|
1498
|
+
<div class="trigger-left">
|
|
1499
|
+
${e ? n`
|
|
1500
|
+
<div
|
|
1501
|
+
class="trigger-icon"
|
|
1502
|
+
style="${this.useOrbs ? "overflow: hidden;" : ""}"
|
|
1503
|
+
>
|
|
1504
|
+
${this.useOrbs ? n`<ui-orb
|
|
1505
|
+
agentState="listening"
|
|
1506
|
+
.colors="${e[this.colorKey] || ["#CADCFC", "#A0B9D1"]}"
|
|
1507
|
+
></ui-orb>` : n`<md-icon style="font-size: 16px;"
|
|
1508
|
+
>record_voice_over</md-icon
|
|
1509
|
+
>`}
|
|
1510
|
+
</div>
|
|
1511
|
+
<span class="trigger-text"
|
|
1512
|
+
>${e[this.titleKey] || e.name}</span
|
|
1513
|
+
>
|
|
1514
|
+
` : n`
|
|
1515
|
+
<span
|
|
1516
|
+
class="trigger-text"
|
|
1517
|
+
style="color: var(--md-sys-color-on-surface-variant)"
|
|
1518
|
+
>${this.placeholder}</span
|
|
1519
|
+
>
|
|
1520
|
+
`}
|
|
1521
|
+
</div>
|
|
1522
|
+
<md-icon style="color: var(--md-sys-color-on-surface-variant);"
|
|
1523
|
+
>unfold_more</md-icon
|
|
1524
|
+
>
|
|
1525
|
+
</div>
|
|
1526
|
+
</button>
|
|
1527
|
+
|
|
1528
|
+
<!-- Dropdown Menu -->
|
|
1529
|
+
<md-menu
|
|
1530
|
+
id="voice-menu"
|
|
1531
|
+
anchor="voice-anchor"
|
|
1532
|
+
positioning="popover"
|
|
1533
|
+
@closed=${this._handleMenuClosed}
|
|
1534
|
+
>
|
|
1535
|
+
<!-- The click.stop modifier stops the menu from closing when searching -->
|
|
1536
|
+
<div
|
|
1537
|
+
class="search-container"
|
|
1538
|
+
@click=${(e) => e.stopPropagation()}
|
|
1539
|
+
>
|
|
1540
|
+
<md-outlined-text-field
|
|
1541
|
+
placeholder="Search voices..."
|
|
1542
|
+
.value=${this._searchQuery}
|
|
1543
|
+
@input=${(e) => this._searchQuery = e.target.value}
|
|
1544
|
+
>
|
|
1545
|
+
<md-icon slot="leading-icon">search</md-icon>
|
|
1546
|
+
</md-outlined-text-field>
|
|
1547
|
+
</div>
|
|
1548
|
+
|
|
1549
|
+
${t.length === 0 ? n` <div class="empty-state">No voice found.</div> ` : t.map((e) => n`
|
|
1550
|
+
<md-menu-item
|
|
1551
|
+
@click=${() => this._selectVoice(e[this.idKey])}
|
|
1552
|
+
?selected=${this.value === e[this.idKey]}
|
|
1553
|
+
>
|
|
1554
|
+
<div slot="headline" class="voice-item-content">
|
|
1555
|
+
<!-- Avatar / Preview Button -->
|
|
1556
|
+
<div
|
|
1557
|
+
class="voice-avatar"
|
|
1558
|
+
@click=${(t) => this._togglePreview(t, e)}
|
|
1559
|
+
>
|
|
1560
|
+
${this.useOrbs ? n`<ui-orb
|
|
1561
|
+
agentState="${this._previewingVoiceId === e[this.idKey] ? "talking" : "listening"}"
|
|
1562
|
+
.colors="${e[this.colorKey] || ["#CADCFC", "#A0B9D1"]}"
|
|
1563
|
+
></ui-orb>` : n`<md-icon style="font-size: 18px;"
|
|
1564
|
+
>face</md-icon
|
|
1565
|
+
>`}
|
|
1566
|
+
${e[this.previewUrlKey] ? n`
|
|
1567
|
+
<div
|
|
1568
|
+
class="play-overlay ${this._previewingVoiceId === e[this.idKey] ? "active" : ""}"
|
|
1569
|
+
>
|
|
1570
|
+
<md-icon style="font-size: 16px;">
|
|
1571
|
+
${this._previewingVoiceId === e[this.idKey] ? "pause" : "play_arrow"}
|
|
1572
|
+
</md-icon>
|
|
1573
|
+
</div>
|
|
1574
|
+
` : ""}
|
|
1575
|
+
</div>
|
|
1576
|
+
|
|
1577
|
+
<!-- Voice Info -->
|
|
1578
|
+
<div class="voice-info">
|
|
1579
|
+
<span class="voice-name">${e[this.titleKey]}</span>
|
|
1580
|
+
${e[this.subtitleKey] || e.labels ? n`
|
|
1581
|
+
<div class="voice-labels">
|
|
1582
|
+
${e[this.subtitleKey] ? n`<span class="voice-badge"
|
|
1583
|
+
>${e[this.subtitleKey]}</span
|
|
1584
|
+
>` : Object.values(e.labels || {}).filter(Boolean).map((e) => n`<span class="voice-badge"
|
|
1585
|
+
>${e}</span
|
|
1586
|
+
>`)}
|
|
1587
|
+
</div>
|
|
1588
|
+
` : ""}
|
|
1589
|
+
</div>
|
|
1590
|
+
</div>
|
|
1591
|
+
|
|
1592
|
+
${this.value === e[this.idKey] ? n`<md-icon slot="end">check</md-icon>` : ""}
|
|
1593
|
+
</md-menu-item>
|
|
1594
|
+
`)}
|
|
1595
|
+
</md-menu>
|
|
1596
|
+
`;
|
|
1597
|
+
}
|
|
1598
|
+
_toggleMenu() {
|
|
1599
|
+
this._menuEl && (this._menuEl.open = !this._menuEl.open);
|
|
1600
|
+
}
|
|
1601
|
+
_handleMenuClosed() {
|
|
1602
|
+
this._stopPreview();
|
|
1603
|
+
}
|
|
1604
|
+
_selectVoice(e) {
|
|
1605
|
+
this.value = e, this.dispatchEvent(new CustomEvent("voice-change", {
|
|
1606
|
+
detail: { voiceId: e },
|
|
1607
|
+
bubbles: !0,
|
|
1608
|
+
composed: !0
|
|
1609
|
+
}));
|
|
1610
|
+
}
|
|
1611
|
+
_togglePreview(e, t) {
|
|
1612
|
+
e.stopPropagation(), e.preventDefault(), !(!t[this.previewUrlKey] || !this._audioEl) && (this._previewingVoiceId === t[this.idKey] ? this._stopPreview() : (this._audioEl.src = t[this.previewUrlKey], this._audioEl.play().catch(console.error), this._previewingVoiceId = t[this.idKey]));
|
|
1613
|
+
}
|
|
1614
|
+
_stopPreview() {
|
|
1615
|
+
this._audioEl && (this._audioEl.pause(), this._audioEl.currentTime = 0), this._previewingVoiceId = void 0;
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
l([i({ type: Array })], j.prototype, "voices", void 0), l([i({ type: String })], j.prototype, "value", void 0), l([i({ type: String })], j.prototype, "placeholder", void 0), l([i({ type: String })], j.prototype, "idKey", void 0), l([i({ type: String })], j.prototype, "titleKey", void 0), l([i({ type: String })], j.prototype, "subtitleKey", void 0), l([i({ type: String })], j.prototype, "previewUrlKey", void 0), l([i({ type: Boolean })], j.prototype, "useOrbs", void 0), l([i({ type: String })], j.prototype, "colorKey", void 0), l([o()], j.prototype, "_searchQuery", void 0), l([o()], j.prototype, "_previewingVoiceId", void 0), l([a("md-menu")], j.prototype, "_menuEl", void 0), l([a("audio")], j.prototype, "_audioEl", void 0), j = l([r("ui-voice-picker")], j);
|
|
1619
|
+
var M = class extends e {
|
|
1620
|
+
constructor(...e) {
|
|
1621
|
+
super(...e), this.speed = 50, this.barCount = 60, this.barWidth = 4, this.barHeight = 4, this.barGap = 2, this.barRadius = 2, this.fadeEdges = !0, this.fadeWidth = 24, this.height = 128, this.active = !0, this._animationFrameId = 0, this._lastTime = 0, this._bars = [], this._seed = Math.random(), this._dataIndex = 0;
|
|
1622
|
+
}
|
|
1623
|
+
static {
|
|
1624
|
+
this.styles = t`
|
|
1625
|
+
:host {
|
|
1626
|
+
display: block;
|
|
1627
|
+
width: 100%;
|
|
1628
|
+
}
|
|
1629
|
+
.container {
|
|
1630
|
+
position: relative;
|
|
1631
|
+
width: 100%;
|
|
1632
|
+
overflow: hidden;
|
|
1633
|
+
}
|
|
1634
|
+
canvas {
|
|
1635
|
+
position: absolute;
|
|
1636
|
+
top: 0;
|
|
1637
|
+
left: 0;
|
|
1638
|
+
display: block;
|
|
1639
|
+
height: 100%;
|
|
1640
|
+
width: 100%;
|
|
1641
|
+
}
|
|
1642
|
+
`;
|
|
1643
|
+
}
|
|
1644
|
+
render() {
|
|
1645
|
+
return n`
|
|
1646
|
+
<div class="container" style="height: ${this.height}px;">
|
|
1647
|
+
<canvas></canvas>
|
|
1648
|
+
</div>
|
|
1649
|
+
`;
|
|
1650
|
+
}
|
|
1651
|
+
firstUpdated() {
|
|
1652
|
+
this._resizeObserver = new ResizeObserver(() => {
|
|
1653
|
+
this._handleResize();
|
|
1654
|
+
}), this._resizeObserver.observe(this._container), this._canvas && this._container && this._populateInitialBars(), this._startAnimation();
|
|
1655
|
+
}
|
|
1656
|
+
disconnectedCallback() {
|
|
1657
|
+
super.disconnectedCallback(), this._resizeObserver && this._resizeObserver.disconnect(), this._animationFrameId && cancelAnimationFrame(this._animationFrameId);
|
|
1658
|
+
}
|
|
1659
|
+
_handleResize() {
|
|
1660
|
+
if (!this._canvas || !this._container) return;
|
|
1661
|
+
let e = this._container.getBoundingClientRect(), t = window.devicePixelRatio || 1;
|
|
1662
|
+
this._canvas.width = e.width * t, this._canvas.height = e.height * t, this._canvas.style.width = `${e.width}px`, this._canvas.style.height = `${e.height}px`;
|
|
1663
|
+
let n = this._canvas.getContext("2d");
|
|
1664
|
+
n && n.scale(t, t);
|
|
1665
|
+
}
|
|
1666
|
+
_seededRandom(e) {
|
|
1667
|
+
let t = Math.sin(this._seed * 1e4 + e * 137.5) * 1e4;
|
|
1668
|
+
return t - Math.floor(t);
|
|
1669
|
+
}
|
|
1670
|
+
_populateInitialBars() {
|
|
1671
|
+
let e = this._container.getBoundingClientRect(), t = this.barWidth + this.barGap, n = e.width, r = 0;
|
|
1672
|
+
for (this._bars = []; n > -t;) this._bars.push({
|
|
1673
|
+
x: n,
|
|
1674
|
+
height: .2 + this._seededRandom(r++) * .6
|
|
1675
|
+
}), n -= t;
|
|
1676
|
+
this._bars.reverse();
|
|
1677
|
+
}
|
|
1678
|
+
_startAnimation() {
|
|
1679
|
+
this._lastTime = performance.now();
|
|
1680
|
+
let e = (t) => {
|
|
1681
|
+
if (!this._canvas) return;
|
|
1682
|
+
let n = this._canvas.getContext("2d");
|
|
1683
|
+
if (!n) return;
|
|
1684
|
+
let r = this._lastTime ? (t - this._lastTime) / 1e3 : 0;
|
|
1685
|
+
this._lastTime = t;
|
|
1686
|
+
let i = this._canvas.getBoundingClientRect();
|
|
1687
|
+
n.clearRect(0, 0, i.width, i.height);
|
|
1688
|
+
let a = this.barColor;
|
|
1689
|
+
a ||= getComputedStyle(this).getPropertyValue("--md-sys-color-primary").trim() || "#0066cc";
|
|
1690
|
+
let o = this.barWidth + this.barGap, s = this.active ? this.speed : 0;
|
|
1691
|
+
for (let e = 0; e < this._bars.length; e++) this._bars[e].x -= s * r, this.active || (this._bars[e].height += (.05 - this._bars[e].height) * .15);
|
|
1692
|
+
for (this._bars = this._bars.filter((e) => e.x + this.barWidth > -o); this._bars.length === 0 || this._bars[this._bars.length - 1].x < i.width;) {
|
|
1693
|
+
let e = this._bars[this._bars.length - 1], t = e ? e.x + o : i.width, n;
|
|
1694
|
+
if (this.data && this.data.length > 0) n = this.data[this._dataIndex % this.data.length] || .1, this._dataIndex = (this._dataIndex + 1) % this.data.length;
|
|
1695
|
+
else if (this.analyserNode) {
|
|
1696
|
+
(!this._dataArray || this._dataArray.length !== this.analyserNode.frequencyBinCount) && (this._dataArray = new Uint8Array(this.analyserNode.frequencyBinCount)), this.analyserNode.getByteFrequencyData(this._dataArray);
|
|
1697
|
+
let e = 0, t = Math.min(this._dataArray.length, 50);
|
|
1698
|
+
for (let n = 0; n < t; n++) e += this._dataArray[n];
|
|
1699
|
+
let r = e / t / 255;
|
|
1700
|
+
n = Math.max(.1, r ** 1.5 * 1.5);
|
|
1701
|
+
} else {
|
|
1702
|
+
let e = Date.now() / 1e3, t = this._bars.length + e * .01, r = Math.sin(t * .1) * .2, i = Math.cos(t * .05) * .15, a = this._seededRandom(t) * .4;
|
|
1703
|
+
n = Math.max(.1, Math.min(.9, .3 + r + i + a));
|
|
1704
|
+
}
|
|
1705
|
+
if (this._bars.push({
|
|
1706
|
+
x: t,
|
|
1707
|
+
height: n
|
|
1708
|
+
}), this._bars.length > this.barCount * 2) break;
|
|
1709
|
+
}
|
|
1710
|
+
let c = i.height / 2;
|
|
1711
|
+
for (let e of this._bars) if (e.x < i.width && e.x + this.barWidth > 0) {
|
|
1712
|
+
let t = Math.max(this.barHeight, e.height * i.height * .8), r = c - t / 2;
|
|
1713
|
+
n.fillStyle = a, n.globalAlpha = .3 + e.height * .7, this.barRadius > 0 ? (n.beginPath(), n.roundRect(e.x, r, this.barWidth, t, this.barRadius), n.fill()) : n.fillRect(e.x, r, this.barWidth, t);
|
|
1714
|
+
}
|
|
1715
|
+
n.globalAlpha = 1, this.fadeEdges && this.fadeWidth > 0 && f(n, i.width, i.height, this.fadeWidth), this._animationFrameId = requestAnimationFrame(e);
|
|
1716
|
+
};
|
|
1717
|
+
this._animationFrameId = requestAnimationFrame(e);
|
|
1718
|
+
}
|
|
1719
|
+
};
|
|
1720
|
+
l([i({ type: Number })], M.prototype, "speed", void 0), l([i({ type: Number })], M.prototype, "barCount", void 0), l([i({ type: Number })], M.prototype, "barWidth", void 0), l([i({ type: Number })], M.prototype, "barHeight", void 0), l([i({ type: Number })], M.prototype, "barGap", void 0), l([i({ type: Number })], M.prototype, "barRadius", void 0), l([i({ type: String })], M.prototype, "barColor", void 0), l([i({ type: Boolean })], M.prototype, "fadeEdges", void 0), l([i({ type: Number })], M.prototype, "fadeWidth", void 0), l([i({ type: Number })], M.prototype, "height", void 0), l([i({ type: Array })], M.prototype, "data", void 0), l([i({ attribute: !1 })], M.prototype, "analyserNode", void 0), l([i({ type: Boolean })], M.prototype, "active", void 0), l([a("canvas")], M.prototype, "_canvas", void 0), l([a(".container")], M.prototype, "_container", void 0), M = l([r("ui-scrolling-waveform")], M);
|
|
1721
|
+
var N = class extends e {
|
|
1722
|
+
constructor(...e) {
|
|
1723
|
+
super(...e), this.title = "Component", this.description = "", this.mode = "preview";
|
|
1724
|
+
}
|
|
1725
|
+
static {
|
|
1726
|
+
this.styles = t`
|
|
1727
|
+
:host {
|
|
1728
|
+
display: block;
|
|
1729
|
+
background: var(--md-sys-color-surface, #ffffff);
|
|
1730
|
+
color: var(--md-sys-color-on-surface, #1e1e1e);
|
|
1731
|
+
border-radius: 12px;
|
|
1732
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
1733
|
+
transition:
|
|
1734
|
+
background-color 0.3s,
|
|
1735
|
+
color 0.3s;
|
|
1736
|
+
overflow: hidden;
|
|
1737
|
+
font-family: inherit;
|
|
1738
|
+
border: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
.header {
|
|
1742
|
+
padding: 1.5rem 2rem 1rem 2rem;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
.title {
|
|
1746
|
+
margin-top: 0;
|
|
1747
|
+
margin-bottom: 0.5rem;
|
|
1748
|
+
font-size: 1.4rem;
|
|
1749
|
+
font-weight: 600;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
.description {
|
|
1753
|
+
margin: 0;
|
|
1754
|
+
font-size: 0.95rem;
|
|
1755
|
+
color: var(--md-sys-color-on-surface-variant, #444444);
|
|
1756
|
+
line-height: 1.5;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
.tabs {
|
|
1760
|
+
display: flex;
|
|
1761
|
+
gap: 8px;
|
|
1762
|
+
padding: 0 2rem;
|
|
1763
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
.tab-btn {
|
|
1767
|
+
padding: 8px 16px;
|
|
1768
|
+
background: transparent;
|
|
1769
|
+
border: none;
|
|
1770
|
+
border-bottom: 2px solid transparent;
|
|
1771
|
+
color: var(--md-sys-color-on-surface-variant, #444444);
|
|
1772
|
+
font-family: inherit;
|
|
1773
|
+
font-weight: 600;
|
|
1774
|
+
font-size: 0.9rem;
|
|
1775
|
+
cursor: pointer;
|
|
1776
|
+
transition: all 0.2s;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
.tab-btn:hover {
|
|
1780
|
+
color: var(--md-sys-color-primary, #0066cc);
|
|
1781
|
+
background: var(--md-sys-color-surface-container-highest, #e3e3e3);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
.tab-btn.active {
|
|
1785
|
+
color: var(--md-sys-color-primary, #0066cc);
|
|
1786
|
+
border-bottom-color: var(--md-sys-color-primary, #0066cc);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.content-area {
|
|
1790
|
+
padding: 2rem;
|
|
1791
|
+
position: relative;
|
|
1792
|
+
background: var(--md-sys-color-surface, #ffffff);
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
.preview-panel {
|
|
1796
|
+
display: none;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
.preview-panel.active {
|
|
1800
|
+
display: block;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
.code-panel {
|
|
1804
|
+
display: none;
|
|
1805
|
+
background: #1e1e1e; /* Standard terminal color */
|
|
1806
|
+
color: #e3e3e3;
|
|
1807
|
+
padding: 1.5rem;
|
|
1808
|
+
border-radius: 8px;
|
|
1809
|
+
overflow-x: auto;
|
|
1810
|
+
font-family: 'Courier New', Courier, monospace;
|
|
1811
|
+
font-size: 0.85rem;
|
|
1812
|
+
line-height: 1.5;
|
|
1813
|
+
margin: 0;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
.code-panel.active {
|
|
1817
|
+
display: block;
|
|
1818
|
+
}
|
|
1819
|
+
`;
|
|
1820
|
+
}
|
|
1821
|
+
render() {
|
|
1822
|
+
return n`
|
|
1823
|
+
<div class="header">
|
|
1824
|
+
<h2 class="title">${this.title}</h2>
|
|
1825
|
+
${this.description ? n`<p class="description">${this.description}</p>` : ""}
|
|
1826
|
+
</div>
|
|
1827
|
+
|
|
1828
|
+
<div class="tabs">
|
|
1829
|
+
<button
|
|
1830
|
+
class="tab-btn ${this.mode === "preview" ? "active" : ""}"
|
|
1831
|
+
@click="${() => this.mode = "preview"}"
|
|
1832
|
+
>
|
|
1833
|
+
Preview
|
|
1834
|
+
</button>
|
|
1835
|
+
<button
|
|
1836
|
+
class="tab-btn ${this.mode === "code" ? "active" : ""}"
|
|
1837
|
+
@click="${() => this.mode = "code"}"
|
|
1838
|
+
>
|
|
1839
|
+
Code
|
|
1840
|
+
</button>
|
|
1841
|
+
</div>
|
|
1842
|
+
|
|
1843
|
+
<div class="content-area">
|
|
1844
|
+
<div class="preview-panel ${this.mode === "preview" ? "active" : ""}">
|
|
1845
|
+
<slot></slot>
|
|
1846
|
+
</div>
|
|
1847
|
+
|
|
1848
|
+
<pre
|
|
1849
|
+
class="code-panel ${this.mode === "code" ? "active" : ""}"
|
|
1850
|
+
><code><slot name="code"></slot></code></pre>
|
|
1851
|
+
</div>
|
|
1852
|
+
`;
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
l([i({ type: String })], N.prototype, "title", void 0), l([i({ type: String })], N.prototype, "description", void 0), l([i({ type: String })], N.prototype, "mode", void 0), N = l([r("ui-showcase-card")], N);
|
|
1856
|
+
var P = class extends e {
|
|
1857
|
+
constructor(...e) {
|
|
1858
|
+
super(...e), this.text = "", this.duration = 2, this.delay = 0, this.repeat = !0, this.repeatDelay = .5, this.startOnView = !0, this.once = !1, this.spread = 2, this._isInView = !1;
|
|
1859
|
+
}
|
|
1860
|
+
static {
|
|
1861
|
+
this.styles = t`
|
|
1862
|
+
:host {
|
|
1863
|
+
display: inline-block;
|
|
1864
|
+
font-family: inherit;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
span {
|
|
1868
|
+
position: relative;
|
|
1869
|
+
display: inline-block;
|
|
1870
|
+
|
|
1871
|
+
/* Default colors fallback to MD3 tokens if available */
|
|
1872
|
+
/* Use a highly transparent base color for maximum contrast against the shimmer */
|
|
1873
|
+
--base-color: color-mix(
|
|
1874
|
+
in srgb,
|
|
1875
|
+
var(--md-sys-color-on-surface, #1e1e1e) 20%,
|
|
1876
|
+
transparent
|
|
1877
|
+
);
|
|
1878
|
+
--shimmer-color: var(--md-sys-color-on-surface, #1e1e1e);
|
|
1879
|
+
|
|
1880
|
+
--shimmer-bg: linear-gradient(
|
|
1881
|
+
90deg,
|
|
1882
|
+
transparent calc(50% - var(--spread)),
|
|
1883
|
+
var(--shimmer-color) 50%,
|
|
1884
|
+
transparent calc(50% + var(--spread))
|
|
1885
|
+
);
|
|
1886
|
+
|
|
1887
|
+
background-image:
|
|
1888
|
+
var(--shimmer-bg), linear-gradient(var(--base-color), var(--base-color));
|
|
1889
|
+
background-size:
|
|
1890
|
+
250% 100%,
|
|
1891
|
+
auto; /* Important: this defines the size of the animated gradient */
|
|
1892
|
+
background-position: 100% center;
|
|
1893
|
+
background-repeat: no-repeat;
|
|
1894
|
+
-webkit-background-clip: text;
|
|
1895
|
+
background-clip: text;
|
|
1896
|
+
color: transparent;
|
|
1897
|
+
-webkit-text-fill-color: transparent;
|
|
1898
|
+
opacity: 0;
|
|
1899
|
+
transition: opacity 0.3s ease;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
/* When active, trigger the keyframe animation */
|
|
1903
|
+
span.active {
|
|
1904
|
+
opacity: 1;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
@keyframes shimmer {
|
|
1908
|
+
0% {
|
|
1909
|
+
background-position: 100% center;
|
|
1910
|
+
}
|
|
1911
|
+
100% {
|
|
1912
|
+
background-position: 0% center;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
`;
|
|
1916
|
+
}
|
|
1917
|
+
firstUpdated() {
|
|
1918
|
+
this.startOnView ? (this._intersectionObserver = new IntersectionObserver((e) => {
|
|
1919
|
+
e.forEach((e) => {
|
|
1920
|
+
e.isIntersecting ? (this._isInView = !0, this.once && this._intersectionObserver && this._intersectionObserver.disconnect()) : this.once || (this._isInView = !1);
|
|
1921
|
+
});
|
|
1922
|
+
}), this._intersectionObserver.observe(this)) : this._isInView = !0;
|
|
1923
|
+
}
|
|
1924
|
+
disconnectedCallback() {
|
|
1925
|
+
super.disconnectedCallback(), this._intersectionObserver && this._intersectionObserver.disconnect();
|
|
1926
|
+
}
|
|
1927
|
+
render() {
|
|
1928
|
+
let e = !this.startOnView || this._isInView, t = `${this.text.length * this.spread}px`, r = this.duration + this.repeatDelay, i = this.repeat ? "infinite" : "1", a = {
|
|
1929
|
+
"--spread": t,
|
|
1930
|
+
...this.color && { "--base-color": this.color },
|
|
1931
|
+
...this.shimmerColor && { "--shimmer-color": this.shimmerColor },
|
|
1932
|
+
"animation-name": e ? "shimmer" : "none",
|
|
1933
|
+
"animation-duration": `${r}s`,
|
|
1934
|
+
"animation-timing-function": "linear",
|
|
1935
|
+
"animation-delay": `${this.delay}s`,
|
|
1936
|
+
"animation-iteration-count": i,
|
|
1937
|
+
"background-position": e && !this.repeat ? "0% center" : "100% center"
|
|
1938
|
+
}, o = Object.entries(a).map(([e, t]) => `${e}: ${t}`).join("; ");
|
|
1939
|
+
return n`
|
|
1940
|
+
<span class="${e ? "active" : ""}" style="${o}">
|
|
1941
|
+
${this.text}
|
|
1942
|
+
</span>
|
|
1943
|
+
`;
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1946
|
+
l([i({ type: String })], P.prototype, "text", void 0), l([i({ type: Number })], P.prototype, "duration", void 0), l([i({ type: Number })], P.prototype, "delay", void 0), l([i({ type: Boolean })], P.prototype, "repeat", void 0), l([i({ type: Number })], P.prototype, "repeatDelay", void 0), l([i({ type: Boolean })], P.prototype, "startOnView", void 0), l([i({ type: Boolean })], P.prototype, "once", void 0), l([i({ type: Number })], P.prototype, "spread", void 0), l([i({ type: String })], P.prototype, "color", void 0), l([i({ type: String })], P.prototype, "shimmerColor", void 0), l([o()], P.prototype, "_isInView", void 0), P = l([r("ui-shimmering-text")], P);
|
|
1947
|
+
var F = class extends e {
|
|
1948
|
+
constructor(...e) {
|
|
1949
|
+
super(...e), this.agentState = null, this.inputVolume = 0, this.outputVolume = 0, this.volumeMode = "auto", this.seed = Math.floor(Math.random() * 2 ** 32), this._animationFrameId = 0, this._animSpeed = .1, this._curIn = 0, this._curOut = 0, this._textureLoader = new c.TextureLoader(), this._lastTime = 0, this._vertexShader = "\nuniform float uTime;\nuniform sampler2D uPerlinTexture;\nvarying vec2 vUv;\n\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n", this._fragmentShader = "\nuniform float uTime;\nuniform float uAnimation;\nuniform float uInverted;\nuniform float uOffsets[7];\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform float uInputVolume;\nuniform float uOutputVolume;\nuniform float uOpacity;\nuniform sampler2D uPerlinTexture;\nvarying vec2 vUv;\n\nconst float PI = 3.14159265358979323846;\n\nbool drawOval(vec2 polarUv, vec2 polarCenter, float a, float b, bool reverseGradient, float softness, out vec4 color) {\n vec2 p = polarUv - polarCenter;\n float oval = (p.x * p.x) / (a * a) + (p.y * p.y) / (b * b);\n float edge = smoothstep(1.0, 1.0 - softness, oval);\n if (edge > 0.0) {\n float gradient = reverseGradient ? (1.0 - (p.x / a + 1.0) / 2.0) : ((p.x / a + 1.0) / 2.0);\n gradient = mix(0.5, gradient, 0.1);\n color = vec4(vec3(gradient), 0.85 * edge);\n return true;\n }\n return false;\n}\n\nvec3 colorRamp(float grayscale, vec3 color1, vec3 color2, vec3 color3, vec3 color4) {\n if (grayscale < 0.33) {\n return mix(color1, color2, grayscale * 3.0);\n } else if (grayscale < 0.66) {\n return mix(color2, color3, (grayscale - 0.33) * 3.0);\n } else {\n return mix(color3, color4, (grayscale - 0.66) * 3.0);\n }\n}\n\nvec2 hash2(vec2 p) {\n return fract(sin(vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)))) * 43758.5453);\n}\n\nfloat noise2D(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n vec2 u = f * f * (3.0 - 2.0 * f);\n float n = mix(\n mix(dot(hash2(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),\n dot(hash2(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),\n mix(dot(hash2(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),\n dot(hash2(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x),\n u.y\n );\n return 0.5 + 0.5 * n;\n}\n\nfloat sharpRing(vec3 decomposed, float time) {\n float ringStart = 1.0;\n float ringWidth = 0.3;\n float noiseScale = 5.0;\n float noise = mix(\n noise2D(vec2(decomposed.x, time) * noiseScale),\n noise2D(vec2(decomposed.y, time) * noiseScale),\n decomposed.z\n );\n noise = (noise - 0.5) * 2.5;\n return ringStart + noise * ringWidth * 1.5;\n}\n\nfloat smoothRing(vec3 decomposed, float time) {\n float ringStart = 0.9;\n float ringWidth = 0.2;\n float noiseScale = 6.0;\n float noise = mix(\n noise2D(vec2(decomposed.x, time) * noiseScale),\n noise2D(vec2(decomposed.y, time) * noiseScale),\n decomposed.z\n );\n noise = (noise - 0.5) * 5.0;\n return ringStart + noise * ringWidth;\n}\n\nfloat flow(vec3 decomposed, float time) {\n return mix(\n texture(uPerlinTexture, vec2(time, decomposed.x / 2.0)).r,\n texture(uPerlinTexture, vec2(time, decomposed.y / 2.0)).r,\n decomposed.z\n );\n}\n\nvoid main() {\n vec2 uv = vUv * 2.0 - 1.0;\n float radius = length(uv);\n float theta = atan(uv.y, uv.x);\n if (theta < 0.0) theta += 2.0 * PI;\n\n vec3 decomposed = vec3(\n theta / (2.0 * PI),\n mod(theta / (2.0 * PI) + 0.5, 1.0) + 1.0,\n abs(theta / PI - 1.0)\n );\n\n float noise = flow(decomposed, radius * 0.03 - uAnimation * 0.2) - 0.5;\n theta += noise * mix(0.08, 0.25, uOutputVolume);\n\n vec4 color = vec4(1.0, 1.0, 1.0, 1.0);\n float originalCenters[7] = float[7](0.0, 0.5 * PI, 1.0 * PI, 1.5 * PI, 2.0 * PI, 2.5 * PI, 3.0 * PI);\n float centers[7];\n for (int i = 0; i < 7; i++) {\n centers[i] = originalCenters[i] + 0.5 * sin(uTime / 20.0 + uOffsets[i]);\n }\n\n float a, b;\n vec4 ovalColor;\n\n for (int i = 0; i < 7; i++) {\n float noise = texture(uPerlinTexture, vec2(mod(centers[i] + uTime * 0.05, 1.0), 0.5)).r;\n a = 0.5 + noise * 0.3;\n b = noise * mix(3.5, 2.5, uInputVolume);\n bool reverseGradient = (i % 2 == 1);\n float distTheta = min(\n abs(theta - centers[i]),\n min(abs(theta + 2.0 * PI - centers[i]), abs(theta - 2.0 * PI - centers[i]))\n );\n if (drawOval(vec2(distTheta, radius), vec2(0.0, 0.0), a, b, reverseGradient, 0.6, ovalColor)) {\n color.rgb = mix(color.rgb, ovalColor.rgb, ovalColor.a);\n color.a = max(color.a, ovalColor.a);\n }\n }\n \n float ringRadius1 = sharpRing(decomposed, uTime * 0.1);\n float ringRadius2 = smoothRing(decomposed, uTime * 0.1);\n float inputRadius1 = radius + uInputVolume * 0.2;\n float inputRadius2 = radius + uInputVolume * 0.15;\n float opacity1 = mix(0.2, 0.6, uInputVolume);\n float opacity2 = mix(0.15, 0.45, uInputVolume);\n\n float ringAlpha1 = (inputRadius2 >= ringRadius1) ? opacity1 : 0.0;\n float ringAlpha2 = smoothstep(ringRadius2 - 0.05, ringRadius2 + 0.05, inputRadius1) * opacity2;\n float totalRingAlpha = max(ringAlpha1, ringAlpha2);\n \n vec3 ringColor = vec3(1.0);\n color.rgb = 1.0 - (1.0 - color.rgb) * (1.0 - ringColor * totalRingAlpha);\n\n vec3 color1 = vec3(0.0, 0.0, 0.0);\n vec3 color2 = uColor1;\n vec3 color3 = uColor2;\n vec3 color4 = vec3(1.0, 1.0, 1.0);\n\n float luminance = mix(color.r, 1.0 - color.r, uInverted);\n color.rgb = colorRamp(luminance, color1, color2, color3, color4);\n color.a *= uOpacity;\n\n gl_FragColor = color;\n}\n";
|
|
1950
|
+
}
|
|
1951
|
+
static {
|
|
1952
|
+
this.styles = t`
|
|
1953
|
+
:host {
|
|
1954
|
+
display: block;
|
|
1955
|
+
width: 100%;
|
|
1956
|
+
height: 100%;
|
|
1957
|
+
position: relative;
|
|
1958
|
+
}
|
|
1959
|
+
.container {
|
|
1960
|
+
width: 100%;
|
|
1961
|
+
height: 100%;
|
|
1962
|
+
}
|
|
1963
|
+
canvas {
|
|
1964
|
+
display: block;
|
|
1965
|
+
width: 100%;
|
|
1966
|
+
height: 100%;
|
|
1967
|
+
}
|
|
1968
|
+
`;
|
|
1969
|
+
}
|
|
1970
|
+
render() {
|
|
1971
|
+
return n`<div class="container"></div>`;
|
|
1972
|
+
}
|
|
1973
|
+
firstUpdated() {
|
|
1974
|
+
this._initThree();
|
|
1975
|
+
}
|
|
1976
|
+
updated(e) {
|
|
1977
|
+
e.has("colors") && this._updateColors();
|
|
1978
|
+
}
|
|
1979
|
+
_updateColors() {
|
|
1980
|
+
if (!(!this._targetColor1 || !this._targetColor2)) if (this.colors && this.colors.length === 2) this._targetColor1.set(this.colors[0]), this._targetColor2.set(this.colors[1]);
|
|
1981
|
+
else {
|
|
1982
|
+
let e = getComputedStyle(this), t = e.getPropertyValue("--md-sys-color-primary").trim() || "#CADCFC", n = e.getPropertyValue("--md-sys-color-secondary").trim() || "#A0B9D1";
|
|
1983
|
+
this._targetColor1.set(t), this._targetColor2.set(n);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
disconnectedCallback() {
|
|
1987
|
+
super.disconnectedCallback(), this._animationFrameId && cancelAnimationFrame(this._animationFrameId), this._resizeObserver && this._resizeObserver.disconnect(), this._renderer && this._renderer.dispose(), this._mesh && (this._mesh.geometry.dispose(), this._mesh.material.dispose());
|
|
1988
|
+
}
|
|
1989
|
+
async _initThree() {
|
|
1990
|
+
if (!this._container) return;
|
|
1991
|
+
this._targetColor1 = new c.Color(), this._targetColor2 = new c.Color(), this._updateColors();
|
|
1992
|
+
try {
|
|
1993
|
+
this._perlinNoiseTexture = await this._textureLoader.loadAsync("https://storage.googleapis.com/eleven-public-cdn/images/perlin-noise.png"), this._perlinNoiseTexture.wrapS = c.RepeatWrapping, this._perlinNoiseTexture.wrapT = c.RepeatWrapping;
|
|
1994
|
+
} catch (e) {
|
|
1995
|
+
console.warn("Failed to load perlin noise texture for orb.", e);
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
let e = this._container.clientWidth, t = this._container.clientHeight;
|
|
1999
|
+
this._scene = new c.Scene(), this._camera = new c.OrthographicCamera(-5, 5, 5, -5, .1, 10), this._camera.position.z = 1, this._renderer = new c.WebGLRenderer({
|
|
2000
|
+
alpha: !0,
|
|
2001
|
+
antialias: !0,
|
|
2002
|
+
premultipliedAlpha: !0
|
|
2003
|
+
}), this._renderer.setSize(e, t), this._renderer.setPixelRatio(window.devicePixelRatio), this._container.appendChild(this._renderer.domElement);
|
|
2004
|
+
let n = this._splitmix32(this.seed), r = new Float32Array(Array.from({ length: 7 }, () => n() * Math.PI * 2)), i = document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches, a = {
|
|
2005
|
+
uColor1: new c.Uniform(this._targetColor1),
|
|
2006
|
+
uColor2: new c.Uniform(this._targetColor2),
|
|
2007
|
+
uOffsets: { value: r },
|
|
2008
|
+
uPerlinTexture: new c.Uniform(this._perlinNoiseTexture),
|
|
2009
|
+
uTime: new c.Uniform(0),
|
|
2010
|
+
uAnimation: new c.Uniform(.1),
|
|
2011
|
+
uInverted: new c.Uniform(i ? 1 : 0),
|
|
2012
|
+
uInputVolume: new c.Uniform(0),
|
|
2013
|
+
uOutputVolume: new c.Uniform(0),
|
|
2014
|
+
uOpacity: new c.Uniform(0)
|
|
2015
|
+
}, o = new c.CircleGeometry(3.5, 64), s = new c.ShaderMaterial({
|
|
2016
|
+
uniforms: a,
|
|
2017
|
+
vertexShader: this._vertexShader,
|
|
2018
|
+
fragmentShader: this._fragmentShader,
|
|
2019
|
+
transparent: !0
|
|
2020
|
+
});
|
|
2021
|
+
this._mesh = new c.Mesh(o, s), this._scene.add(this._mesh), this._resizeObserver = new ResizeObserver(() => {
|
|
2022
|
+
this._container && this._renderer && this._renderer.setSize(this._container.clientWidth, this._container.clientHeight);
|
|
2023
|
+
}), this._resizeObserver.observe(this._container), new MutationObserver(() => {
|
|
2024
|
+
if (!this._mesh) return;
|
|
2025
|
+
let e = document.documentElement.classList.contains("dark");
|
|
2026
|
+
this._mesh.material.uniforms.uInverted.value = e ? 1 : 0, this._updateColors();
|
|
2027
|
+
}).observe(document.documentElement, {
|
|
2028
|
+
attributes: !0,
|
|
2029
|
+
attributeFilter: ["class"]
|
|
2030
|
+
}), this._lastTime = performance.now(), this._animate();
|
|
2031
|
+
}
|
|
2032
|
+
_animate() {
|
|
2033
|
+
if (this._animationFrameId = requestAnimationFrame(() => this._animate()), !this._mesh || !this._renderer || !this._scene || !this._camera) return;
|
|
2034
|
+
let e = performance.now(), t = (e - this._lastTime) / 1e3;
|
|
2035
|
+
this._lastTime = e;
|
|
2036
|
+
let n = this._mesh.material.uniforms;
|
|
2037
|
+
n.uTime.value += t * .5, n.uOpacity.value < 1 && (n.uOpacity.value = Math.min(1, n.uOpacity.value + t * 2));
|
|
2038
|
+
let r = 0, i = .3;
|
|
2039
|
+
if (this.volumeMode === "manual") r = this._clamp01(this.inputVolume), i = this._clamp01(this.outputVolume);
|
|
2040
|
+
else {
|
|
2041
|
+
let e = n.uTime.value * 2;
|
|
2042
|
+
if (this.agentState === null) r = 0, i = .3;
|
|
2043
|
+
else if (this.agentState === "listening") r = this._clamp01(.55 + Math.sin(e * 3.2) * .35), i = .45;
|
|
2044
|
+
else if (this.agentState === "talking") r = this._clamp01(.65 + Math.sin(e * 4.8) * .22), i = this._clamp01(.75 + Math.sin(e * 3.6) * .22);
|
|
2045
|
+
else {
|
|
2046
|
+
let t = .38 + .07 * Math.sin(e * .7), n = .05 * Math.sin(e * 2.1) * Math.sin(e * .37 + 1.2);
|
|
2047
|
+
r = this._clamp01(t + n), i = this._clamp01(.48 + .12 * Math.sin(e * 1.05 + .6));
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
this._curIn += (r - this._curIn) * .2, this._curOut += (i - this._curOut) * .2;
|
|
2051
|
+
let a = .1 + (1 - (this._curOut - 1) ** 2) * .9;
|
|
2052
|
+
this._animSpeed += (a - this._animSpeed) * .12, n.uAnimation.value += t * this._animSpeed, n.uInputVolume.value = this._curIn, n.uOutputVolume.value = this._curOut, n.uColor1.value.lerp(this._targetColor1, .08), n.uColor2.value.lerp(this._targetColor2, .08), this._renderer.render(this._scene, this._camera);
|
|
2053
|
+
}
|
|
2054
|
+
_splitmix32(e) {
|
|
2055
|
+
return function() {
|
|
2056
|
+
e |= 0, e = e + 2654435769 | 0;
|
|
2057
|
+
let t = e ^ e >>> 16;
|
|
2058
|
+
return t = Math.imul(t, 569420461), t ^= t >>> 15, t = Math.imul(t, 1935289751), ((t ^= t >>> 15) >>> 0) / 4294967296;
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
_clamp01(e) {
|
|
2062
|
+
return Number.isFinite(e) ? Math.min(1, Math.max(0, e)) : 0;
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
l([i({ type: Array })], F.prototype, "colors", void 0), l([i({ type: String })], F.prototype, "agentState", void 0), l([i({ type: Number })], F.prototype, "inputVolume", void 0), l([i({ type: Number })], F.prototype, "outputVolume", void 0), l([i({ type: String })], F.prototype, "volumeMode", void 0), l([i({ type: Number })], F.prototype, "seed", void 0), l([a(".container")], F.prototype, "_container", void 0), F = l([r("ui-orb")], F);
|
|
2066
|
+
var I = class extends e {
|
|
2067
|
+
static {
|
|
2068
|
+
this.styles = t`
|
|
2069
|
+
:host {
|
|
2070
|
+
display: flex;
|
|
2071
|
+
align-items: center;
|
|
2072
|
+
gap: 8px;
|
|
2073
|
+
width: 100%;
|
|
2074
|
+
box-sizing: border-box;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
md-slider {
|
|
2078
|
+
flex: 1;
|
|
2079
|
+
min-width: 0; /* Prevent flex overflow */
|
|
2080
|
+
width: 100%;
|
|
2081
|
+
--md-slider-inactive-track-color: var(
|
|
2082
|
+
--md-sys-color-outline-variant,
|
|
2083
|
+
#c4c7c5
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
md-icon-button {
|
|
2088
|
+
color: var(--md-sys-color-on-surface-variant, #444);
|
|
2089
|
+
}
|
|
2090
|
+
`;
|
|
2091
|
+
}
|
|
2092
|
+
render() {
|
|
2093
|
+
let e = this.playerState?.volume ?? 1, t = this.playerState?.muted ?? !1, r = "volume_up";
|
|
2094
|
+
return t || e === 0 ? r = "volume_off" : e < .5 && (r = "volume_down"), n`
|
|
2095
|
+
<md-icon-button @click="${this._toggleMute}" part="button">
|
|
2096
|
+
<md-icon>${r}</md-icon>
|
|
2097
|
+
</md-icon-button>
|
|
2098
|
+
<md-slider
|
|
2099
|
+
part="slider"
|
|
2100
|
+
min="0"
|
|
2101
|
+
max="1"
|
|
2102
|
+
value="${t ? 0 : e}"
|
|
2103
|
+
step="0.01"
|
|
2104
|
+
?disabled="${!this.playerState?.src}"
|
|
2105
|
+
@input="${this._handleInput}"
|
|
2106
|
+
></md-slider>
|
|
2107
|
+
`;
|
|
2108
|
+
}
|
|
2109
|
+
_handleInput(e) {
|
|
2110
|
+
let t = e.target;
|
|
2111
|
+
this.playerState && this.playerState.setVolume(t.value);
|
|
2112
|
+
}
|
|
2113
|
+
_toggleMute() {
|
|
2114
|
+
this.playerState && this.playerState.toggleMute();
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
l([C({
|
|
2118
|
+
context: w,
|
|
2119
|
+
subscribe: !0
|
|
2120
|
+
}), i({ attribute: !1 })], I.prototype, "playerState", void 0), I = l([r("ui-audio-volume-slider")], I);
|
|
2121
|
+
export { u as ScreamVoiceButton, E as UiAudioPlayButton, k as UiAudioPlayer, D as UiAudioProgressSlider, T as UiAudioProvider, O as UiAudioTimeDisplay, I as UiAudioVolumeSlider, p as UiLiveWaveform, A as UiMicSelector, F as UiOrb, M as UiScrollingWaveform, P as UiShimmeringText, N as UiShowcaseCard, m as UiVoiceButton, j as UiVoicePicker, h as UiWaveform, w as audioPlayerContext };
|