@ghchinoy/lit-audio-ui 0.3.5 → 0.4.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.
@@ -1,8 +1,8 @@
1
1
  import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import "@material/web/icon/icon.js";
2
3
  import { LitElement as t, css as n, html as r } from "lit";
3
4
  import { customElement as i, property as a } from "lit/decorators.js";
4
5
  import "@material/web/button/filled-button.js";
5
- import "@material/web/icon/icon.js";
6
6
  /**
7
7
  * Copyright 2026 Google LLC
8
8
  *
@@ -0,0 +1,126 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import "@material/web/icon/icon.js";
3
+ import { LitElement as t, css as n, html as r } from "lit";
4
+ import { customElement as i, property as a } 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 o = class extends t {
21
+ constructor(...e) {
22
+ super(...e), this.flipped = !1, this.axis = "y", this.duration = "0.6s";
23
+ }
24
+ static {
25
+ this.styles = n`
26
+ :host {
27
+ display: block;
28
+ perspective: 1000px;
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+
33
+ .flip-container {
34
+ position: relative;
35
+ width: 100%;
36
+ height: 100%;
37
+ text-align: center;
38
+ transition: transform var(--ui-3d-flip-duration, 0.6s);
39
+ transform-style: preserve-3d;
40
+ }
41
+
42
+ :host([flipped]) .flip-container {
43
+ transform: rotateY(180deg);
44
+ }
45
+
46
+ :host([flipped][axis='x']) .flip-container {
47
+ transform: rotateX(180deg);
48
+ }
49
+
50
+ .front,
51
+ .back {
52
+ position: absolute;
53
+ width: 100%;
54
+ height: 100%;
55
+ backface-visibility: hidden;
56
+ -webkit-backface-visibility: hidden;
57
+ display: flex;
58
+ flex-direction: column;
59
+ border-radius: inherit;
60
+ overflow: hidden;
61
+ }
62
+
63
+ .back {
64
+ transform: rotateY(180deg);
65
+ background: var(--md-sys-color-surface, #ffffff);
66
+ }
67
+
68
+ :host([axis='x']) .back {
69
+ transform: rotateX(180deg);
70
+ }
71
+
72
+ /* Slot for optional trigger icon */
73
+ .flip-trigger {
74
+ position: absolute;
75
+ top: 12px;
76
+ right: 12px;
77
+ z-index: 10;
78
+ cursor: pointer;
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ }
83
+ `;
84
+ }
85
+ render() {
86
+ return r`
87
+ <div
88
+ class="flip-container"
89
+ style="--ui-3d-flip-duration: ${this.duration}"
90
+ >
91
+ <div class="front">
92
+ <slot name="front"></slot>
93
+ <div class="flip-trigger" @click=${this.toggle}>
94
+ <slot name="flip-icon">
95
+ <!-- Default info icon if nothing provided -->
96
+ <md-icon style="color: white; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));">info</md-icon>
97
+ </slot>
98
+ </div>
99
+ </div>
100
+ <div class="back">
101
+ <slot name="back"></slot>
102
+ <div class="flip-trigger" @click=${this.toggle}>
103
+ <slot name="flip-icon-back">
104
+ <md-icon style="color: var(--md-sys-color-primary);">close</md-icon>
105
+ </slot>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ `;
110
+ }
111
+ toggle() {
112
+ this.flipped = !this.flipped, this.dispatchEvent(new CustomEvent("flip-change", {
113
+ detail: { flipped: this.flipped },
114
+ bubbles: !0,
115
+ composed: !0
116
+ }));
117
+ }
118
+ };
119
+ e([a({
120
+ type: Boolean,
121
+ reflect: !0
122
+ })], o.prototype, "flipped", void 0), e([a({
123
+ type: String,
124
+ reflect: !0
125
+ })], o.prototype, "axis", void 0), e([a({ type: String })], o.prototype, "duration", void 0), o = e([i("ui-3d-flip")], o);
126
+ export { o as Ui3dFlip };
@@ -0,0 +1,46 @@
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 "@material/web/icon/icon.js";
5
+ import { LitElement as r, css as i, html as a } from "lit";
6
+ import { customElement as o } from "lit/decorators.js";
7
+ import "@material/web/iconbutton/icon-button.js";
8
+ /**
9
+ * Copyright 2026 Google LLC
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+ var s = class extends r {
24
+ static {
25
+ this.styles = i`
26
+ :host {
27
+ display: inline-block;
28
+ }
29
+ `;
30
+ }
31
+ render() {
32
+ return a`
33
+ <md-icon-button
34
+ ?disabled=${!(this.playerState && this.playerState.items.length > 0 && (this.playerState.currentIndex < this.playerState.items.length - 1 || this.playerState.autoAdvance))}
35
+ @click=${() => this.playerState?.next()}
36
+ >
37
+ <md-icon>skip_next</md-icon>
38
+ </md-icon-button>
39
+ `;
40
+ }
41
+ };
42
+ e([t({
43
+ context: n,
44
+ subscribe: !0
45
+ })], s.prototype, "playerState", void 0), s = e([o("ui-audio-next-button")], s);
46
+ export { s as UiAudioNextButton };
@@ -1,9 +1,9 @@
1
1
  import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
2
  import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
3
  import { audioPlayerContext as n } from "../utils/audio-context.js";
4
+ import "@material/web/icon/icon.js";
4
5
  import { LitElement as r, css as i, html as a } from "lit";
5
6
  import { customElement as o, property as s } from "lit/decorators.js";
6
- import "@material/web/icon/icon.js";
7
7
  import "@material/web/iconbutton/filled-icon-button.js";
8
8
  import "@material/web/progress/circular-progress.js";
9
9
  var c = class extends r {
@@ -0,0 +1,46 @@
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 "@material/web/icon/icon.js";
5
+ import { LitElement as r, css as i, html as a } from "lit";
6
+ import { customElement as o } from "lit/decorators.js";
7
+ import "@material/web/iconbutton/icon-button.js";
8
+ /**
9
+ * Copyright 2026 Google LLC
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+ var s = class extends r {
24
+ static {
25
+ this.styles = i`
26
+ :host {
27
+ display: inline-block;
28
+ }
29
+ `;
30
+ }
31
+ render() {
32
+ return a`
33
+ <md-icon-button
34
+ ?disabled=${!(this.playerState && this.playerState.items.length > 0 && (this.playerState.currentIndex > 0 || this.playerState.autoAdvance))}
35
+ @click=${() => this.playerState?.previous()}
36
+ >
37
+ <md-icon>skip_previous</md-icon>
38
+ </md-icon-button>
39
+ `;
40
+ }
41
+ };
42
+ e([t({
43
+ context: n,
44
+ subscribe: !0
45
+ })], s.prototype, "playerState", void 0), s = e([o("ui-audio-prev-button")], s);
46
+ export { s as UiAudioPrevButton };
@@ -5,7 +5,7 @@ import { LitElement as r, css as i, html as a } from "lit";
5
5
  import { customElement as o, property as s, query as c, state as l } from "lit/decorators.js";
6
6
  var u = class extends r {
7
7
  constructor(...e) {
8
- super(...e), this.src = "", this._animationFrameId = 0, this.state = {
8
+ super(...e), this.src = "", this.items = [], this.autoAdvance = !0, this._animationFrameId = 0, this.state = {
9
9
  src: "",
10
10
  isPlaying: !1,
11
11
  isBuffering: !1,
@@ -13,13 +13,19 @@ var u = class extends r {
13
13
  duration: 0,
14
14
  volume: 1,
15
15
  muted: !1,
16
+ items: [],
17
+ currentIndex: -1,
18
+ autoAdvance: !0,
16
19
  analyserNode: void 0,
17
20
  play: () => this.play(),
18
21
  pause: () => this.pause(),
19
22
  togglePlay: () => this._togglePlay(),
20
23
  seek: (e) => this._seek(e),
21
24
  setVolume: (e) => this._setVolume(e),
22
- toggleMute: () => this._toggleMute()
25
+ toggleMute: () => this._toggleMute(),
26
+ next: () => this.next(),
27
+ previous: () => this.previous(),
28
+ select: (e) => this.select(e)
23
29
  };
24
30
  }
25
31
  static {
@@ -55,10 +61,10 @@ var u = class extends r {
55
61
  isPlaying: !1,
56
62
  currentTime: 0,
57
63
  error: void 0
58
- });
64
+ }), e.has("items") && (this._updateState({ items: this.items }), this.items.length > 0 && !this.src && this.state.currentIndex === -1 && this.select(0)), e.has("autoAdvance") && this._updateState({ autoAdvance: this.autoAdvance });
59
65
  }
60
66
  updated(e) {
61
- e.has("src") && this._audioEl && this._audioEl.load();
67
+ e.has("src") && this._audioEl && (this._audioEl.load(), this.state.isPlaying && this.play());
62
68
  }
63
69
  disconnectedCallback() {
64
70
  super.disconnectedCallback(), this._animationFrameId && cancelAnimationFrame(this._animationFrameId), this._audioContext && this._audioContext.state !== "closed" && this._audioContext.close();
@@ -103,14 +109,34 @@ var u = class extends r {
103
109
  _toggleMute() {
104
110
  this._audioEl && (this._audioEl.muted = !this._audioEl.muted, this._updateState({ muted: this._audioEl.muted }));
105
111
  }
112
+ next() {
113
+ if (this.items.length === 0) return;
114
+ let e = (this.state.currentIndex + 1) % this.items.length;
115
+ this.select(e);
116
+ }
117
+ previous() {
118
+ if (this.items.length === 0) return;
119
+ let e = (this.state.currentIndex - 1 + this.items.length) % this.items.length;
120
+ this.select(e);
121
+ }
122
+ select(e) {
123
+ if (e >= 0 && e < this.items.length) {
124
+ let t = this.items[e];
125
+ this.src = t.src, this._updateState({
126
+ currentIndex: e,
127
+ src: t.src,
128
+ currentTime: 0
129
+ });
130
+ }
131
+ }
106
132
  _handleLoadedMetadata() {
107
133
  this._updateState({ duration: this._audioEl.duration });
108
134
  }
109
135
  _handleEnded() {
110
- this._updateState({
136
+ this.autoAdvance && this.items.length > 0 ? (this.next(), this.play()) : (this._updateState({
111
137
  isPlaying: !1,
112
138
  currentTime: 0
113
- }), this._audioEl.currentTime = 0;
139
+ }), this._audioEl.currentTime = 0);
114
140
  }
115
141
  _handlePlaying() {
116
142
  this._updateState({
@@ -136,5 +162,5 @@ var u = class extends r {
136
162
  this._animationFrameId = requestAnimationFrame(e);
137
163
  }
138
164
  };
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);
165
+ e([s({ type: String })], u.prototype, "src", void 0), e([s({ type: Array })], u.prototype, "items", void 0), e([s({ type: Boolean })], u.prototype, "autoAdvance", 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
166
  export { u as UiAudioProvider };
@@ -5,7 +5,7 @@ import { LitElement as r, css as i, html as a } from "lit";
5
5
  import { customElement as o, property as s } from "lit/decorators.js";
6
6
  var c = class extends r {
7
7
  constructor(...e) {
8
- super(...e), this.format = "full";
8
+ super(...e), this.format = "combined", this.separator = " / ", this.compact = !1;
9
9
  }
10
10
  static {
11
11
  this.styles = i`
