@ghchinoy/lit-audio-ui 0.1.1 → 0.2.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.
Files changed (35) hide show
  1. package/README.md +1 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js +7 -0
  3. package/dist/components/scream-voice-button.js +64 -0
  4. package/dist/components/ui-audio-play-button.js +84 -0
  5. package/dist/components/ui-audio-player.js +61 -0
  6. package/dist/components/ui-audio-progress-slider.js +52 -0
  7. package/dist/components/ui-audio-provider.js +140 -0
  8. package/dist/components/ui-audio-time-display.js +40 -0
  9. package/dist/components/ui-audio-volume-slider.js +64 -0
  10. package/dist/components/ui-live-waveform.js +128 -0
  11. package/dist/components/ui-mic-selector.js +245 -0
  12. package/dist/components/ui-scrolling-waveform.js +107 -0
  13. package/dist/components/ui-shimmering-text.js +95 -0
  14. package/dist/components/ui-showcase-card.js +140 -0
  15. package/dist/components/ui-speech-cancel-button.js +34 -0
  16. package/dist/components/ui-speech-preview.js +72 -0
  17. package/dist/components/ui-speech-provider.js +106 -0
  18. package/dist/components/ui-speech-record-button.js +76 -0
  19. package/dist/components/ui-voice-button.js +208 -0
  20. package/dist/components/ui-voice-picker.js +356 -0
  21. package/dist/components/ui-waveform.js +89 -0
  22. package/dist/index.js +22 -0
  23. package/dist/node_modules/@lit/context/lib/context-request-event.js +14 -0
  24. package/dist/node_modules/@lit/context/lib/controllers/context-consumer.js +26 -0
  25. package/dist/node_modules/@lit/context/lib/controllers/context-provider.js +34 -0
  26. package/dist/node_modules/@lit/context/lib/create-context.js +9 -0
  27. package/dist/node_modules/@lit/context/lib/decorators/consume.js +27 -0
  28. package/dist/node_modules/@lit/context/lib/decorators/provide.js +54 -0
  29. package/dist/node_modules/@lit/context/lib/value-notifier.js +37 -0
  30. package/dist/scream-audio-ui.umd.js +113 -2
  31. package/dist/utils/audio-context.js +8 -0
  32. package/dist/utils/audio-utils.js +27 -0
  33. package/dist/utils/speech-context.js +3 -0
  34. package/package.json +9 -6
  35. package/dist/scream-audio-ui.es.js +0 -2001
package/README.md CHANGED
@@ -87,6 +87,7 @@ The library currently ships with the following native WebComponents:
87
87
  * ✨ **`<ui-shimmering-text>`**: A pure CSS, dependency-free text loading effect translating complex gradients into native `@keyframes`.
88
88
  * 🎛️ **`<ui-mic-selector>`**: Handles hardware microphone enumeration (`navigator.mediaDevices`), permissions, and displays a live audio preview directly inside a dropdown menu.
89
89
  * 🎭 **`<ui-voice-picker>`**: A searchable dropdown menu (combobox) that handles rendering complex persona objects, including real-time audio previews injected directly into the menu items.
90
+ * 🗣️ **Atomic Speech Architecture**: Use `<ui-speech-provider>`, `<ui-speech-record-button>`, `<ui-speech-preview>`, and `<ui-speech-cancel-button>` to build custom recording UIs (like the Smart Textarea) with built-in state management and visualization.
90
91
 
91
92
 
92
93
  ## How to Build & Extend (For Developers)
