@prolibu-suite/cobalt-core 0.11.4 → 0.11.5

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/custom-elements.json +31 -31
  2. package/dist/cjs/co-audio-player.cjs.entry.js +198 -46
  3. package/dist/cjs/index-bHoidDwv.js +4 -4
  4. package/dist/cobalt/cobalt.esm.js +1 -1
  5. package/dist/cobalt/p-4a7fd0b4.entry.js +1 -0
  6. package/dist/collection/components/co-audio-player/co-audio-player.js +40 -47
  7. package/dist/collection/components/co-audio-player/co-audio-player.peaks-cache.js +104 -0
  8. package/dist/collection/components/co-audio-player/co-audio-player.utils.js +61 -0
  9. package/dist/collection/components/co-autocomplete/co-autocomplete.js +1 -1
  10. package/dist/collection/components/co-avatar-group/co-avatar-group.js +1 -1
  11. package/dist/collection/components/co-checkbox-group/co-checkbox-group.js +1 -1
  12. package/dist/collection/components/co-color-area/co-color-area.js +2 -2
  13. package/dist/collection/components/co-color-slider/co-color-slider.js +1 -1
  14. package/dist/collection/components/co-combobox/co-combobox.js +1 -1
  15. package/dist/collection/components/co-command-search/co-command-search.js +1 -1
  16. package/dist/collection/components/co-data-table/co-data-table.js +1 -1
  17. package/dist/collection/components/co-date-range-picker/co-date-range-picker.js +2 -2
  18. package/dist/collection/components/co-document-editor/co-document-editor.js +5 -5
  19. package/dist/collection/components/co-popover/co-popover.js +1 -1
  20. package/dist/collection/components/co-radio-group/co-radio-group.js +1 -1
  21. package/dist/collection/components/co-ref-field/co-ref-field.js +4 -4
  22. package/dist/collection/components/co-segmented-bar/co-segmented-bar.js +1 -1
  23. package/dist/collection/components/co-select/co-select.js +1 -1
  24. package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +1 -1
  25. package/dist/collection/components/co-toast/co-toast.js +2 -2
  26. package/dist/collection/components/co-transfer-list/co-transfer-list.js +1 -1
  27. package/dist/collection/components/co-tree-item/co-tree-item.js +2 -2
  28. package/dist/components/co-audio-player.js +1 -1
  29. package/dist/esm/co-audio-player.entry.js +198 -46
  30. package/dist/esm/index-DJkaRtbb.js +4 -4
  31. package/dist/types/components/co-audio-player/co-audio-player.d.ts +8 -1
  32. package/dist/types/components/co-audio-player/co-audio-player.peaks-cache.d.ts +58 -0
  33. package/dist/types/components/co-audio-player/co-audio-player.utils.d.ts +33 -0
  34. package/package.json +3 -3
  35. package/dist/cobalt/p-358a6c2a.entry.js +0 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-09-27T16:55:03",
2
+ "timestamp": "2026-09-28T18:52:48",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.43.4",
@@ -1434,7 +1434,7 @@
1434
1434
  "references": {
1435
1435
  "AutocompleteOption": {
1436
1436
  "location": "local",
1437
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-autocomplete/co-autocomplete.tsx",
1437
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-autocomplete/co-autocomplete.tsx",
1438
1438
  "id": "src/components/co-autocomplete/co-autocomplete.tsx::AutocompleteOption"
1439
1439
  }
1440
1440
  }
@@ -2270,7 +2270,7 @@
2270
2270
  "references": {
2271
2271
  "CoAvatarGroupItem": {
2272
2272
  "location": "local",
2273
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-avatar-group/co-avatar-group.tsx",
2273
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-avatar-group/co-avatar-group.tsx",
2274
2274
  "id": "src/components/co-avatar-group/co-avatar-group.tsx::CoAvatarGroupItem"
2275
2275
  }
2276
2276
  }
@@ -7047,7 +7047,7 @@
7047
7047
  "references": {
7048
7048
  "CheckboxOption": {
7049
7049
  "location": "local",
7050
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-checkbox-group/co-checkbox-group.tsx",
7050
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-checkbox-group/co-checkbox-group.tsx",
7051
7051
  "id": "src/components/co-checkbox-group/co-checkbox-group.tsx::CheckboxOption"
7052
7052
  }
7053
7053
  }
@@ -8162,7 +8162,7 @@
8162
8162
  "references": {
8163
8163
  "ColorAreaDetail": {
8164
8164
  "location": "local",
8165
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-color-area/co-color-area.tsx",
8165
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-color-area/co-color-area.tsx",
8166
8166
  "id": "src/components/co-color-area/co-color-area.tsx::ColorAreaDetail"
8167
8167
  }
8168
8168
  }