@@ -24,17 +24,16 @@ var c = class extends r {
24
24
  if (this.format === "remaining") {
25
25
  let n = Math.max(0, t - e);
26
26
  return a`-${this._formatTime(n)}`;
27
- } else return a`${this._formatTime(e)} /
28
- ${t ? this._formatTime(t) : "--:--"}`;
27
+ } else return a`${this._formatTime(e)}${this.separator}${t ? this._formatTime(t) : "--:--"}`;
29
28
  }
30
29
  _formatTime(e) {
31
- if (!e || isNaN(e)) return "0:00";
30
+ if (!e || isNaN(e)) return this.compact, "0:00";
32
31
  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;
32
+ return t > 0 ? i += "" + t + ":" + (n < 10 ? "0" : "") : this.compact, i += "" + n + ":" + (r < 10 ? "0" : ""), i += "" + r, i;
34
33
  }
35
34
  };
36
35
  e([t({
37
36
  context: n,
38
37
  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);
38
+ }), s({ attribute: !1 })], c.prototype, "playerState", void 0), e([s({ type: String })], c.prototype, "format", void 0), e([s({ type: String })], c.prototype, "separator", void 0), e([s({ type: Boolean })], c.prototype, "compact", void 0), c = e([o("ui-audio-time-display")], c);
40
39
  export { c as UiAudioTimeDisplay };
