@ghchinoy/lit-audio-ui 0.1.1 → 0.1.2
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/dist/_virtual/_@oxc-project_runtime@0.113.0/helpers/decorate.js +7 -0
- package/dist/components/scream-voice-button.js +64 -0
- package/dist/components/ui-audio-play-button.js +84 -0
- package/dist/components/ui-audio-player.js +61 -0
- package/dist/components/ui-audio-progress-slider.js +52 -0
- package/dist/components/ui-audio-provider.js +140 -0
- package/dist/components/ui-audio-time-display.js +40 -0
- package/dist/components/ui-audio-volume-slider.js +64 -0
- package/dist/components/ui-live-waveform.js +128 -0
- package/dist/components/ui-mic-selector.js +245 -0
- package/dist/components/ui-scrolling-waveform.js +107 -0
- package/dist/components/ui-shimmering-text.js +95 -0
- package/dist/components/ui-showcase-card.js +140 -0
- package/dist/components/ui-voice-button.js +208 -0
- package/dist/components/ui-voice-picker.js +356 -0
- package/dist/components/ui-waveform.js +89 -0
- package/dist/index.js +17 -0
- package/dist/node_modules/@lit/context/lib/context-request-event.js +14 -0
- package/dist/node_modules/@lit/context/lib/controllers/context-consumer.js +26 -0
- package/dist/node_modules/@lit/context/lib/controllers/context-provider.js +34 -0
- package/dist/node_modules/@lit/context/lib/create-context.js +9 -0
- package/dist/node_modules/@lit/context/lib/decorators/consume.js +27 -0
- package/dist/node_modules/@lit/context/lib/decorators/provide.js +54 -0
- package/dist/node_modules/@lit/context/lib/value-notifier.js +37 -0
- package/dist/utils/audio-context.js +8 -0
- package/dist/utils/audio-utils.js +27 -0
- package/package.json +8 -5
- package/dist/scream-audio-ui.es.js +0 -2001
|
@@ -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 };
|