@@ -8182,7 +8182,7 @@
8182
8182
  "references": {
8183
8183
  "ColorAreaDetail": {
8184
8184
  "location": "local",
8185
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-color-area/co-color-area.tsx",
8185
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-color-area/co-color-area.tsx",
8186
8186
  "id": "src/components/co-color-area/co-color-area.tsx::ColorAreaDetail"
8187
8187
  }
8188
8188
  }
@@ -8688,7 +8688,7 @@
8688
8688
  "references": {
8689
8689
  "ColorSliderChannel": {
8690
8690
  "location": "local",
8691
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-color-slider/co-color-slider.tsx",
8691
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-color-slider/co-color-slider.tsx",
8692
8692
  "id": "src/components/co-color-slider/co-color-slider.tsx::ColorSliderChannel"
8693
8693
  }
8694
8694
  }
@@ -9908,7 +9908,7 @@
9908
9908
  "references": {
9909
9909
  "ComboboxOption": {
9910
9910
  "location": "local",
9911
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-combobox/co-combobox.tsx",
9911
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-combobox/co-combobox.tsx",
9912
9912
  "id": "src/components/co-combobox/co-combobox.tsx::ComboboxOption"
9913
9913
  }
9914
9914
  }
@@ -10617,7 +10617,7 @@
10617
10617
  "references": {
10618
10618
  "CommandItem": {
10619
10619
  "location": "local",
10620
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-command-search/co-command-search.tsx",
10620
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-command-search/co-command-search.tsx",
10621
10621
  "id": "src/components/co-command-search/co-command-search.tsx::CommandItem"
10622
10622
  }
10623
10623
  }
@@ -11009,7 +11009,7 @@
11009
11009
  "references": {
11010
11010
  "CoDataTableColumn": {
11011
11011
  "location": "local",
11012
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-data-table/co-data-table.tsx",
11012
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-data-table/co-data-table.tsx",
11013
11013
  "id": "src/components/co-data-table/co-data-table.tsx::CoDataTableColumn"
11014
11014
  }
11015
11015
  }
@@ -15179,7 +15179,7 @@
15179
15179
  "references": {
15180
15180
  "DateRangeValue": {
15181
15181
  "location": "local",
15182
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-date-range-picker/co-date-range-picker.tsx",
15182
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-date-range-picker/co-date-range-picker.tsx",
15183
15183
  "id": "src/components/co-date-range-picker/co-date-range-picker.tsx::DateRangeValue"
15184
15184
  }
15185
15185
  }
@@ -15255,7 +15255,7 @@
15255
15255
  "references": {
15256
15256
  "DateRangeValue": {
15257
15257
  "location": "local",
15258
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-date-range-picker/co-date-range-picker.tsx",
15258
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-date-range-picker/co-date-range-picker.tsx",
15259
15259
  "id": "src/components/co-date-range-picker/co-date-range-picker.tsx::DateRangeValue"
15260
15260
  }
15261
15261
  }
@@ -16469,7 +16469,7 @@
16469
16469
  "references": {
16470
16470
  "DocumentFeatures": {
16471
16471
  "location": "local",
16472
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-document-editor/co-document-editor.tsx",
16472
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-document-editor/co-document-editor.tsx",
16473
16473
  "id": "src/components/co-document-editor/co-document-editor.tsx::DocumentFeatures"
16474
16474
  }
16475
16475
  }
@@ -16652,7 +16652,7 @@
16652
16652
  "references": {
16653
16653
  "DocumentMargins": {
16654
16654
  "location": "local",
16655
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-document-editor/co-document-editor.tsx",
16655
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-document-editor/co-document-editor.tsx",
16656
16656
  "id": "src/components/co-document-editor/co-document-editor.tsx::DocumentMargins"
16657
16657
  }
16658
16658
  }
@@ -16687,7 +16687,7 @@
16687
16687
  "references": {
16688
16688
  "DocumentPageSize": {
16689
16689
  "location": "local",
16690
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-document-editor/co-document-editor.tsx",
16690
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-document-editor/co-document-editor.tsx",
16691
16691
  "id": "src/components/co-document-editor/co-document-editor.tsx::DocumentPageSize"
16692
16692
  }
16693
16693
  }
@@ -17164,7 +17164,7 @@
17164
17164
  "references": {
17165
17165
  "DocumentActionDetail": {
17166
17166
  "location": "local",
17167
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-document-editor/co-document-editor.tsx",
17167
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-document-editor/co-document-editor.tsx",
17168
17168
  "id": "src/components/co-document-editor/co-document-editor.tsx::DocumentActionDetail"
17169
17169
  }
17170
17170
  }