@@ -1,9 +1,9 @@
1
1
  import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
2
  import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
3
  import { audioPlayerContext as n } from "../utils/audio-context.js";
4
+ import "@material/web/icon/icon.js";
4
5
  import { LitElement as r, css as i, html as a } from "lit";
5
6
  import { customElement as o, property as s } from "lit/decorators.js";
6
- import "@material/web/icon/icon.js";
7
7
  import "@material/web/slider/slider.js";
8
8
  import "@material/web/iconbutton/icon-button.js";
9
9
  var c = class extends r {
@@ -1,8 +1,8 @@
1
1
  import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
2
  import "./ui-live-waveform.js";
3
+ import "@material/web/icon/icon.js";
3
4
  import { LitElement as t, css as n, html as r } from "lit";
4
5
  import { customElement as i, property as a, query as o, state as s } from "lit/decorators.js";
5
- import "@material/web/icon/icon.js";
6
6
  import "@material/web/menu/menu.js";
7
7
  import "@material/web/menu/menu-item.js";
8
8
  import "@material/web/divider/divider.js";
@@ -32,7 +32,12 @@ var c = class extends t {
32
32
  this._initThree();
33
33
  }
34
34
  updated(e) {
35
- e.has("colors") && this._updateColors();
35
+ e.has("colors") && this._updateColors(), e.has("seed") && this._updateSeed();
36
+ }
37
+ _updateSeed() {
38
+ if (!this._mesh) return;
39
+ let e = this._splitmix32(this.seed), t = new Float32Array(Array.from({ length: 7 }, () => e() * Math.PI * 2));
40
+ this._mesh.material.uniforms.uOffsets.value = t;
36
41
  }
37
42
  _updateColors() {
38
43
  if (!(!this._targetColor1 || !this._targetColor2)) if (this.colors && this.colors.length === 2) this._targetColor1.set(this.colors[0]), this._targetColor2.set(this.colors[1]);
@@ -0,0 +1,106 @@
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 "@material/web/icon/icon.js";
5
+ import { LitElement as r, css as i, html as a } from "lit";
6
+ import { customElement as o, property as s } from "lit/decorators.js";
7
+ import "@material/web/list/list.js";
8
+ import "@material/web/list/list-item.js";
9
+ /**
10
+ * Copyright 2026 Google LLC
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+ var c = class extends r {
25
+ constructor(...e) {
26
+ super(...e), this.header = "Queue", this.emptyText = "No tracks in queue";
27
+ }
28
+ static {
29
+ this.styles = i`
30
+ :host {
31
+ display: block;
32
+ background: var(--md-sys-color-surface-container-low, transparent);
33
+ border-radius: 12px;
34
+ overflow: hidden;
35
+ font-family: inherit;
36
+ color-scheme: light dark;
37
+ }
38
+
39
+ .playlist-header {
40
+ padding: 16px 20px;
41
+ font-size: 0.75rem;
42
+ font-weight: 700;
43
+ text-transform: uppercase;
44
+ letter-spacing: 0.05em;
45
+ color: var(--md-sys-color-primary);
46
+ border-bottom: 1px solid var(--md-sys-color-outline-variant);
47
+ }
48
+
49
+ md-list {
50
+ background: transparent;
51
+ --md-list-container-color: transparent;
52
+ }
53
+
54
+ md-list-item {
55
+ --md-list-item-label-text-font: inherit;
56
+ --md-list-item-supporting-text-font: inherit;
57
+ cursor: pointer;
58
+ }
59
+
60
+ md-list-item[selected] {
61
+ --md-list-item-label-text-color: var(--md-sys-color-primary);
62
+ background: var(--md-sys-color-primary-container, rgba(0, 102, 204, 0.1));
63
+ }
64
+
65
+ .now-playing-icon {
66
+ color: var(--md-sys-color-primary);
67
+ font-size: 18px;
68
+ }
69
+
70
+ .empty-state {
71
+ padding: 32px;
72
+ text-align: center;
73
+ color: var(--md-sys-color-on-surface-variant);
74
+ font-size: 0.9rem;
75
+ }
76
+ `;
77
+ }
78
+ render() {
79
+ let e = this.playerState?.items || [], t = this.playerState?.currentIndex ?? -1;
80
+ return a`
81
+ <div class="playlist-header">${this.header}</div>
82
+
83
+ ${e.length === 0 ? a`<div class="empty-state">${this.emptyText}</div>` : a`
84
+ <md-list>
85
+ ${e.map((e, n) => a`
86
+ <md-list-item
87
+ ?selected=${n === t}
88
+ @click=${() => this.playerState?.select(n)}
89
+ >
90
+ <div slot="headline">${e.title || "Untitled Track"}</div>
91
+ <div slot="supporting-text">${e.artist || "Unknown Artist"}</div>
92
+ ${n === t ? a`<md-icon slot="start" class="now-playing-icon"
93
+ >${this.playerState?.isPlaying ? "graphic_eq" : "play_arrow"}</md-icon
94
+ >` : a`<md-icon slot="start">music_note</md-icon>`}
95
+ </md-list-item>
96
+ `)}
97
+ </md-list>
98
+ `}
99
+ `;
100
+ }
101
+ };
102
+ e([t({
103
+ context: n,
104
+ subscribe: !0
105
+ })], c.prototype, "playerState", void 0), e([s({ type: String })], c.prototype, "header", void 0), e([s({ type: String })], c.prototype, "emptyText", void 0), c = e([o("ui-playlist")], c);
106
+ export { c as UiPlaylist };
@@ -0,0 +1,75 @@
1
+ import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
+ import { getNormalizedFrequencyData as t } from "../utils/audio-utils.js";
3
+ import { c as n } from "../node_modules/@lit/context/lib/decorators/consume.js";
4
+ import { audioPlayerContext as r } from "../utils/audio-context.js";
5
+ import { LitElement as i, css as a, html as o } from "lit";
6
+ import { customElement as s, property as c, query as l } from "lit/decorators.js";
7
+ /**
8
+ * Copyright 2026 Google LLC
9
+ *
10
+ * Licensed under the Apache License, Version 2.0 (the "License");
11
+ * you may not use this file except in compliance with the License.
12
+ * You may obtain a copy of the License at
13
+ *
14
+ * http://www.apache.org/licenses/LICENSE-2.0
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ var u = class extends i {
23
+ constructor(...e) {
24
+ super(...e), this.barWidth = 4, this.barGap = 2, this.height = 100, this._animationFrameId = 0;
25
+ }
26
+ static {
27
+ this.styles = a`
28
+ :host {
29
+ display: block;
30
+ width: 100%;
31
+ overflow: hidden;
32
+ }
33
+ canvas {
34
+ display: block;
35
+ width: 100%;
36
+ height: 100%;
37
+ }
38
+ `;
39
+ }
40
+ render() {
41
+ return o`<canvas style="height: ${this.height}px;"></canvas>`;
42
+ }
43
+ firstUpdated() {
44
+ this._startLoop();
45
+ }
46
+ disconnectedCallback() {
47
+ super.disconnectedCallback(), cancelAnimationFrame(this._animationFrameId);
48
+ }
49
+ _startLoop() {
50
+ let e = () => {
51
+ this._renderFrame(), this._animationFrameId = requestAnimationFrame(e);
52
+ };
53
+ this._animationFrameId = requestAnimationFrame(e);
54
+ }
55
+ _renderFrame() {
56
+ if (!this._canvas || !this.playerState?.analyserNode) return;
57
+ let e = this.playerState.analyserNode;
58
+ this._dataArray ||= new Uint8Array(e.frequencyBinCount);
59
+ let n = this._canvas.getContext("2d");
60
+ if (!n) return;
61
+ let r = this._canvas.getBoundingClientRect(), i = window.devicePixelRatio || 1;
62
+ this._canvas.width !== r.width * i && (this._canvas.width = r.width * i, this._canvas.height = r.height * i, n.scale(i, i)), n.clearRect(0, 0, r.width, r.height);
63
+ let a = t(e, this._dataArray), o = this.barWidth + this.barGap, s = Math.floor(r.width / o), c = getComputedStyle(this), l = this.color;
64
+ l ||= c.getPropertyValue("--md-sys-color-primary").trim() || "#0066cc", n.fillStyle = l;
65
+ for (let e = 0; e < s; e++) {
66
+ let t = (a[Math.floor(e / s * (a.length * .6))] || 0) * r.height, i = e * o, c = r.height - t;
67
+ n.fillRect(i, c, this.barWidth, t);
68
+ }
69
+ }
70
+ };
71
+ e([n({
72
+ context: r,
73
+ subscribe: !0
74
+ })], u.prototype, "playerState", void 0), e([c({ type: Number })], u.prototype, "barWidth", void 0), e([c({ type: Number })], u.prototype, "barGap", void 0), e([c({ type: Number })], u.prototype, "height", void 0), e([c({ type: String })], u.prototype, "color", void 0), e([l("canvas")], u.prototype, "_canvas", void 0), u = e([s("ui-spectrum-visualizer")], u);
75
+ export { u as UiSpectrumVisualizer };
@@ -1,9 +1,9 @@
1
1
  import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
2
  import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
3
  import { speechContext as n } from "../utils/speech-context.js";
4
+ import "@material/web/icon/icon.js";
4
5
  import { LitElement as r, css as i, html as a } from "lit";
5
6
  import { customElement as o } from "lit/decorators.js";
6
- import "@material/web/icon/icon.js";
7
7
  import "@material/web/iconbutton/icon-button.js";
8
8
  var s = class extends r {
9
9
  static {
@@ -1,9 +1,9 @@
1
1
  import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
2
  import { c as t } from "../node_modules/@lit/context/lib/decorators/consume.js";
3
3
  import { speechContext as n } from "../utils/speech-context.js";
4
+ import "@material/web/icon/icon.js";
4
5
  import { LitElement as r, css as i, html as a } from "lit";
5
6
  import { customElement as o, property as s } from "lit/decorators.js";
6
- import "@material/web/icon/icon.js";
7
7
  import "@material/web/iconbutton/filled-icon-button.js";
8
8
  var c = class extends r {
9
9
  constructor(...e) {
@@ -1,9 +1,9 @@
1
1
  import { __decorate as e } from "../_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js";
2
2
  import "./ui-live-waveform.js";
3
+ import "@material/web/icon/icon.js";
3
4
  import { LitElement as t, css as n, html as r } from "lit";
4
5
  import { customElement as i, property as a, state as o } from "lit/decorators.js";
5
6
  import "@material/web/button/filled-button.js";
6
- import "@material/web/icon/icon.js";
7
7
  import { classMap as s } from "lit/directives/class-map.js";
8
8
  import "@material/web/button/outlined-button.js";
9
9
  /**
@@ -36,13 +36,16 @@ var c = class extends t {
36
36
  .wrapper {
37
37
  display: flex;
38
38
  align-items: center;
39
+ justify-content: center;
39
40
  gap: 12px;
41
+ min-width: 100%;
40
42
  }
41
43
 
42
44
  md-filled-button,
43
45
  md-outlined-button {
44
46
  transition: all 0.2s ease-in-out;
45
47
  font-family: inherit;
48
+ min-width: 140px; /* Ensure enough room for text + wave */
46
49
  }
47
50
 
48
51
  /* Customize the button depending on the state */
@@ -111,6 +114,7 @@ var c = class extends t {
111
114
  background: transparent;
112
115
  border: none;
113
116
  transition: all 0.3s ease;
117
+ flex-shrink: 0;
114
118
  }
115
119
 
116
120
  .waveform-slot.idle {
@@ -124,12 +128,13 @@ var c = class extends t {
124
128
  opacity: 1;
125
129
  }
126
130
 
131
+ /* Waveform contrast colors based on state */
127
132
  .waveform-slot.recording {
128
- color: var(--md-sys-color-on-error-container, #410002);
133
+ --current-wave-color: var(--md-sys-color-error, #ba1a1a);
129
134
  }
130
135
 
131
136
  .waveform-slot.processing {
132
- color: var(--md-sys-color-on-secondary-container, #001d36);
137
+ --current-wave-color: var(--md-sys-color-primary, #0066cc);
133
138
  }
134
139
 
135
140
  .trailing-text {
@@ -217,7 +222,7 @@ var c = class extends t {
217
222
  .barRadius=${4}
218
223
  .fadeEdges=${!1}
219
224
  .sensitivity=${1.8}
220
- barColor="var(--ui-speech-wave-color, currentColor)"
225
+ barColor="var(--ui-speech-wave-color, var(--current-wave-color, currentColor))"
221
226
  height="20"
222
227
  style="position: absolute; inset: 0;"
223
228
  ></ui-live-waveform>