@prolibu-suite/cobalt-core 0.11.4 → 0.11.6
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/custom-elements.json +65 -31
- package/dist/cjs/co-audio-player.cjs.entry.js +198 -46
- package/dist/cjs/co-kbd_2.cjs.entry.js +10 -3
- package/dist/cjs/cobalt.cjs.js +1 -1
- package/dist/cjs/index-bHoidDwv.js +4 -4
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cobalt/cobalt.esm.js +1 -1
- package/dist/cobalt/p-4a7fd0b4.entry.js +1 -0
- package/dist/cobalt/p-868d1a18.entry.js +1 -0
- package/dist/collection/components/co-audio-player/co-audio-player.js +40 -47
- package/dist/collection/components/co-audio-player/co-audio-player.peaks-cache.js +104 -0
- package/dist/collection/components/co-audio-player/co-audio-player.utils.js +61 -0
- package/dist/collection/components/co-autocomplete/co-autocomplete.js +1 -1
- package/dist/collection/components/co-avatar-group/co-avatar-group.js +1 -1
- package/dist/collection/components/co-checkbox-group/co-checkbox-group.js +1 -1
- package/dist/collection/components/co-color-area/co-color-area.js +2 -2
- package/dist/collection/components/co-color-slider/co-color-slider.js +1 -1
- package/dist/collection/components/co-combobox/co-combobox.js +1 -1
- package/dist/collection/components/co-command-search/co-command-search.js +1 -1
- package/dist/collection/components/co-data-table/co-data-table.js +1 -1
- package/dist/collection/components/co-date-range-picker/co-date-range-picker.js +2 -2
- package/dist/collection/components/co-document-editor/co-document-editor.js +5 -5
- package/dist/collection/components/co-popover/co-popover.js +1 -1
- package/dist/collection/components/co-radio-group/co-radio-group.js +1 -1
- package/dist/collection/components/co-ref-field/co-ref-field.js +4 -4
- package/dist/collection/components/co-segmented-bar/co-segmented-bar.js +1 -1
- package/dist/collection/components/co-select/co-select.js +1 -1
- package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +1 -1
- package/dist/collection/components/co-toast/co-toast.js +2 -2
- package/dist/collection/components/co-tooltip/co-tooltip.css +14 -5
- package/dist/collection/components/co-tooltip/co-tooltip.js +29 -2
- package/dist/collection/components/co-transfer-list/co-transfer-list.js +1 -1
- package/dist/collection/components/co-tree-item/co-tree-item.js +2 -2
- package/dist/components/co-audio-player.js +1 -1
- package/dist/components/co-autocomplete.js +1 -1
- package/dist/components/co-avatar-group.js +1 -1
- package/dist/components/co-avatar.js +1 -1
- package/dist/components/co-combobox.js +1 -1
- package/dist/components/co-command-search.js +1 -1
- package/dist/components/co-data-table.js +1 -1
- package/dist/components/co-document-editor.js +1 -1
- package/dist/components/co-file-field.js +1 -1
- package/dist/components/co-list-item.js +1 -1
- package/dist/components/co-option-list.js +1 -1
- package/dist/components/co-ref-field.js +1 -1
- package/dist/components/co-sidebar-nav.js +1 -1
- package/dist/components/co-tooltip.js +1 -1
- package/dist/components/{p-BqFBZ1PT.js → p-BW10Sgiy.js} +1 -1
- package/dist/components/{p-VcrjNCew.js → p-BzNBPHen.js} +1 -1
- package/dist/components/p-V7HYy0VO.js +1 -0
- package/dist/esm/co-audio-player.entry.js +198 -46
- package/dist/esm/co-kbd_2.entry.js +10 -3
- package/dist/esm/cobalt.js +1 -1
- package/dist/esm/index-DJkaRtbb.js +4 -4
- package/dist/esm/loader.js +1 -1
- package/dist/types/components/co-audio-player/co-audio-player.d.ts +8 -1
- package/dist/types/components/co-audio-player/co-audio-player.peaks-cache.d.ts +58 -0
- package/dist/types/components/co-audio-player/co-audio-player.utils.d.ts +33 -0
- package/dist/types/components/co-tooltip/co-tooltip.d.ts +6 -0
- package/dist/types/components.d.ts +11 -0
- package/package.json +4 -4
- package/dist/cobalt/p-358a6c2a.entry.js +0 -1
- package/dist/cobalt/p-db623519.entry.js +0 -1
- package/dist/components/p-9VoFSbIH.js +0 -1
|
@@ -51,6 +51,67 @@ function resamplePeaks(channelData, bars) {
|
|
|
51
51
|
}
|
|
52
52
|
return peaks;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolución de la envolvente que se retiene por grabación, en bloques.
|
|
56
|
+
*
|
|
57
|
+
* Las barras EXACTAS del ancho vigente se calculan del PCM antes de soltarlo (ver
|
|
58
|
+
* `co-audio-player.peaks-cache`); la envolvente solo sirve los anchos que aparecen
|
|
59
|
+
* DESPUÉS (resize, o una segunda instancia que llega con el caché ya caliente). El
|
|
60
|
+
* reproductor deriva las barras del ancho (`floor(ancho / barDensity)`, ~853 a
|
|
61
|
+
* 2560 px), así que la envolvente tiene que ir muy por encima de eso: 65 536 bloques
|
|
62
|
+
* = ≥ 76 bloques por barra hasta 2560 px, y pesan 256 KB (Float32) frente a ~32 MB
|
|
63
|
+
* del PCM de 3 min a 44,1 kHz. Medido contra el exacto con PCM sintético de 3 min:
|
|
64
|
+
* desvío máx. ≈ 1 px de 40 en las barras que difieren (≈ 0,8 px con ruido blanco).
|
|
65
|
+
*/
|
|
66
|
+
const PEAK_ENVELOPE_BINS = 65536;
|
|
67
|
+
/** PCM → envolvente de a lo sumo `maxBins` bloques. Con `length ≤ maxBins` el bloque
|
|
68
|
+
* es de 1 muestra y la envolvente ES |PCM| (sin pérdida). */
|
|
69
|
+
function buildPeakEnvelope(channelData, maxBins = PEAK_ENVELOPE_BINS) {
|
|
70
|
+
const length = channelData.length;
|
|
71
|
+
const blockSize = Math.max(1, Math.ceil(length / Math.max(1, maxBins)));
|
|
72
|
+
const bins = Math.ceil(length / blockSize);
|
|
73
|
+
const peaks = new Float32Array(bins);
|
|
74
|
+
for (let b = 0; b < bins; b++) {
|
|
75
|
+
let max = 0;
|
|
76
|
+
const end = Math.min(length, (b + 1) * blockSize);
|
|
77
|
+
for (let j = b * blockSize; j < end; j++) {
|
|
78
|
+
const abs = Math.abs(channelData[j]);
|
|
79
|
+
if (abs > max)
|
|
80
|
+
max = abs;
|
|
81
|
+
}
|
|
82
|
+
peaks[b] = max;
|
|
83
|
+
}
|
|
84
|
+
return { peaks, blockSize, length };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Las mismas barras que `resamplePeaks(pcm, bars)`, calculadas desde la envolvente:
|
|
88
|
+
* mismo paso (`floor(length / bars)` muestras), mismo recorte de la cola y misma
|
|
89
|
+
* fórmula de altura. Cada barra toma el máximo de los bloques que TOCA su rango de
|
|
90
|
+
* muestras, así que el resultado es IDÉNTICO cuando `blockSize` es 1 o divide el
|
|
91
|
+
* paso, y en el caso general solo puede sumar el pico de un bloque parcial en cada
|
|
92
|
+
* borde (nunca queda por debajo del exacto).
|
|
93
|
+
*/
|
|
94
|
+
function resampleEnvelope(env, bars) {
|
|
95
|
+
const { peaks: blocks, blockSize, length: len } = env;
|
|
96
|
+
if (!len || bars <= 0)
|
|
97
|
+
return [];
|
|
98
|
+
const step = Math.max(1, Math.floor(len / bars));
|
|
99
|
+
const peaks = [];
|
|
100
|
+
for (let i = 0; i < bars; i++) {
|
|
101
|
+
const start = i * step;
|
|
102
|
+
const end = Math.min(start + step, len); // exclusivo, como el bucle de resamplePeaks
|
|
103
|
+
let max = 0;
|
|
104
|
+
if (start < end) {
|
|
105
|
+
const last = Math.floor((end - 1) / blockSize);
|
|
106
|
+
for (let b = Math.floor(start / blockSize); b <= last; b++) {
|
|
107
|
+
if (blocks[b] > max)
|
|
108
|
+
max = blocks[b];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
peaks.push(0.12 + max * 0.88);
|
|
112
|
+
}
|
|
113
|
+
return peaks;
|
|
114
|
+
}
|
|
54
115
|
/** Deterministic pseudo-waveform (stable render + testable — no Math.random). */
|
|
55
116
|
function fallbackPeaks(bars) {
|
|
56
117
|
if (bars <= 0)
|
|
@@ -70,6 +131,105 @@ function scrubFraction(clientX, rect) {
|
|
|
70
131
|
return Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
71
132
|
}
|
|
72
133
|
|
|
134
|
+
/** Tope de entradas. Cada una pesa ≤ 256 KB (`PEAK_ENVELOPE_BINS` Float32) más unas
|
|
135
|
+
* pocas listas de barras exactas, así que el caché lleno queda en ~4 MB. Una
|
|
136
|
+
* instancia montada conserva SU envolvente aunque la entrada se desaloje (es la
|
|
137
|
+
* misma referencia, no una copia). */
|
|
138
|
+
const PEAKS_CACHE_MAX = 16;
|
|
139
|
+
// Map conserva el orden de inserción: la primera clave es la menos usada (LRU).
|
|
140
|
+
const cache = new Map();
|
|
141
|
+
const inflight = new Map();
|
|
142
|
+
function remember(url, value) {
|
|
143
|
+
cache.delete(url);
|
|
144
|
+
cache.set(url, value);
|
|
145
|
+
while (cache.size > PEAKS_CACHE_MAX)
|
|
146
|
+
cache.delete(cache.keys().next().value);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Picos + duración de `url`: del caché si están, uniéndose a la carga en vuelo si
|
|
150
|
+
* otra instancia ya la pidió, o cargándolos. Solo se cachea un resultado real: un
|
|
151
|
+
* error (CORS, red, códec) o un `null` se devuelven tal cual y el próximo pedido
|
|
152
|
+
* reintenta.
|
|
153
|
+
*/
|
|
154
|
+
function loadAudioPeaks(url, wantBars, loader = decodeAudioPeaks) {
|
|
155
|
+
const hit = cache.get(url);
|
|
156
|
+
if (hit) {
|
|
157
|
+
remember(url, hit); // refresca su posición LRU
|
|
158
|
+
return Promise.resolve(hit);
|
|
159
|
+
}
|
|
160
|
+
const pending = inflight.get(url);
|
|
161
|
+
if (pending) {
|
|
162
|
+
if (wantBars)
|
|
163
|
+
pending.wanted.add(wantBars);
|
|
164
|
+
return pending.promise;
|
|
165
|
+
}
|
|
166
|
+
const wanted = new Set(wantBars ? [wantBars] : []);
|
|
167
|
+
const p = loader(url, wanted).then((res) => {
|
|
168
|
+
inflight.delete(url);
|
|
169
|
+
if (res)
|
|
170
|
+
remember(url, res);
|
|
171
|
+
return res;
|
|
172
|
+
}, (err) => {
|
|
173
|
+
inflight.delete(url);
|
|
174
|
+
throw err;
|
|
175
|
+
});
|
|
176
|
+
inflight.set(url, { promise: p, wanted });
|
|
177
|
+
return p;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Descarga + decode con Web Audio, reducido a la envolvente. El `AudioBuffer`
|
|
181
|
+
* (y con él el PCM completo) no sale de esta función: queda sin referencias
|
|
182
|
+
* apenas se calcula la envolvente.
|
|
183
|
+
*/
|
|
184
|
+
async function decodeAudioPeaks(url, wanted = new Set()) {
|
|
185
|
+
var _a;
|
|
186
|
+
let ctx;
|
|
187
|
+
try {
|
|
188
|
+
/**
|
|
189
|
+
* `OfflineAudioContext` FIRST, and a live `AudioContext` only as the
|
|
190
|
+
* fallback: decoding a waveform needs no audio OUTPUT, and a live context
|
|
191
|
+
* created before the user has interacted trips the browser's autoplay
|
|
192
|
+
* policy — "The AudioContext was not allowed to start. It must be resumed
|
|
193
|
+
* (or created) after a user gesture on the page", once per player. A page
|
|
194
|
+
* with 15 recordings logged it 15 times. The offline one has no output
|
|
195
|
+
* device, so the policy does not apply to it.
|
|
196
|
+
*
|
|
197
|
+
* Its arguments are placeholders: nothing is ever rendered through this
|
|
198
|
+
* context, it is only the host for `decodeAudioData`.
|
|
199
|
+
*/
|
|
200
|
+
const OfflineCtx = window.OfflineAudioContext || window.webkitOfflineAudioContext;
|
|
201
|
+
const Ctx = window.AudioContext || window.webkitAudioContext;
|
|
202
|
+
if ((!OfflineCtx && !Ctx) || typeof fetch === 'undefined')
|
|
203
|
+
return null;
|
|
204
|
+
ctx = OfflineCtx ? new OfflineCtx(1, 1, 44100) : new Ctx();
|
|
205
|
+
const buf = await (await fetch(url)).arrayBuffer();
|
|
206
|
+
const audioBuf = await ctx.decodeAudioData(buf);
|
|
207
|
+
const pcm = audioBuf.getChannelData(0);
|
|
208
|
+
return { envelope: buildPeakEnvelope(pcm), exact: exactBarsFor(pcm, wanted), duration: audioBuf.duration };
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
// Always release the context. Chrome caps concurrent AudioContexts per
|
|
212
|
+
// document, so leaking one per failed decode would eventually stop every
|
|
213
|
+
// later player on the page from decoding at all. `close()` lives on
|
|
214
|
+
// AudioContext, not on every BaseAudioContext (an OfflineAudioContext
|
|
215
|
+
// needs no closing and may not implement it): optional call, not a cast.
|
|
216
|
+
try {
|
|
217
|
+
await ((_a = ctx === null || ctx === void 0 ? void 0 : ctx.close) === null || _a === void 0 ? void 0 : _a.call(ctx));
|
|
218
|
+
}
|
|
219
|
+
catch ( /* already closed */_b) { /* already closed */ }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Barras exactas para cada ancho que las instancias en espera quieren ahora mismo. */
|
|
223
|
+
function exactBarsFor(pcm, wanted) {
|
|
224
|
+
const exact = new Map();
|
|
225
|
+
for (const want of wanted) {
|
|
226
|
+
const bars = want();
|
|
227
|
+
if (bars > 0 && !exact.has(bars))
|
|
228
|
+
exact.set(bars, resamplePeaks(pcm, bars));
|
|
229
|
+
}
|
|
230
|
+
return exact;
|
|
231
|
+
}
|
|
232
|
+
|
|
73
233
|
const coAudioPlayerCss = () => `:host{display:block;font-family:var(--co-font-family-primary)}.co-audio-player{display:flex;align-items:center;gap:var(--co-spacing-md);padding:var(--co-spacing-sm) var(--co-spacing-md);border:1px solid var(--co-semantic-border-default);border-radius:var(--co-border-radius-xs);background:var(--co-semantic-surface-secondary);min-width:180px}.co-audio-player--compact{gap:var(--co-spacing-sm2);padding:var(--co-spacing-sm2) var(--co-spacing-sm);min-width:140px}.co-audio-player__play{flex:0 0 auto;width:28px;height:28px;border:none;border-radius:50%;cursor:pointer;background:var(--co-semantic-surface-primary);color:var(--co-semantic-text-inverse);display:inline-flex;align-items:center;justify-content:center}.co-audio-player--compact .co-audio-player__play{width:24px;height:24px}.co-audio-player__play:disabled{opacity:.5;cursor:not-allowed}.co-audio-player__play:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:2px}.co-audio-player__wave{flex:1;height:40px;min-width:0;cursor:pointer;display:flex;align-items:center}.co-audio-player__wave:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:2px;border-radius:var(--co-border-radius-xxs)}.co-audio-player__svg{width:100%;height:100%;display:block}.co-audio-player__bar{fill:var(--co-semantic-border-field);transition:fill var(--co-motion-duration-fast)}.co-audio-player__wave:hover .co-audio-player__bar{fill:var(--co-semantic-text-muted)}.co-audio-player__bar--played{fill:var(--co-semantic-surface-primary)}.co-audio-player__time{flex:0 0 auto;font-size:var(--co-font-size-12);color:var(--co-semantic-text-muted);font-variant-numeric:tabular-nums;min-width:62px;text-align:right}.co-audio-player__speed{flex:0 0 auto;border:none;background:transparent;cursor:pointer;padding:2px 4px;color:var(--co-semantic-text-muted);font-family:var(--co-font-family-primary);font-size:var(--co-font-size-12);font-variant-numeric:tabular-nums;min-width:34px;text-align:center}.co-audio-player__speed:hover{color:var(--co-semantic-text-default)}.co-audio-player__speed:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:2px;border-radius:var(--co-border-radius-xxs)}.co-audio-player__dl{flex:0 0 auto;border:none;background:transparent;cursor:pointer;padding:2px;color:var(--co-semantic-text-muted);display:inline-flex}.co-audio-player__dl:hover{color:var(--co-semantic-text-default)}.co-audio-player__dl:focus-visible{outline:2px solid var(--co-semantic-border-focus);outline-offset:2px}.co-audio-player--error .co-audio-player__wave{opacity:.5}.co-audio-player--error .co-audio-player__play{background:var(--co-color-status-error);color:var(--co-semantic-text-inverse);opacity:1}`;
|
|
74
234
|
|
|
75
235
|
const CoAudioPlayer = class {
|
|
@@ -108,8 +268,15 @@ const CoAudioPlayer = class {
|
|
|
108
268
|
this.containerWidth = 0;
|
|
109
269
|
/** Current playback rate; the speed control cycles through `SPEEDS`. */
|
|
110
270
|
this.playbackRate = 1;
|
|
111
|
-
//
|
|
112
|
-
|
|
271
|
+
// Envolvente acotada del decode (≤ PEAK_ENVELOPE_BINS máximos por bloque), para
|
|
272
|
+
// re-derivar las barras al redimensionar sin volver a descargar. NUNCA el PCM
|
|
273
|
+
// completo: ese pesaba ~32 MB por 3 min de audio y por instancia. La envolvente es
|
|
274
|
+
// la misma referencia que guarda el caché de módulo (compartida, no copiada).
|
|
275
|
+
this.peakEnvelope = null;
|
|
276
|
+
// Barras exactas (del PCM, calculadas dentro del decode) para los anchos que esta
|
|
277
|
+
// instancia —u otra con la misma src— tenía mientras el decode estaba en vuelo.
|
|
278
|
+
// Un ancho que no esté acá sale de la envolvente.
|
|
279
|
+
this.exactPeaks = null;
|
|
113
280
|
// Length reported by the WebAudio decode; NaN when nothing has decoded (yet).
|
|
114
281
|
// It is the only trustworthy duration for a container without a duration header.
|
|
115
282
|
this.decodedDuration = NaN;
|
|
@@ -238,17 +405,22 @@ const CoAudioPlayer = class {
|
|
|
238
405
|
this.resizeObserver = undefined;
|
|
239
406
|
}
|
|
240
407
|
onSrcChange() { this.init(); }
|
|
241
|
-
// Re-derive bars for the current width: from
|
|
408
|
+
// Re-derive bars for the current width: from the decoded envelope if any, else the
|
|
242
409
|
// deterministic fallback shape. Reassign the array so Stencil re-renders.
|
|
243
410
|
rebuildBars() {
|
|
244
|
-
this.peaks = this.
|
|
245
|
-
?
|
|
411
|
+
this.peaks = this.peakEnvelope
|
|
412
|
+
? this.decodedBars(this.effectiveBars)
|
|
246
413
|
: fallbackPeaks(this.effectiveBars);
|
|
247
414
|
}
|
|
415
|
+
decodedBars(bars) {
|
|
416
|
+
var _a, _b;
|
|
417
|
+
return (_b = (_a = this.exactPeaks) === null || _a === void 0 ? void 0 : _a.get(bars)) !== null && _b !== void 0 ? _b : resampleEnvelope(this.peakEnvelope, bars);
|
|
418
|
+
}
|
|
248
419
|
init() {
|
|
249
420
|
this.teardown();
|
|
250
421
|
const token = ++this.loadToken;
|
|
251
|
-
this.
|
|
422
|
+
this.peakEnvelope = null;
|
|
423
|
+
this.exactPeaks = null;
|
|
252
424
|
this.decodedDuration = NaN;
|
|
253
425
|
this.peaks = fallbackPeaks(this.effectiveBars); // draw immediately; refine with real peaks
|
|
254
426
|
this.currentTime = 0;
|
|
@@ -287,56 +459,36 @@ const CoAudioPlayer = class {
|
|
|
287
459
|
}
|
|
288
460
|
this.isPlaying = false;
|
|
289
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Picos + duración del decode, vía el caché de módulo (`co-audio-player.peaks-cache`):
|
|
464
|
+
* dos instancias con la misma `src` comparten UNA descarga y UN decode, y lo que
|
|
465
|
+
* vuelve es la envolvente acotada, no el PCM.
|
|
466
|
+
*/
|
|
290
467
|
async extractPeaks(url, token) {
|
|
291
|
-
var _a;
|
|
292
|
-
let ctx;
|
|
293
468
|
try {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
* created before the user has interacted trips the browser's autoplay
|
|
298
|
-
* policy — "The AudioContext was not allowed to start. It must be resumed
|
|
299
|
-
* (or created) after a user gesture on the page", once per player. A page
|
|
300
|
-
* with 15 recordings logged it 15 times. The offline one has no output
|
|
301
|
-
* device, so the policy does not apply to it.
|
|
302
|
-
*
|
|
303
|
-
* Its arguments are placeholders: nothing is ever rendered through this
|
|
304
|
-
* context, it is only the host for `decodeAudioData`.
|
|
305
|
-
*/
|
|
306
|
-
const OfflineCtx = window.OfflineAudioContext || window.webkitOfflineAudioContext;
|
|
307
|
-
const Ctx = window.AudioContext || window.webkitAudioContext;
|
|
308
|
-
if ((!OfflineCtx && !Ctx) || typeof fetch === 'undefined')
|
|
309
|
-
return; // keep fallback
|
|
310
|
-
ctx = OfflineCtx ? new OfflineCtx(1, 1, 44100) : new Ctx();
|
|
311
|
-
const buf = await (await fetch(url)).arrayBuffer();
|
|
312
|
-
const audioBuf = await ctx.decodeAudioData(buf);
|
|
469
|
+
// El ancho se consulta al TERMINAR el decode (no ahora: acá todavía no se midió);
|
|
470
|
+
// 0 si esta carga ya no es la vigente, para no pagar barras que nadie pinta.
|
|
471
|
+
const res = await loadAudioPeaks(url, () => (token === this.loadToken ? this.effectiveBars : 0));
|
|
313
472
|
if (token !== this.loadToken)
|
|
314
473
|
return; // a newer src superseded this decode
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
this.
|
|
474
|
+
if (!res)
|
|
475
|
+
return; // no WebAudio/fetch here: keep the fallback
|
|
476
|
+
this.peakEnvelope = res.envelope;
|
|
477
|
+
this.exactPeaks = res.exact;
|
|
478
|
+
this.decodedDuration = res.duration;
|
|
479
|
+
this.peaks = this.decodedBars(this.effectiveBars);
|
|
318
480
|
}
|
|
319
|
-
catch (
|
|
481
|
+
catch (_a) {
|
|
320
482
|
if (token !== this.loadToken)
|
|
321
483
|
return;
|
|
322
484
|
// Undecodable (codec WebAudio can't handle, CORS, network): keep the
|
|
323
485
|
// deterministic fallback wave and let <audio> own the duration — playback,
|
|
324
486
|
// seek and the time readout all still work, or degrade to `--:--`.
|
|
325
|
-
this.
|
|
487
|
+
this.peakEnvelope = null;
|
|
488
|
+
this.exactPeaks = null;
|
|
326
489
|
this.decodedDuration = NaN;
|
|
327
490
|
this.peaks = fallbackPeaks(this.effectiveBars); // stays functional
|
|
328
491
|
}
|
|
329
|
-
finally {
|
|
330
|
-
// Always release the context. Chrome caps concurrent AudioContexts per
|
|
331
|
-
// document, so leaking one per failed decode would eventually stop every
|
|
332
|
-
// later player on the page from decoding at all. `close()` lives on
|
|
333
|
-
// AudioContext, not on every BaseAudioContext (an OfflineAudioContext
|
|
334
|
-
// needs no closing and may not implement it): optional call, not a cast.
|
|
335
|
-
try {
|
|
336
|
-
await ((_a = ctx === null || ctx === void 0 ? void 0 : ctx.close) === null || _a === void 0 ? void 0 : _a.call(ctx));
|
|
337
|
-
}
|
|
338
|
-
catch ( /* already closed */_c) { /* already closed */ }
|
|
339
|
-
}
|
|
340
492
|
if (token === this.loadToken)
|
|
341
493
|
this.syncDuration();
|
|
342
494
|
}
|
|
@@ -417,13 +569,13 @@ const CoAudioPlayer = class {
|
|
|
417
569
|
// below `aria-valuenow` the moment playback advances. ARIA 1.2 makes valuemax
|
|
418
570
|
// optional for `slider`, so leaving it off while unknown is well-formed.
|
|
419
571
|
const ariaMax = total ? Math.floor(total) : undefined;
|
|
420
|
-
const bar = (hFrac, i, played = false) => (h("rect", { key: '
|
|
421
|
-
return (h("div", { key: '
|
|
572
|
+
const bar = (hFrac, i, played = false) => (h("rect", { key: 'a3d5a225a904e22ba9905ef8198fa7e1b14e0e2f', x: i * UNIT, y: H / 2 - (hFrac * H) / 2, width: W, height: Math.max(hFrac * H, 2), rx: R, class: { 'co-audio-player__bar': true, 'co-audio-player__bar--played': played } }));
|
|
573
|
+
return (h("div", { key: 'afbbf524713a1146653e69928ee21e263202315f', class: {
|
|
422
574
|
'co-audio-player': true,
|
|
423
575
|
'co-audio-player--compact': this.compact,
|
|
424
576
|
'co-audio-player--error': this.hasError,
|
|
425
577
|
'co-audio-player--playing': this.isPlaying,
|
|
426
|
-
}, part: "player" }, h("button", { key: '
|
|
578
|
+
}, part: "player" }, h("button", { key: '5918385fbd8fb6fad4795e1a75eed5d073e0654c', type: "button", class: "co-audio-player__play", part: "play", "aria-label": this.hasError ? 'Audio no disponible' : this.isPlaying ? 'Pausar' : 'Reproducir', "aria-busy": this.isLoading ? 'true' : 'false', disabled: this.hasError || !this.src, onClick: this.toggle }, h("co-icon", { key: 'e7a566257df253914593712bd20029bb8f1bcdfd', name: this.hasError ? 'warning-circle' : this.isPlaying ? 'pause' : 'play', weight: "fill", size: this.compact ? 12 : 14 })), h("div", { key: '70488015726d0c966fa3e738d9f2893d71b99ebf', class: "co-audio-player__wave", part: "wave", role: "slider", tabindex: "0", "aria-label": "Progreso de reproducci\u00F3n", "aria-valuemin": 0, "aria-valuemax": ariaMax, "aria-valuenow": Math.floor(this.currentTime), ref: (el) => (this.waveEl = el), onClick: (e) => this.seekAt(e.clientX), onKeyDown: this.onWaveKey }, h("svg", { key: '8c70e4f74537e5f8ab6f66bc0da464a72507e81c', class: "co-audio-player__svg", viewBox: `0 0 ${vbW} ${H}`, preserveAspectRatio: "none", "aria-hidden": "true" }, this.peaks.map((hFrac, i) => bar(hFrac, i)), h("clipPath", { key: 'f0d37d76b3d6ab09dbd09582c8657b3fe7cb8c4c', id: "co-ap-clip" }, h("rect", { key: '436de0ce10f98c868760c76505d134f3db2dbb54', x: "0", y: "0", width: (pct / 100) * vbW, height: H })), h("g", { key: '939a1a8851d6e41b4d3c37d7201f87a6403830bb', "clip-path": "url(#co-ap-clip)" }, this.peaks.map((hFrac, i) => bar(hFrac, i, true))))), h("span", { key: 'bd57bdd97a780864aa633ec5174d4f733799a278', class: "co-audio-player__time", part: "time" }, formatTime(this.currentTime), " / ", formatTime(this.duration)), !this.compact && (h("button", { key: 'd1f559546da0b0f97009afca79fe922ce569e67f', type: "button", class: "co-audio-player__speed", part: "speed", "aria-label": `Velocidad de reproducción ${this.playbackRate}x`, onClick: this.cycleSpeed }, this.playbackRate, "x")), !this.compact && !this.noDownload && (h("button", { key: 'a1503b85bb2edf02566dcad9b8e83b1fea20b159', type: "button", class: "co-audio-player__dl", part: "download", "aria-label": "Descargar", onClick: () => this.download() }, h("co-icon", { key: 'dc96b8afdb8036e76a0c515d76c625460f4fc44d', name: "download-simple", size: 14 })))));
|
|
427
579
|
}
|
|
428
580
|
static get watchers() { return {
|
|
429
581
|
"src": [{
|
|
@@ -179,7 +179,7 @@ const CoKbd = class {
|
|
|
179
179
|
};
|
|
180
180
|
CoKbd.style = coKbdCss();
|
|
181
181
|
|
|
182
|
-
const coTooltipCss = () => `:host{display:inline-block;position:relative}.co-tooltip{display:inline-block}.co-tooltip__trigger{display:inline-block}:host([block]){display:block;width:100%;min-width:0}:host([block]) .co-tooltip,:host([block]) .co-tooltip__trigger{display:block;width:100%;min-width:0}.co-tooltip__panel{position:fixed;inset:auto;top:0;left:0;margin:0;border:0;max-height:none;overflow:visible;z-index:10000;visibility:hidden;opacity:0;pointer-events:none
|
|
182
|
+
const coTooltipCss = () => `:host{display:inline-block;position:relative}.co-tooltip{display:inline-block}.co-tooltip__trigger{display:inline-block}:host([block]){display:block;width:100%;min-width:0}:host([block]) .co-tooltip,:host([block]) .co-tooltip__trigger{display:block;width:100%;min-width:0}.co-tooltip__panel{position:fixed;inset:auto;top:0;left:0;margin:0;border:0;max-height:none;overflow:visible;z-index:10000;visibility:hidden;opacity:0;pointer-events:none;--co-tooltip-surface:var(--co-color-base-negro, #1f1c1b);background:var(--co-tooltip-surface);color:var(--co-color-base-blanco, #fff);font-family:var(--co-font-family-primary, 'Geist', sans-serif);font-size:var(--co-font-size-12, 12px);line-height:1.4;font-weight:400;text-transform:none;letter-spacing:var(--co-font-letter-spacing-body, normal);padding:var(--co-spacing-xs, 4px) var(--co-spacing-sm, 8px);border-radius:var(--co-border-radius-xxs, 4px);box-shadow:0 4px 12px rgba(31, 28, 27, 0.12);white-space:normal;word-wrap:break-word;transition:opacity var(--co-motion-duration-fast) var(--co-motion-ease-out), transform var(--co-motion-duration-fast) var(--co-motion-ease-out)}.co-tooltip__panel--positioned{visibility:visible;opacity:1}.co-tooltip__panel--closing{visibility:visible;opacity:0;transition:opacity var(--co-motion-duration-instant) var(--co-motion-ease-in), transform var(--co-motion-duration-instant) var(--co-motion-ease-in)}.co-tooltip__panel--top{transform:translateY(4px)}.co-tooltip__panel--top.co-tooltip__panel--positioned{transform:translateY(0)}.co-tooltip__panel--bottom{transform:translateY(-4px)}.co-tooltip__panel--bottom.co-tooltip__panel--positioned{transform:translateY(0)}.co-tooltip__panel--left{transform:translateX(4px)}.co-tooltip__panel--left.co-tooltip__panel--positioned{transform:translateX(0)}.co-tooltip__panel--right{transform:translateX(-4px)}.co-tooltip__panel--right.co-tooltip__panel--positioned{transform:translateX(0)}.co-tooltip__arrow{position:absolute;width:0;height:0;border:5px solid transparent}.co-tooltip__arrow--top{bottom:-10px;left:50%;transform:translateX(-50%);border-top-color:var(--co-tooltip-surface);border-bottom:none}.co-tooltip__arrow--bottom{top:-5px;left:50%;transform:translateX(-50%);border-bottom-color:var(--co-tooltip-surface);border-top:none}.co-tooltip__arrow--left{right:-10px;top:50%;transform:translateY(-50%);border-left-color:var(--co-tooltip-surface);border-right:none}.co-tooltip__arrow--right{left:-5px;top:50%;transform:translateY(-50%);border-right-color:var(--co-tooltip-surface);border-left:none}.co-tooltip__panel--tone-error{--co-tooltip-surface:var(--co-semantic-status-error, #aa2e1b)}.co-tooltip__content{display:block}.co-tooltip__panel--label{padding:4px 8px;border-radius:var(--co-border-radius-xs, 8px);font-weight:var(--co-font-weight-medium, 500);line-height:1;box-shadow:none}.co-tooltip__panel--label .co-tooltip__arrow{display:none}.co-tooltip__panel--with-shortcut{display:flex;align-items:center;gap:6px}.co-tooltip__panel--with-shortcut:not(.co-tooltip__panel--label){padding-right:10px;}.co-tooltip__panel--label.co-tooltip__panel--with-shortcut{padding-right:4px}`;
|
|
183
183
|
|
|
184
184
|
const CoTooltip = class {
|
|
185
185
|
constructor(hostRef) {
|
|
@@ -214,6 +214,12 @@ const CoTooltip = class {
|
|
|
214
214
|
* arrow hide is CSS-only — `arrow`'s own default/behavior is unchanged.
|
|
215
215
|
*/
|
|
216
216
|
this.variant = 'default';
|
|
217
|
+
/**
|
|
218
|
+
* Color tone of the panel (and arrow). `error` = solid `--co-semantic-status-error`
|
|
219
|
+
* surface with white text, for a record/state warning (e.g. "Inactive").
|
|
220
|
+
* Independent of `variant`.
|
|
221
|
+
*/
|
|
222
|
+
this.tone = 'default';
|
|
217
223
|
// ── State ──────────────────────────────────────────────────
|
|
218
224
|
this.visible = false;
|
|
219
225
|
this.actualPlacement = 'top';
|
|
@@ -445,11 +451,12 @@ const CoTooltip = class {
|
|
|
445
451
|
// ── Render ─────────────────────────────────────────────────
|
|
446
452
|
render() {
|
|
447
453
|
const side = this.actualPlacement.split('-')[0];
|
|
448
|
-
return (h("div", { key: '
|
|
454
|
+
return (h("div", { key: '53c8dbd21619fd8caa4105b86b4b1765c2361252', class: "co-tooltip" }, h("div", { key: 'f9e5e3b352b37c69a67538c6db3d10f9fc945976', class: "co-tooltip__trigger", ref: (el) => (this.triggerEl = el), onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, onPointerDown: this.handlePointerDown, onFocusin: this.handleFocusIn, onFocusout: this.handleFocusOut, onKeyDown: this.handleKeyDown, "aria-describedby": this.visible ? 'tooltip-panel' : undefined }, h("slot", { key: '067ac53e5b491c7990acfa7c236ae2090a6657fc' })), (this.visible || this.closing) && this.content && (h("div", { key: '42436edfbd802bfd8c23dea5b8cfdd290fa150c7', id: "tooltip-panel", class: {
|
|
449
455
|
'co-tooltip__panel': true,
|
|
450
456
|
[`co-tooltip__panel--${side}`]: true,
|
|
451
457
|
'co-tooltip__panel--closing': this.closing,
|
|
452
458
|
'co-tooltip__panel--label': this.variant === 'label',
|
|
459
|
+
'co-tooltip__panel--tone-error': this.tone === 'error',
|
|
453
460
|
'co-tooltip__panel--with-shortcut': !!this.shortcut,
|
|
454
461
|
}, role: "tooltip", popover: "manual", part: "panel", onTransitionEnd: this.handlePanelTransitionEnd, ref: (el) => {
|
|
455
462
|
if (el && el !== this.panelEl) {
|
|
@@ -457,7 +464,7 @@ const CoTooltip = class {
|
|
|
457
464
|
this.promotePanel();
|
|
458
465
|
this.scheduleUpdate();
|
|
459
466
|
}
|
|
460
|
-
}, style: { maxWidth: this.maxWidth } }, this.arrow && h("div", { key: '
|
|
467
|
+
}, style: { maxWidth: this.maxWidth } }, this.arrow && h("div", { key: '7520aaa3245fea55a7fcac31f33c56247b1fb5d8', class: `co-tooltip__arrow co-tooltip__arrow--${side}` }), h("span", { key: 'd3797d1c3a31c64cb30d4d8f448204e3a9f8c017', class: "co-tooltip__content" }, this.content), this.shortcut && h("co-kbd", { key: '01354b1fd110de3000d48ad9eaef0e3329a64e48', keys: this.shortcut, surface: "dark", size: "xs" })))));
|
|
461
468
|
}
|
|
462
469
|
get el() { return getElement(this); }
|
|
463
470
|
static get watchers() { return {
|