@@ -17219,7 +17219,7 @@
17219
17219
  "references": {
17220
17220
  "DocumentHeaderDetail": {
17221
17221
  "location": "local",
17222
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-document-editor/co-document-editor.tsx",
17222
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-document-editor/co-document-editor.tsx",
17223
17223
  "id": "src/components/co-document-editor/co-document-editor.tsx::DocumentHeaderDetail"
17224
17224
  }
17225
17225
  }
@@ -30467,7 +30467,7 @@
30467
30467
  "references": {
30468
30468
  "PopoverPlacement": {
30469
30469
  "location": "local",
30470
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-popover/co-popover.tsx",
30470
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-popover/co-popover.tsx",
30471
30471
  "id": "src/components/co-popover/co-popover.tsx::PopoverPlacement"
30472
30472
  }
30473
30473
  }
@@ -31653,7 +31653,7 @@
31653
31653
  "references": {
31654
31654
  "RadioOption": {
31655
31655
  "location": "local",
31656
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-radio-group/co-radio-group.tsx",
31656
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-radio-group/co-radio-group.tsx",
31657
31657
  "id": "src/components/co-radio-group/co-radio-group.tsx::RadioOption"
31658
31658
  }
31659
31659
  }
@@ -33454,7 +33454,7 @@
33454
33454
  },
33455
33455
  "RefFieldOption": {
33456
33456
  "location": "local",
33457
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33457
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33458
33458
  "id": "src/components/co-ref-field/co-ref-field.tsx::RefFieldOption"
33459
33459
  }
33460
33460
  },
@@ -33483,7 +33483,7 @@
33483
33483
  "references": {
33484
33484
  "RefFieldOption": {
33485
33485
  "location": "local",
33486
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33486
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33487
33487
  "id": "src/components/co-ref-field/co-ref-field.tsx::RefFieldOption"
33488
33488
  }
33489
33489
  }
@@ -33573,7 +33573,7 @@
33573
33573
  "references": {
33574
33574
  "RefFieldOption": {
33575
33575
  "location": "local",
33576
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33576
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33577
33577
  "id": "src/components/co-ref-field/co-ref-field.tsx::RefFieldOption"
33578
33578
  }
33579
33579
  }
@@ -33621,7 +33621,7 @@
33621
33621
  "references": {
33622
33622
  "RefFieldOption": {
33623
33623
  "location": "local",
33624
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33624
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-ref-field/co-ref-field.tsx",
33625
33625
  "id": "src/components/co-ref-field/co-ref-field.tsx::RefFieldOption"
33626
33626
  }
33627
33627
  }
@@ -35954,7 +35954,7 @@
35954
35954
  "references": {
35955
35955
  "CoSegment": {
35956
35956
  "location": "local",
35957
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-segmented-bar/co-segmented-bar.tsx",
35957
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-segmented-bar/co-segmented-bar.tsx",
35958
35958
  "id": "src/components/co-segmented-bar/co-segmented-bar.tsx::CoSegment"
35959
35959
  }
35960
35960
  }
@@ -36494,7 +36494,7 @@
36494
36494
  "references": {
36495
36495
  "SelectOption": {
36496
36496
  "location": "local",
36497
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-select/co-select.tsx",
36497
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-select/co-select.tsx",
36498
36498
  "id": "src/components/co-select/co-select.tsx::SelectOption"
36499
36499
  }
36500
36500
  }
@@ -37568,7 +37568,7 @@
37568
37568
  "references": {
37569
37569
  "NavigateDetail": {
37570
37570
  "location": "local",
37571
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-sidebar-nav/co-sidebar-nav.tsx",
37571
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-sidebar-nav/co-sidebar-nav.tsx",
37572
37572
  "id": "src/components/co-sidebar-nav/co-sidebar-nav.tsx::NavigateDetail"
37573
37573
  }
37574
37574
  }
@@ -41104,7 +41104,7 @@
41104
41104
  "references": {
41105
41105
  "CoToastActionsLayout": {
41106
41106
  "location": "local",
41107
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-toast/co-toast.tsx",
41107
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-toast/co-toast.tsx",
41108
41108
  "id": "src/components/co-toast/co-toast.tsx::CoToastActionsLayout"
41109
41109
  }
41110
41110
  }
@@ -41280,7 +41280,7 @@
41280
41280
  "references": {
41281
41281
  "CoToastVariant": {
41282
41282
  "location": "local",
41283
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-toast/co-toast.tsx",
41283
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-toast/co-toast.tsx",
41284
41284
  "id": "src/components/co-toast/co-toast.tsx::CoToastVariant"
41285
41285
  }
41286
41286
  }