@@ -0,0 +1,7 @@
1
+ function e(e, t, n, r) {
2
+ var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
3
+ if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
4
+ 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);
5
+ return i > 3 && a && Object.defineProperty(t, n, a), a;
6
+ }
7
+ export { e as __decorate };
@@ -0,0 +1,64 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { LitElement as t, css as n, html as r } from "lit";
3
+ import { customElement as i, property as a } from "lit/decorators.js";
4
+ import "@material/web/button/filled-button.js";
5
+ import "@material/web/icon/icon.js";
6
+ /**
7
+ * Copyright 2026 Google LLC
8
+ *
9
+ * Licensed under the Apache License, Version 2.0 (the "License");
10
+ * you may not use this file except in compliance with the License.
11
+ * You may obtain a copy of the License at
12
+ *
13
+ * http://www.apache.org/licenses/LICENSE-2.0
14
+ *
15
+ * Unless required by applicable law or agreed to in writing, software
16
+ * distributed under the License is distributed on an "AS IS" BASIS,
17
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ * See the License for the specific language governing permissions and
19
+ * limitations under the License.
20
+ */
21
+ var o = class extends t {
22
+ constructor(...e) {
23
+ super(...e), this.state = "idle";
24
+ }
25
+ static {
26
+ this.styles = n`
27
+ :host {
28
+ display: inline-block;
29
+ }
30
+
31
+ md-filled-button {
32
+ --md-filled-button-container-shape: 999px;
33
+ }
34
+
35
+ md-filled-button.recording {
36
+ --md-filled-button-container-color: var(--md-sys-color-error, #ba1a1a);
37
+ --md-filled-button-label-text-color: var(
38
+ --md-sys-color-on-error,
39
+ #ffffff
40
+ );
41
+ }
42
+ `;
43
+ }
44
+ render() {
45
+ return r`
46
+ <md-filled-button class="${this.state}" @click="${this._handleClick}">
47
+ <md-icon slot="icon">
48
+ ${this.state === "recording" ? "stop" : "mic"}
49
+ </md-icon>
50
+
51
+ ${this.state === "recording" ? "Recording..." : "Speak"}
52
+ </md-filled-button>
53
+ `;
54
+ }
55
+ _handleClick() {
56
+ this.state = this.state === "idle" ? "recording" : "idle", this.dispatchEvent(new CustomEvent("voice-toggle", {
57
+ bubbles: !0,
58
+ composed: !0,
59
+ detail: { state: this.state }
60
+ }));
61
+ }
62
+ };
63
+ e([a({ type: String })], o.prototype, "state", void 0), o = e([i("scream-voice-button")], o);
64
+ export { o as ScreamVoiceButton };
@@ -0,0 +1,84 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
+ import { audioPlayerContext as n } from "../utils/audio-context.js";
4
+ import { LitElement as r, css as i, html as a } from "lit";
5
+ import { customElement as o, property as s } from "lit/decorators.js";
6
+ import "@material/web/icon/icon.js";
7
+ import "@material/web/iconbutton/filled-icon-button.js";
8
+ import "@material/web/progress/circular-progress.js";
9
+ var c = class extends r {
10
+ static {
11
+ this.styles = i`
12
+ :host {
13
+ display: inline-flex;
14
+ position: relative;
15
+ align-items: center;
16
+ justify-content: center;
17
+ font-family: inherit;
18
+ }
19
+
20
+ md-filled-icon-button {
21
+ --md-filled-icon-button-container-color: var(
22
+ --md-sys-color-primary,
23
+ #0066cc
24
+ );
25
+ --md-filled-icon-button-icon-color: var(
26
+ --md-sys-color-on-primary,
27
+ #ffffff
28
+ );
29
+ --md-filled-icon-button-hover-icon-color: var(
30
+ --md-sys-color-on-primary,
31
+ #ffffff
32
+ );
33
+ --md-filled-icon-button-focus-icon-color: var(
34
+ --md-sys-color-on-primary,
35
+ #ffffff
36
+ );
37
+ --md-filled-icon-button-pressed-icon-color: var(
38
+ --md-sys-color-on-primary,
39
+ #ffffff
40
+ );
41
+
42
+ --md-filled-icon-button-toggle-icon-color: var(
43
+ --md-sys-color-on-primary,
44
+ #ffffff
45
+ );
46
+ --md-filled-icon-button-selected-container-color: var(
47
+ --md-sys-color-primary,
48
+ #0066cc
49
+ );
50
+ --md-filled-icon-button-selected-icon-color: var(
51
+ --md-sys-color-on-primary,
52
+ #ffffff
53
+ );
54
+ color: var(--md-sys-color-on-primary, #ffffff);
55
+ }
56
+
57
+ md-circular-progress {
58
+ position: absolute;
59
+ --md-circular-progress-size: 48px;
60
+ }
61
+ `;
62
+ }
63
+ render() {
64
+ let e = this.playerState?.isPlaying ?? !1, t = this.playerState?.isBuffering ?? !1;
65
+ return a`
66
+ <md-filled-icon-button
67
+ part="button"
68
+ @click="${this._handleClick}"
69
+ ?disabled="${!this.playerState?.src}"
70
+ >
71
+ <md-icon>${e ? "pause" : "play_arrow"}</md-icon>
72
+ </md-filled-icon-button>
73
+ ${t && e ? a`<md-circular-progress indeterminate></md-circular-progress>` : ""}
74
+ `;
75
+ }
76
+ _handleClick() {
77
+ this.playerState && this.playerState.togglePlay();
78
+ }
79
+ };
80
+ e([t({
81
+ context: n,
82
+ subscribe: !0
83
+ }), s({ attribute: !1 })], c.prototype, "playerState", void 0), c = e([o("ui-audio-play-button")], c);
84
+ export { c as UiAudioPlayButton };
@@ -0,0 +1,61 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import "./ui-audio-provider.js";
3
+ import "./ui-audio-play-button.js";
4
+ import "./ui-audio-progress-slider.js";
5
+ import "./ui-audio-time-display.js";
6
+ import { LitElement as t, css as n, html as r } from "lit";
7
+ import { customElement as i, property as a } from "lit/decorators.js";
8
+ var o = class extends t {
9
+ static {
10
+ this.styles = n`
11
+ :host {
12
+ display: inline-block;
13
+ width: 100%;
14
+ max-width: 400px;
15
+ }
16
+
17
+ .player-pill {
18
+ display: flex;
19
+ align-items: center;
20
+ gap: 16px;
21
+ padding: 12px 24px;
22
+ background: var(--md-sys-color-surface-container-high, #e2e2e2);
23
+ border-radius: 999px; /* Pill shape */
24
+ width: fit-content;
25
+ font-family: inherit;
26
+ }
27
+
28
+ .time-container {
29
+ min-width: 85px; /* prevent jitter when times change */
30
+ }
31
+
32
+ .slider-container {
33
+ width: 200px;
34
+ display: flex;
35
+ align-items: center;
36
+ }
37
+ `;
38
+ }
39
+ render() {
40
+ return r`
41
+ <ui-audio-provider .src="${this.item?.src || ""}">
42
+ <div class="player-pill" part="container">
43
+ <!-- Atomic Play/Pause Button -->
44
+ <ui-audio-play-button></ui-audio-play-button>
45
+
46
+ <!-- Atomic Time Display (Full format: 0:00 / 0:00) -->
47
+ <div class="time-container">
48
+ <ui-audio-time-display format="full"></ui-audio-time-display>
49
+ </div>
50
+
51
+ <!-- Atomic Slider -->
52
+ <div class="slider-container">
53
+ <ui-audio-progress-slider></ui-audio-progress-slider>
54
+ </div>
55
+ </div>
56
+ </ui-audio-provider>
57
+ `;
58
+ }
59
+ };
60
+ e([a({ type: Object })], o.prototype, "item", void 0), o = e([i("ui-audio-player")], o);
61
+ export { o as UiAudioPlayer };
@@ -0,0 +1,52 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
+ import { audioPlayerContext as n } from "../utils/audio-context.js";
4
+ import { LitElement as r, css as i, html as a } from "lit";
5
+ import { customElement as o, property as s } from "lit/decorators.js";
6
+ import "@material/web/slider/slider.js";
7
+ var c = class extends r {
8
+ constructor(...e) {
9
+ super(...e), this._isDragging = !1, this._dragValue = 0;
10
+ }
11
+ static {
12
+ this.styles = i`
13
+ :host {
14
+ display: flex;
15
+ width: 100%;
16
+ align-items: center;
17
+ min-width: 100px;
18
+ }
19
+
20
+ md-slider {
21
+ width: 100%;
22
+ /* Give the slider track better contrast against backgrounds */
23
+ --md-slider-inactive-track-color: var(--md-sys-color-outline, #79747e);
24
+ }
25
+ `;
26
+ }
27
+ render() {
28
+ let e = this.playerState?.duration || 0, t = e === 0 || !this.playerState?.src, n = this._isDragging ? this._dragValue : this.playerState?.currentTime || 0;
29
+ return a`
30
+ <md-slider
31
+ min="0"
32
+ max="${e || 100}"
33
+ value="${n}"
34
+ step="0.1"
35
+ ?disabled="${t}"
36
+ @input="${this._handleInput}"
37
+ @change="${this._handleChange}"
38
+ ></md-slider>
39
+ `;
40
+ }
41
+ _handleInput(e) {
42
+ this._isDragging = !0, this._dragValue = e.target.value;
43
+ }
44
+ _handleChange(e) {
45
+ this._dragValue = e.target.value, this.playerState && this.playerState.seek(this._dragValue), this._isDragging = !1;
46
+ }
47
+ };
48
+ e([t({
49
+ context: n,
50
+ subscribe: !0
51
+ }), s({ attribute: !1 })], c.prototype, "playerState", void 0), c = e([o("ui-audio-progress-slider")], c);
52
+ export { c as UiAudioProgressSlider };
@@ -0,0 +1,140 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { e as t } from "../node_modules/@lit/context/lib/decorators/provide.js";
3
+ import { audioPlayerContext as n } from "../utils/audio-context.js";
4
+ import { LitElement as r, css as i, html as a } from "lit";
5
+ import { customElement as o, property as s, query as c, state as l } from "lit/decorators.js";
6
+ var u = class extends r {
7
+ constructor(...e) {
8
+ super(...e), this.src = "", this._animationFrameId = 0, this.state = {
9
+ src: "",
10
+ isPlaying: !1,
11
+ isBuffering: !1,
12
+ currentTime: 0,
13
+ duration: 0,
14
+ volume: 1,
15
+ muted: !1,
16
+ analyserNode: void 0,
17
+ play: () => this.play(),
18
+ pause: () => this.pause(),
19
+ togglePlay: () => this._togglePlay(),
20
+ seek: (e) => this._seek(e),
21
+ setVolume: (e) => this._setVolume(e),
22
+ toggleMute: () => this._toggleMute()
23
+ };
24
+ }
25
+ static {
26
+ this.styles = i`
27
+ :host {
28
+ display: contents; /* We are completely invisible, just wrapping children */
29
+ }
30
+ audio {
31
+ display: none;
32
+ }
33
+ `;
34
+ }
35
+ render() {
36
+ return a`
37
+ <audio
38
+ crossorigin="anonymous"
39
+ src="${this.src}"
40
+ preload="metadata"
41
+ @loadedmetadata="${this._handleLoadedMetadata}"
42
+ @ended="${this._handleEnded}"
43
+ @playing="${this._handlePlaying}"
44
+ @pause="${this._handlePause}"
45
+ @waiting="${() => this._updateState({ isBuffering: !0 })}"
46
+ @canplay="${() => this._updateState({ isBuffering: !1 })}"
47
+ @error="${this._handleError}"
48
+ ></audio>
49
+ <slot></slot>
50
+ `;
51
+ }
52
+ willUpdate(e) {
53
+ e.has("src") && this._updateState({
54
+ src: this.src,
55
+ isPlaying: !1,
56
+ currentTime: 0,
57
+ error: void 0
58
+ });
59
+ }
60
+ updated(e) {
61
+ e.has("src") && this._audioEl && this._audioEl.load();
62
+ }
63
+ disconnectedCallback() {
64
+ super.disconnectedCallback(), this._animationFrameId && cancelAnimationFrame(this._animationFrameId), this._audioContext && this._audioContext.state !== "closed" && this._audioContext.close();
65
+ }
66
+ _updateState(e) {
67
+ this.state = {
68
+ ...this.state,
69
+ ...e
70
+ }, this.dispatchEvent(new CustomEvent("state-change", {
71
+ detail: this.state,
72
+ bubbles: !0,
73
+ composed: !0
74
+ }));
75
+ }
76
+ _setupAudioContext() {
77
+ if (!(this._audioContext || !this._audioEl)) try {
78
+ 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 });
79
+ } catch (e) {
80
+ console.warn("Failed to set up AudioContext for visualizer:", e);
81
+ }
82
+ }
83
+ play() {
84
+ this._audioEl.src && (this._setupAudioContext(), this._audioContext?.state === "suspended" && this._audioContext.resume(), this._audioEl.play().catch((e) => {
85
+ console.error("Error playing audio", e), this._updateState({ error: "Playback failed" });
86
+ }));
87
+ }
88
+ pause() {
89
+ this._audioEl && this._audioEl.pause();
90
+ }
91
+ _togglePlay() {
92
+ this.state.isPlaying ? this.pause() : this.play();
93
+ }
94
+ _seek(e) {
95
+ this._audioEl && (this._audioEl.currentTime = e, this._updateState({ currentTime: e }));
96
+ }
97
+ _setVolume(e) {
98
+ this._audioEl && (this._audioEl.volume = e, this._updateState({
99
+ volume: e,
100
+ muted: e === 0
101
+ }));
102
+ }
103
+ _toggleMute() {
104
+ this._audioEl && (this._audioEl.muted = !this._audioEl.muted, this._updateState({ muted: this._audioEl.muted }));
105
+ }
106
+ _handleLoadedMetadata() {
107
+ this._updateState({ duration: this._audioEl.duration });
108
+ }
109
+ _handleEnded() {
110
+ this._updateState({
111
+ isPlaying: !1,
112
+ currentTime: 0
113
+ }), this._audioEl.currentTime = 0;
114
+ }
115
+ _handlePlaying() {
116
+ this._updateState({
117
+ isPlaying: !0,
118
+ isBuffering: !1,
119
+ error: void 0
120
+ }), this._startTrackingTime();
121
+ }
122
+ _handlePause() {
123
+ this._updateState({ isPlaying: !1 }), this._animationFrameId && cancelAnimationFrame(this._animationFrameId);
124
+ }
125
+ _handleError() {
126
+ this._updateState({
127
+ error: "Error loading audio",
128
+ isPlaying: !1,
129
+ isBuffering: !1
130
+ });
131
+ }
132
+ _startTrackingTime() {
133
+ let e = () => {
134
+ this._audioEl && this.state.isPlaying && (Math.abs(this.state.currentTime - this._audioEl.currentTime) > .05 && this._updateState({ currentTime: this._audioEl.currentTime }), this._animationFrameId = requestAnimationFrame(e));
135
+ };
136
+ this._animationFrameId = requestAnimationFrame(e);
137
+ }
138
+ };
139
+ e([s({ type: String })], u.prototype, "src", void 0), e([c("audio")], u.prototype, "_audioEl", void 0), e([t({ context: n }), l()], u.prototype, "state", void 0), u = e([o("ui-audio-provider")], u);
140
+ export { u as UiAudioProvider };
@@ -0,0 +1,40 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
+ import { audioPlayerContext as n } from "../utils/audio-context.js";
4
+ import { LitElement as r, css as i, html as a } from "lit";
5
+ import { customElement as o, property as s } from "lit/decorators.js";
6
+ var c = class extends r {
7
+ constructor(...e) {
8
+ super(...e), this.format = "full";
9
+ }
10
+ static {
11
+ this.styles = i`
12
+ :host {
13
+ display: inline-block;
14
+ font-variant-numeric: tabular-nums;
15
+ font-size: 14px;
16
+ color: var(--md-sys-color-on-surface-variant, #444);
17
+ font-family: inherit;
18
+ }
19
+ `;
20
+ }
21
+ render() {
22
+ let e = this.playerState?.currentTime || 0, t = this.playerState?.duration || 0;
23
+ if (this.format === "elapsed") return a`${this._formatTime(e)}`;
24
+ if (this.format === "remaining") {
25
+ let n = Math.max(0, t - e);
26
+ return a`-${this._formatTime(n)}`;
27
+ } else return a`${this._formatTime(e)} /
28
+ ${t ? this._formatTime(t) : "--:--"}`;
29
+ }
30
+ _formatTime(e) {
31
+ if (!e || isNaN(e)) return "0:00";
32
+ let t = Math.floor(e / 3600), n = Math.floor(e % 3600 / 60), r = Math.floor(e % 60), i = "";
33
+ return t > 0 && (i += "" + t + ":" + (n < 10 ? "0" : "")), i += "" + n + ":" + (r < 10 ? "0" : ""), i += "" + r, i;
34
+ }
35
+ };
36
+ e([t({
37
+ context: n,
38
+ subscribe: !0
39
+ }), s({ attribute: !1 })], c.prototype, "playerState", void 0), e([s({ type: String })], c.prototype, "format", void 0), c = e([o("ui-audio-time-display")], c);
40
+ export { c as UiAudioTimeDisplay };
@@ -0,0 +1,64 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
+ import { audioPlayerContext as n } from "../utils/audio-context.js";
4
+ import { LitElement as r, css as i, html as a } from "lit";
5
+ import { customElement as o, property as s } from "lit/decorators.js";
6
+ import "@material/web/icon/icon.js";
7
+ import "@material/web/slider/slider.js";
8
+ import "@material/web/iconbutton/icon-button.js";
9
+ var c = class extends r {
10
+ static {
11
+ this.styles = i`
12
+ :host {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: 8px;
16
+ width: 100%;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ md-slider {
21
+ flex: 1;
22
+ min-width: 0; /* Prevent flex overflow */
23
+ width: 100%;
24
+ --md-slider-inactive-track-color: var(
25
+ --md-sys-color-outline-variant,
26
+ #c4c7c5
27
+ );
28
+ }
29
+
30
+ md-icon-button {
31
+ color: var(--md-sys-color-on-surface-variant, #444);
32
+ }
33
+ `;
34
+ }
35
+ render() {
36
+ let e = this.playerState?.volume ?? 1, t = this.playerState?.muted ?? !1, n = "volume_up";
37
+ return t || e === 0 ? n = "volume_off" : e < .5 && (n = "volume_down"), a`
38
+ <md-icon-button @click="${this._toggleMute}" part="button">
39
+ <md-icon>${n}</md-icon>
40
+ </md-icon-button>
41
+ <md-slider
42
+ part="slider"
43
+ min="0"
44
+ max="1"
45
+ value="${t ? 0 : e}"
46
+ step="0.01"
47
+ ?disabled="${!this.playerState?.src}"
48
+ @input="${this._handleInput}"
49
+ ></md-slider>
50
+ `;
51
+ }
52
+ _handleInput(e) {
53
+ let t = e.target;
54
+ this.playerState && this.playerState.setVolume(t.value);
55
+ }
56
+ _toggleMute() {
57
+ this.playerState && this.playerState.toggleMute();
58
+ }
59
+ };
60
+ e([t({
61
+ context: n,
62
+ subscribe: !0
63
+ }), s({ attribute: !1 })], c.prototype, "playerState", void 0), c = e([o("ui-audio-volume-slider")], c);
64
+ export { c as UiAudioVolumeSlider };
@@ -0,0 +1,128 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { applyCanvasEdgeFade as t, getNormalizedFrequencyData as n } from "../utils/audio-utils.js";
3
+ import { LitElement as r, css as i, html as a } from "lit";
4
+ import { customElement as o, property as s, query as c } from "lit/decorators.js";
5
+ /**
6
+ * Copyright 2026 Google LLC
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */
20
+ var l = class extends r {
21
+ constructor(...e) {
22
+ 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 = [];
23
+ }
24
+ static {
25
+ this.styles = i`
26
+ :host {
27
+ display: block;
28
+ width: 100%;
29
+ }
30
+ .container {
31
+ position: relative;
32
+ width: 100%;
33
+ }
34
+ canvas {
35
+ position: absolute;
36
+ top: 0;
37
+ left: 0;
38
+ display: block;
39
+ height: 100%;
40
+ width: 100%;
41
+ }
42
+ `;
43
+ }
44
+ render() {
45
+ return a`
46
+ <div class="container" style="height: ${this.height}px;">
47
+ <canvas></canvas>
48
+ </div>
49
+ `;
50
+ }
51
+ firstUpdated() {
52
+ this._resizeObserver = new ResizeObserver(() => {
53
+ this._handleResize();
54
+ }), this._resizeObserver.observe(this._container), this._startAnimationLoop();
55
+ }
56
+ updated(e) {
57
+ 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);
58
+ }
59
+ disconnectedCallback() {
60
+ super.disconnectedCallback(), this._resizeObserver && this._resizeObserver.disconnect(), this._animationFrameId && cancelAnimationFrame(this._animationFrameId);
61
+ }
62
+ _handleResize() {
63
+ if (!this._canvas || !this._container) return;
64
+ let e = this._container.getBoundingClientRect(), t = window.devicePixelRatio || 1;
65
+ 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`;
66
+ let n = this._canvas.getContext("2d");
67
+ n && n.scale(t, t), this._renderFrame();
68
+ }
69
+ _startAnimationLoop() {
70
+ let e = (t) => {
71
+ this._updateData(t), this._renderFrame(), this._animationFrameId = requestAnimationFrame(e);
72
+ };
73
+ this._animationFrameId = requestAnimationFrame(e);
74
+ }
75
+ _updateData(e) {
76
+ if (!this._canvas) return;
77
+ let t = this._canvas.getBoundingClientRect(), r = Math.floor(t.width / (this.barWidth + this.barGap));
78
+ if (this.active && this.analyserNode && this._dataArray) {
79
+ if (e - this._lastUpdateTime > this.updateRate) {
80
+ this._lastUpdateTime = e;
81
+ let t = n(this.analyserNode, this._dataArray), i = Math.floor(t.length * .05), a = Math.floor(t.length * .4), o = t.slice(i, a), s = Math.floor(r / 2), c = Array(r).fill(.05), l = o.length - 1;
82
+ for (let e = 0; e <= s; e++) {
83
+ let t = e / s, n = o[Math.floor(t * l)] || 0;
84
+ t > .8 && (n *= 1 - (t - .8) * 5);
85
+ let i = Math.max(.05, Math.min(1, n * this.sensitivity)), a = s + e, u = s - e;
86
+ a < r && (c[a] = i), u >= 0 && (c[u] = i);
87
+ }
88
+ this._currentBars = c, this._lastActiveData = [...c];
89
+ }
90
+ } else if (this.processing && !this.active) {
91
+ this._processingTime += .03, this._transitionProgress = Math.min(1, this._transitionProgress + .02);
92
+ let e = Array(r).fill(.05), t = Math.floor(r / 2);
93
+ for (let n = 0; n < r; n++) {
94
+ let r = (n - t) / t, i = 1 - Math.abs(r) * .4, a = Math.sin(this._processingTime * 1.5 + r * 3) * .25, o = Math.sin(this._processingTime * .8 - r * 2) * .2, s = Math.cos(this._processingTime * 2 + r) * .15, c = (.2 + (a + o + s)) * i, l = c;
95
+ if (this._lastActiveData.length > 0 && this._transitionProgress < 1) {
96
+ let e = Math.min(n, this._lastActiveData.length - 1);
97
+ l = (this._lastActiveData[e] || 0) * (1 - this._transitionProgress) + c * this._transitionProgress;
98
+ }
99
+ e[n] = Math.max(.05, Math.min(1, l));
100
+ }
101
+ this._currentBars = e;
102
+ } else if (this._currentBars.length > 0) {
103
+ let e = !0;
104
+ 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);
105
+ e && (this._currentBars = []);
106
+ }
107
+ }
108
+ _renderFrame() {
109
+ if (!this._canvas) return;
110
+ let e = this._canvas.getContext("2d");
111
+ if (!e) return;
112
+ let n = this._canvas.getBoundingClientRect();
113
+ e.clearRect(0, 0, n.width, n.height);
114
+ let r = getComputedStyle(this), i = this.barColor;
115
+ if (!i) {
116
+ let e = r.getPropertyValue("--md-sys-color-primary").trim(), t = r.getPropertyValue("color").trim();
117
+ i = e || t || "#0066cc";
118
+ }
119
+ let a = this.barWidth + this.barGap, o = Math.floor(n.width / a), s = n.height / 2;
120
+ for (let t = 0; t < o && t < this._currentBars.length; t++) {
121
+ let r = this._currentBars[t] || .05, o = t * a, c = Math.max(this.barHeight, r * n.height * .8), l = s - c / 2;
122
+ e.fillStyle = i, e.globalAlpha = .4 + r * .6, this.barRadius > 0 ? (e.beginPath(), e.roundRect(o, l, this.barWidth, c, this.barRadius), e.fill()) : e.fillRect(o, l, this.barWidth, c);
123
+ }
124
+ this.fadeEdges && t(e, n.width, n.height, this.fadeWidth), e.globalAlpha = 1;
125
+ }
126
+ };
127
+ e([s({ type: Boolean })], l.prototype, "active", void 0), e([s({ type: Boolean })], l.prototype, "processing", void 0), e([s({ attribute: !1 })], l.prototype, "analyserNode", void 0), e([s({ type: Number })], l.prototype, "barWidth", void 0), e([s({ type: Number })], l.prototype, "barHeight", void 0), e([s({ type: Number })], l.prototype, "barGap", void 0), e([s({ type: Number })], l.prototype, "barRadius", void 0), e([s({ type: String })], l.prototype, "barColor", void 0), e([s({ type: Boolean })], l.prototype, "fadeEdges", void 0), e([s({ type: Number })], l.prototype, "fadeWidth", void 0), e([s({ type: Number })], l.prototype, "height", void 0), e([s({ type: Number })], l.prototype, "sensitivity", void 0), e([s({ type: Number })], l.prototype, "updateRate", void 0), e([c("canvas")], l.prototype, "_canvas", void 0), e([c(".container")], l.prototype, "_container", void 0), l = e([o("ui-live-waveform")], l);
128
+ export { l as UiLiveWaveform };