@@ -42660,7 +42660,7 @@
42660
42660
  "references": {
42661
42661
  "TransferOption": {
42662
42662
  "location": "local",
42663
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-transfer-list/co-transfer-list.tsx",
42663
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-transfer-list/co-transfer-list.tsx",
42664
42664
  "id": "src/components/co-transfer-list/co-transfer-list.tsx::TransferOption"
42665
42665
  }
42666
42666
  }
@@ -43090,7 +43090,7 @@
43090
43090
  "references": {
43091
43091
  "TreeItemSelection": {
43092
43092
  "location": "local",
43093
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-tree-item/co-tree-item.tsx",
43093
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-tree-item/co-tree-item.tsx",
43094
43094
  "id": "src/components/co-tree-item/co-tree-item.tsx::TreeItemSelection"
43095
43095
  }
43096
43096
  }
@@ -43134,7 +43134,7 @@
43134
43134
  "references": {
43135
43135
  "TreeItemSize": {
43136
43136
  "location": "local",
43137
- "path": "/Users/agox/worktrees/cobalt-number-comma/packages/core/src/components/co-tree-item/co-tree-item.tsx",
43137
+ "path": "/Users/agox/worktrees/cobalt-audio-peaks/packages/core/src/components/co-tree-item/co-tree-item.tsx",
43138
43138
  "id": "src/components/co-tree-item/co-tree-item.tsx::TreeItemSize"
43139
43139
  }
43140
43140
  }
@@ -53,6 +53,67 @@ function resamplePeaks(channelData, bars) {
53
53
  }
54
54
  return peaks;
55
55
  }
56
+ /**
57
+ * Resolución de la envolvente que se retiene por grabación, en bloques.
58
+ *
59
+ * Las barras EXACTAS del ancho vigente se calculan del PCM antes de soltarlo (ver
60
+ * `co-audio-player.peaks-cache`); la envolvente solo sirve los anchos que aparecen
61
+ * DESPUÉS (resize, o una segunda instancia que llega con el caché ya caliente). El
62
+ * reproductor deriva las barras del ancho (`floor(ancho / barDensity)`, ~853 a
63
+ * 2560 px), así que la envolvente tiene que ir muy por encima de eso: 65 536 bloques
64
+ * = ≥ 76 bloques por barra hasta 2560 px, y pesan 256 KB (Float32) frente a ~32 MB
65
+ * del PCM de 3 min a 44,1 kHz. Medido contra el exacto con PCM sintético de 3 min:
66
+ * desvío máx. ≈ 1 px de 40 en las barras que difieren (≈ 0,8 px con ruido blanco).
67
+ */
68
+ const PEAK_ENVELOPE_BINS = 65536;
69
+ /** PCM → envolvente de a lo sumo `maxBins` bloques. Con `length ≤ maxBins` el bloque
70
+ * es de 1 muestra y la envolvente ES |PCM| (sin pérdida). */
71
+ function buildPeakEnvelope(channelData, maxBins = PEAK_ENVELOPE_BINS) {
72
+ const length = channelData.length;
73
+ const blockSize = Math.max(1, Math.ceil(length / Math.max(1, maxBins)));
74
+ const bins = Math.ceil(length / blockSize);
75
+ const peaks = new Float32Array(bins);
76
+ for (let b = 0; b < bins; b++) {
77
+ let max = 0;
78
+ const end = Math.min(length, (b + 1) * blockSize);
79
+ for (let j = b * blockSize; j < end; j++) {
80
+ const abs = Math.abs(channelData[j]);
81
+ if (abs > max)
82
+ max = abs;
83
+ }
84
+ peaks[b] = max;
85
+ }
86
+ return { peaks, blockSize, length };
87
+ }
88
+ /**
89
+ * Las mismas barras que `resamplePeaks(pcm, bars)`, calculadas desde la envolvente:
90
+ * mismo paso (`floor(length / bars)` muestras), mismo recorte de la cola y misma
91
+ * fórmula de altura. Cada barra toma el máximo de los bloques que TOCA su rango de
92
+ * muestras, así que el resultado es IDÉNTICO cuando `blockSize` es 1 o divide el
93
+ * paso, y en el caso general solo puede sumar el pico de un bloque parcial en cada
94
+ * borde (nunca queda por debajo del exacto).
95
+ */
96
+ function resampleEnvelope(env, bars) {
97
+ const { peaks: blocks, blockSize, length: len } = env;
98
+ if (!len || bars <= 0)
99
+ return [];
100
+ const step = Math.max(1, Math.floor(len / bars));
101
+ const peaks = [];
102
+ for (let i = 0; i < bars; i++) {
103
+ const start = i * step;
104
+ const end = Math.min(start + step, len); // exclusivo, como el bucle de resamplePeaks
105
+ let max = 0;
106
+ if (start < end) {
107
+ const last = Math.floor((end - 1) / blockSize);
108
+ for (let b = Math.floor(start / blockSize); b <= last; b++) {
109
+ if (blocks[b] > max)
110
+ max = blocks[b];
111
+ }
112
+ }
113
+ peaks.push(0.12 + max * 0.88);
114
+ }
115
+ return peaks;
116
+ }
56
117
  /** Deterministic pseudo-waveform (stable render + testable — no Math.random). */
57
118
  function fallbackPeaks(bars) {
58
119
  if (bars <= 0)
@@ -72,6 +133,105 @@ function scrubFraction(clientX, rect) {
72
133
  return Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
73
134
  }
74
135
 
136
+ /** Tope de entradas. Cada una pesa ≤ 256 KB (`PEAK_ENVELOPE_BINS` Float32) más unas
137
+ * pocas listas de barras exactas, así que el caché lleno queda en ~4 MB. Una
138
+ * instancia montada conserva SU envolvente aunque la entrada se desaloje (es la
139
+ * misma referencia, no una copia). */
140
+ const PEAKS_CACHE_MAX = 16;
141
+ // Map conserva el orden de inserción: la primera clave es la menos usada (LRU).
142
+ const cache = new Map();
143
+ const inflight = new Map();
144
+ function remember(url, value) {
145
+ cache.delete(url);
146
+ cache.set(url, value);
147
+ while (cache.size > PEAKS_CACHE_MAX)
148
+ cache.delete(cache.keys().next().value);
149
+ }
150
+ /**
151
+ * Picos + duración de `url`: del caché si están, uniéndose a la carga en vuelo si
152
+ * otra instancia ya la pidió, o cargándolos. Solo se cachea un resultado real: un
153
+ * error (CORS, red, códec) o un `null` se devuelven tal cual y el próximo pedido
154
+ * reintenta.
155
+ */
156
+ function loadAudioPeaks(url, wantBars, loader = decodeAudioPeaks) {
157
+ const hit = cache.get(url);
158
+ if (hit) {
159
+ remember(url, hit); // refresca su posición LRU
160
+ return Promise.resolve(hit);
161
+ }
162
+ const pending = inflight.get(url);
163
+ if (pending) {
164
+ if (wantBars)
165
+ pending.wanted.add(wantBars);
166
+ return pending.promise;
167
+ }
168
+ const wanted = new Set(wantBars ? [wantBars] : []);
169
+ const p = loader(url, wanted).then((res) => {
170
+ inflight.delete(url);
171
+ if (res)
172
+ remember(url, res);
173
+ return res;
174
+ }, (err) => {
175
+ inflight.delete(url);
176
+ throw err;
177
+ });
178
+ inflight.set(url, { promise: p, wanted });
179
+ return p;
180
+ }
181
+ /**
182
+ * Descarga + decode con Web Audio, reducido a la envolvente. El `AudioBuffer`
183
+ * (y con él el PCM completo) no sale de esta función: queda sin referencias
184
+ * apenas se calcula la envolvente.
185
+ */
186
+ async function decodeAudioPeaks(url, wanted = new Set()) {
187
+ var _a;
188
+ let ctx;
189
+ try {
190
+ /**
191
+ * `OfflineAudioContext` FIRST, and a live `AudioContext` only as the
192
+ * fallback: decoding a waveform needs no audio OUTPUT, and a live context
193
+ * created before the user has interacted trips the browser's autoplay
194
+ * policy — "The AudioContext was not allowed to start. It must be resumed
195
+ * (or created) after a user gesture on the page", once per player. A page
196
+ * with 15 recordings logged it 15 times. The offline one has no output
197
+ * device, so the policy does not apply to it.
198
+ *
199
+ * Its arguments are placeholders: nothing is ever rendered through this
200
+ * context, it is only the host for `decodeAudioData`.
201
+ */
202
+ const OfflineCtx = window.OfflineAudioContext || window.webkitOfflineAudioContext;
203
+ const Ctx = window.AudioContext || window.webkitAudioContext;
204
+ if ((!OfflineCtx && !Ctx) || typeof fetch === 'undefined')
205
+ return null;
206
+ ctx = OfflineCtx ? new OfflineCtx(1, 1, 44100) : new Ctx();
207
+ const buf = await (await fetch(url)).arrayBuffer();
208
+ const audioBuf = await ctx.decodeAudioData(buf);
209
+ const pcm = audioBuf.getChannelData(0);
210
+ return { envelope: buildPeakEnvelope(pcm), exact: exactBarsFor(pcm, wanted), duration: audioBuf.duration };
211
+ }
212
+ finally {
213
+ // Always release the context. Chrome caps concurrent AudioContexts per
214
+ // document, so leaking one per failed decode would eventually stop every
215
+ // later player on the page from decoding at all. `close()` lives on
216
+ // AudioContext, not on every BaseAudioContext (an OfflineAudioContext
217
+ // needs no closing and may not implement it): optional call, not a cast.
218
+ try {
219
+ await ((_a = ctx === null || ctx === void 0 ? void 0 : ctx.close) === null || _a === void 0 ? void 0 : _a.call(ctx));
220
+ }
221
+ catch ( /* already closed */_b) { /* already closed */ }
222
+ }
223
+ }
224
+ /** Barras exactas para cada ancho que las instancias en espera quieren ahora mismo. */
225
+ function exactBarsFor(pcm, wanted) {
226
+ const exact = new Map();
227
+ for (const want of wanted) {
228
+ const bars = want();
229
+ if (bars > 0 && !exact.has(bars))
230
+ exact.set(bars, resamplePeaks(pcm, bars));
231
+ }
232
+ return exact;
233
+ }
234
+
75
235
  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}`;
76
236
 
77
237
  const CoAudioPlayer = class {
@@ -110,8 +270,15 @@ const CoAudioPlayer = class {
110
270
  this.containerWidth = 0;
111
271
  /** Current playback rate; the speed control cycles through `SPEEDS`. */
112
272
  this.playbackRate = 1;
113
- // Decoded mono PCM, kept so bars can be re-resampled on resize without re-fetching.
114
- this.rawChannelData = null;
273
+ // Envolvente acotada del decode (≤ PEAK_ENVELOPE_BINS máximos por bloque), para
274
+ // re-derivar las barras al redimensionar sin volver a descargar. NUNCA el PCM
275
+ // completo: ese pesaba ~32 MB por 3 min de audio y por instancia. La envolvente es
276
+ // la misma referencia que guarda el caché de módulo (compartida, no copiada).
277
+ this.peakEnvelope = null;
278
+ // Barras exactas (del PCM, calculadas dentro del decode) para los anchos que esta
279
+ // instancia —u otra con la misma src— tenía mientras el decode estaba en vuelo.
280
+ // Un ancho que no esté acá sale de la envolvente.
281
+ this.exactPeaks = null;
115
282
  // Length reported by the WebAudio decode; NaN when nothing has decoded (yet).
116
283
  // It is the only trustworthy duration for a container without a duration header.
117
284
  this.decodedDuration = NaN;
@@ -240,17 +407,22 @@ const CoAudioPlayer = class {
240
407
  this.resizeObserver = undefined;
241
408
  }
242
409
  onSrcChange() { this.init(); }
243
- // Re-derive bars for the current width: from cached PCM if decoded, else the
410
+ // Re-derive bars for the current width: from the decoded envelope if any, else the
244
411
  // deterministic fallback shape. Reassign the array so Stencil re-renders.
245
412
  rebuildBars() {
246
- this.peaks = this.rawChannelData
247
- ? resamplePeaks(this.rawChannelData, this.effectiveBars)
413
+ this.peaks = this.peakEnvelope
414
+ ? this.decodedBars(this.effectiveBars)
248
415
  : fallbackPeaks(this.effectiveBars);
249
416
  }
417
+ decodedBars(bars) {
418
+ var _a, _b;
419
+ return (_b = (_a = this.exactPeaks) === null || _a === void 0 ? void 0 : _a.get(bars)) !== null && _b !== void 0 ? _b : resampleEnvelope(this.peakEnvelope, bars);
420
+ }
250
421
  init() {
251
422
  this.teardown();
252
423
  const token = ++this.loadToken;
253
- this.rawChannelData = null;
424
+ this.peakEnvelope = null;
425
+ this.exactPeaks = null;
254
426
  this.decodedDuration = NaN;
255
427
  this.peaks = fallbackPeaks(this.effectiveBars); // draw immediately; refine with real peaks
256
428
  this.currentTime = 0;
@@ -289,56 +461,36 @@ const CoAudioPlayer = class {
289
461
  }
290
462
  this.isPlaying = false;
291
463
  }
464
+ /**
465
+ * Picos + duración del decode, vía el caché de módulo (`co-audio-player.peaks-cache`):
466
+ * dos instancias con la misma `src` comparten UNA descarga y UN decode, y lo que
467
+ * vuelve es la envolvente acotada, no el PCM.
468
+ */
292
469
  async extractPeaks(url, token) {
293
- var _a;
294
- let ctx;
295
470
  try {
296
- /**
297
- * `OfflineAudioContext` FIRST, and a live `AudioContext` only as the
298
- * fallback: decoding a waveform needs no audio OUTPUT, and a live context
299
- * created before the user has interacted trips the browser's autoplay
300
- * policy — "The AudioContext was not allowed to start. It must be resumed
301
- * (or created) after a user gesture on the page", once per player. A page
302
- * with 15 recordings logged it 15 times. The offline one has no output
303
- * device, so the policy does not apply to it.
304
- *
305
- * Its arguments are placeholders: nothing is ever rendered through this
306
- * context, it is only the host for `decodeAudioData`.
307
- */
308
- const OfflineCtx = window.OfflineAudioContext || window.webkitOfflineAudioContext;
309
- const Ctx = window.AudioContext || window.webkitAudioContext;
310
- if ((!OfflineCtx && !Ctx) || typeof fetch === 'undefined')
311
- return; // keep fallback
312
- ctx = OfflineCtx ? new OfflineCtx(1, 1, 44100) : new Ctx();
313
- const buf = await (await fetch(url)).arrayBuffer();
314
- const audioBuf = await ctx.decodeAudioData(buf);
471
+ // El ancho se consulta al TERMINAR el decode (no ahora: acá todavía no se midió);
472
+ // 0 si esta carga ya no es la vigente, para no pagar barras que nadie pinta.
473
+ const res = await loadAudioPeaks(url, () => (token === this.loadToken ? this.effectiveBars : 0));
315
474
  if (token !== this.loadToken)
316
475
  return; // a newer src superseded this decode
317
- this.rawChannelData = audioBuf.getChannelData(0);
318
- this.decodedDuration = audioBuf.duration;
319
- this.peaks = resamplePeaks(this.rawChannelData, this.effectiveBars);
476
+ if (!res)
477
+ return; // no WebAudio/fetch here: keep the fallback
478
+ this.peakEnvelope = res.envelope;
479
+ this.exactPeaks = res.exact;
480
+ this.decodedDuration = res.duration;
481
+ this.peaks = this.decodedBars(this.effectiveBars);
320
482
  }
321
- catch (_b) {
483
+ catch (_a) {
322
484
  if (token !== this.loadToken)
323
485
  return;
324
486
  // Undecodable (codec WebAudio can't handle, CORS, network): keep the
325
487
  // deterministic fallback wave and let <audio> own the duration — playback,
326
488
  // seek and the time readout all still work, or degrade to `--:--`.
327
- this.rawChannelData = null;
489
+ this.peakEnvelope = null;
490
+ this.exactPeaks = null;
328
491
  this.decodedDuration = NaN;
329
492
  this.peaks = fallbackPeaks(this.effectiveBars); // stays functional
330
493
  }
331
- finally {
332
- // Always release the context. Chrome caps concurrent AudioContexts per
333
- // document, so leaking one per failed decode would eventually stop every
334
- // later player on the page from decoding at all. `close()` lives on
335
- // AudioContext, not on every BaseAudioContext (an OfflineAudioContext
336
- // needs no closing and may not implement it): optional call, not a cast.
337
- try {
338
- await ((_a = ctx === null || ctx === void 0 ? void 0 : ctx.close) === null || _a === void 0 ? void 0 : _a.call(ctx));
339
- }
340
- catch ( /* already closed */_c) { /* already closed */ }
341
- }
342
494
  if (token === this.loadToken)
343
495
  this.syncDuration();
344
496
  }
@@ -419,13 +571,13 @@ const CoAudioPlayer = class {
419
571
  // below `aria-valuenow` the moment playback advances. ARIA 1.2 makes valuemax
420
572
  // optional for `slider`, so leaving it off while unknown is well-formed.
421
573
  const ariaMax = total ? Math.floor(total) : undefined;
422
- const bar = (hFrac, i, played = false) => (index.h("rect", { key: '4b294190b3be5e7cf15282d43e7e51164a3dda4c', 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 } }));
423
- return (index.h("div", { key: '0758a438c4a71e822de94f8b41219945f62ae62d', class: {
574
+ const bar = (hFrac, i, played = false) => (index.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 } }));
575
+ return (index.h("div", { key: 'afbbf524713a1146653e69928ee21e263202315f', class: {
424
576
  'co-audio-player': true,
425
577
  'co-audio-player--compact': this.compact,
426
578
  'co-audio-player--error': this.hasError,
427
579
  'co-audio-player--playing': this.isPlaying,
428
- }, part: "player" }, index.h("button", { key: '4c4161e7ba7344a9c6a32dcc8fd92c300b750d95', 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 }, index.h("co-icon", { key: 'ea749ee1cf627a49e3859029ca274170c8d69c63', name: this.hasError ? 'warning-circle' : this.isPlaying ? 'pause' : 'play', weight: "fill", size: this.compact ? 12 : 14 })), index.h("div", { key: 'f39bf8bf114860a672979fa53287672dccea1b7f', 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 }, index.h("svg", { key: '2145f303271ca676988999b5ea7d76ab384c5e2d', class: "co-audio-player__svg", viewBox: `0 0 ${vbW} ${H}`, preserveAspectRatio: "none", "aria-hidden": "true" }, this.peaks.map((hFrac, i) => bar(hFrac, i)), index.h("clipPath", { key: '6014c9f96f5df60086265a5e01344be80ebff741', id: "co-ap-clip" }, index.h("rect", { key: '5857fa3098fc0eb9df58b643c2b44316cf819ae8', x: "0", y: "0", width: (pct / 100) * vbW, height: H })), index.h("g", { key: '241608ddfdb863ba5b18444ed2bffb39e72abeb5', "clip-path": "url(#co-ap-clip)" }, this.peaks.map((hFrac, i) => bar(hFrac, i, true))))), index.h("span", { key: '18f190b8b23f8939da5dd2c230214d42a5b330c1', class: "co-audio-player__time", part: "time" }, formatTime(this.currentTime), " / ", formatTime(this.duration)), !this.compact && (index.h("button", { key: '4a29cae938ab347997bc76e0cfe5621b402730f7', 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 && (index.h("button", { key: 'c451449e629584bc3937b27b31b1124e510af91e', type: "button", class: "co-audio-player__dl", part: "download", "aria-label": "Descargar", onClick: () => this.download() }, index.h("co-icon", { key: 'f4d014129829ab57ddae0d3e32b1e0930f1bc288', name: "download-simple", size: 14 })))));
580
+ }, part: "player" }, index.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 }, index.h("co-icon", { key: 'e7a566257df253914593712bd20029bb8f1bcdfd', name: this.hasError ? 'warning-circle' : this.isPlaying ? 'pause' : 'play', weight: "fill", size: this.compact ? 12 : 14 })), index.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 }, index.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)), index.h("clipPath", { key: 'f0d37d76b3d6ab09dbd09582c8657b3fe7cb8c4c', id: "co-ap-clip" }, index.h("rect", { key: '436de0ce10f98c868760c76505d134f3db2dbb54', x: "0", y: "0", width: (pct / 100) * vbW, height: H })), index.h("g", { key: '939a1a8851d6e41b4d3c37d7201f87a6403830bb', "clip-path": "url(#co-ap-clip)" }, this.peaks.map((hFrac, i) => bar(hFrac, i, true))))), index.h("span", { key: 'bd57bdd97a780864aa633ec5174d4f733799a278', class: "co-audio-player__time", part: "time" }, formatTime(this.currentTime), " / ", formatTime(this.duration)), !this.compact && (index.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 && (index.h("button", { key: 'a1503b85bb2edf02566dcad9b8e83b1fea20b159', type: "button", class: "co-audio-player__dl", part: "download", "aria-label": "Descargar", onClick: () => this.download() }, index.h("co-icon", { key: 'dc96b8afdb8036e76a0c515d76c625460f4fc44d', name: "download-simple", size: 14 })))));
429
581
  }
430
582
  static get watchers() { return {
431
583
  "src": [{
@@ -403,10 +403,6 @@ var loadModule = (cmpMeta, hostRef, hmrVersionId) => {
403
403
  return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
404
404
  /* webpackMode: "lazy" */
405
405
  './co-loader.cjs.entry.js')); }).then(processMod, consoleError);
406
- case 'co-audio-player.cjs':
407
- return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
408
- /* webpackMode: "lazy" */
409
- './co-audio-player.cjs.entry.js')); }).then(processMod, consoleError);
410
406
  case 'co-bottom-sheet.cjs':
411
407
  return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
412
408
  /* webpackMode: "lazy" */
@@ -475,6 +471,10 @@ var loadModule = (cmpMeta, hostRef, hmrVersionId) => {
475
471
  return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
476
472
  /* webpackMode: "lazy" */
477
473
  './co-icon.cjs.entry.js')); }).then(processMod, consoleError);
474
+ case 'co-audio-player.cjs':
475
+ return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
476
+ /* webpackMode: "lazy" */
477
+ './co-audio-player.cjs.entry.js')); }).then(processMod, consoleError);
478
478
  case 'co-color-picker.cjs':
479
479
  return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
480
480
  /* webpackMode: "lazy" */