@prolibu-suite/cobalt-core 0.11.3 → 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 (41) 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/co-number-field.cjs.entry.js +12 -4
  4. package/dist/cjs/index-bHoidDwv.js +4 -4
  5. package/dist/cobalt/cobalt.esm.js +1 -1
  6. package/dist/cobalt/p-4a7fd0b4.entry.js +1 -0
  7. package/dist/cobalt/p-7c20207b.entry.js +1 -0
  8. package/dist/collection/components/co-audio-player/co-audio-player.js +40 -47
  9. package/dist/collection/components/co-audio-player/co-audio-player.peaks-cache.js +104 -0
  10. package/dist/collection/components/co-audio-player/co-audio-player.utils.js +61 -0
  11. package/dist/collection/components/co-autocomplete/co-autocomplete.js +1 -1
  12. package/dist/collection/components/co-avatar-group/co-avatar-group.js +1 -1
  13. package/dist/collection/components/co-checkbox-group/co-checkbox-group.js +1 -1
  14. package/dist/collection/components/co-color-area/co-color-area.js +2 -2
  15. package/dist/collection/components/co-color-slider/co-color-slider.js +1 -1
  16. package/dist/collection/components/co-combobox/co-combobox.js +1 -1
  17. package/dist/collection/components/co-command-search/co-command-search.js +1 -1
  18. package/dist/collection/components/co-data-table/co-data-table.js +1 -1
  19. package/dist/collection/components/co-date-range-picker/co-date-range-picker.js +2 -2
  20. package/dist/collection/components/co-document-editor/co-document-editor.js +5 -5
  21. package/dist/collection/components/co-number-field/co-number-field.js +12 -4
  22. package/dist/collection/components/co-popover/co-popover.js +1 -1
  23. package/dist/collection/components/co-radio-group/co-radio-group.js +1 -1
  24. package/dist/collection/components/co-ref-field/co-ref-field.js +4 -4
  25. package/dist/collection/components/co-segmented-bar/co-segmented-bar.js +1 -1
  26. package/dist/collection/components/co-select/co-select.js +1 -1
  27. package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +1 -1
  28. package/dist/collection/components/co-toast/co-toast.js +2 -2
  29. package/dist/collection/components/co-transfer-list/co-transfer-list.js +1 -1
  30. package/dist/collection/components/co-tree-item/co-tree-item.js +2 -2
  31. package/dist/components/co-audio-player.js +1 -1
  32. package/dist/components/co-number-field.js +1 -1
  33. package/dist/esm/co-audio-player.entry.js +198 -46
  34. package/dist/esm/co-number-field.entry.js +12 -4
  35. package/dist/esm/index-DJkaRtbb.js +4 -4
  36. package/dist/types/components/co-audio-player/co-audio-player.d.ts +8 -1
  37. package/dist/types/components/co-audio-player/co-audio-player.peaks-cache.d.ts +58 -0
  38. package/dist/types/components/co-audio-player/co-audio-player.utils.d.ts +33 -0
  39. package/package.json +4 -4
  40. package/dist/cobalt/p-358a6c2a.entry.js +0 -1
  41. package/dist/cobalt/p-ae999b94.entry.js +0 -1
@@ -1 +1 @@
1
- import{t as e,p as t,H as i,c as r,h as o}from"./index.js";import{R as n}from"./p-CWneqh4n.js";import{d as s}from"./p-KK2vAKs-.js";const c=(e,t)=>({integer:e.slice(0,15),decimals:t.slice(0,8)});function a(e,t=!1){const i=(null!=e?e:"")+"",r=t&&/^\s*-/.test(i),o=i.replace(/[^\d.]/g,""),[n,...s]=o.split("."),{integer:a,decimals:l}=c(n,s.join("")),d=s.length?`${a}.${l}`:a;return d?r?"-"+d:d:r?"-":""}function l(e){if(!e)return"";const t=e.startsWith("-"),i=t?e.slice(1):e,r=i.indexOf("."),o=r>=0?i.slice(0,r):i,n=r>=0?i.slice(r):"";let s="";for(let e=0;e<o.length;e++)e>0&&(o.length-e)%3==0&&(s+=","),s+=o[e];return`${t?"-":""}${s}${n}`}const d=/[^0-9.,-]/g,u=/[0-9.-]/,f=e=>{let t=0;for(const i of e)u.test(i)&&t++;return t},h=(e,t)=>{if(t<=0)return 0;let i=0;for(let r=0;r<e.length;r++)if(u.test(e[r])&&(i++,i===t))return r+1;return e.length},b=t(class extends i{constructor(e){super(),!1!==e&&this.__registerHost(),this.__attachShadow(),this.coChange=r(this,"coChange",7),this.coClear=r(this,"coClear",7),this.required=!1,this.layout="vertical",this.status="default",this.value=0,this.step=1,this.disabled=!1,this.placeholder="0",this.size="md",this.clearable=!1,this.hideControls=!1,this.focused=!1,this.emptiedByUser=!1,this.handleDecrement=()=>{this.canDecrement&&(this.emitValue(this.current-this.step),this.syncInputText())},this.handleIncrement=()=>{this.canIncrement&&(this.emitValue(this.current+this.step),this.syncInputText())},this.handleInput=e=>{var t;const i=e.target;if(this.isAmount)return void this.handleAmountInput(i);let r=i.value;const o=r.replace(d,"");if(o!==r){const e=(null!==(t=i.selectionStart)&&void 0!==t?t:r.length)-(r.length-o.length);i.value=o;const n=e<0?0:e;i.setSelectionRange(n,n),r=o}if(""===r)return void(this.clearable?(this.value=null,this.coChange.emit(null)):this.emptiedByUser=!0);if("-"===r)return;const n=parseFloat(r);isNaN(n)||this.emitValue(this.fromDisplay(n))},this.handlePaste=e=>{var t,i,r,o;if(!this.isAmount)return;e.preventDefault();const n=e.target,s=null!==(i=null===(t=e.clipboardData)||void 0===t?void 0:t.getData("text"))&&void 0!==i?i:"";let d=function(e){const t=((null!=e?e:"")+"").replace(/[^\d.,]/g,"");if(!t)return"";const i=t.lastIndexOf("."),r=t.lastIndexOf(",");let o=-1;if(i>=0&&r>=0)o=Math.max(i,r);else if(i>=0||r>=0){const e=i>=0?".":",",r=t.split(e);2===r.length&&3!==r[1].length&&(o=t.lastIndexOf(e))}const{integer:n,decimals:s}=c((o>=0?t.slice(0,o):t).replace(/[.,]/g,""),o>=0?t.slice(o+1).replace(/[.,]/g,""):"");return n||s?s?`${n||"0"}.${s}`:n:""}(s);d&&this.allowNegative&&/^\s*-/.test(s)&&(d="-"+d);const u=null!==(r=n.selectionStart)&&void 0!==r?r:n.value.length,b=null!==(o=n.selectionEnd)&&void 0!==o?o:u,m=a(n.value.slice(0,u),this.allowNegative),p=a(n.value.slice(b)),_=a(m+d+p,this.allowNegative),g=l(_);n.value=g;const v=h(g,f(m+d));if(n.setSelectionRange(v,v),""===_||"-"===_)return void(""===_&&this.clearable?(this.value=null,this.coChange.emit(null)):""===_&&(this.emptiedByUser=!0));const x=parseFloat(_);isNaN(x)||this.emitValue(x)},this.handleClear=()=>{var e;this.value=null,this.syncInputText(),this.coClear.emit(),this.coChange.emit(null),null===(e=this.inputEl)||void 0===e||e.focus()},this.handleBlur=()=>{var e;this.focused=!1,this.emptiedByUser?this.emitValue(null!==(e=this.min)&&void 0!==e?e:0):this.isEmpty||this.emitValue(this.value),this.syncInputText()},this.handleFocus=()=>{this.focused=!0},this.handleKeyDown=e=>{"ArrowUp"===e.key?(e.preventDefault(),this.handleIncrement()):"ArrowDown"===e.key&&(e.preventDefault(),this.handleDecrement())}}onValueChange(){this.inputEl&&!this.focused&&this.inputEl.value!==this.displayValue&&(this.inputEl.value=this.displayValue)}get isEmpty(){return null==this.value||Number.isNaN(this.value)}get current(){var e;return this.isEmpty?null!==(e=this.min)&&void 0!==e?e:0:this.value}get statusIcon(){return{error:"warning-circle",success:"check-circle",warning:"warning",info:"info"}[this.status]||null}get canDecrement(){return!this.disabled&&(void 0===this.min||this.current>this.min)}get canIncrement(){return!this.disabled&&(void 0===this.max||this.current<this.max)}clamp(e){return void 0!==this.min&&e<this.min?this.min:void 0!==this.max&&e>this.max?this.max:e}get scaled(){return"rate"===this.format||"percent"===this.format}get isAmount(){return"amount"===this.format}get allowNegative(){return void 0===this.min||this.min<0}get displayValue(){if(this.isEmpty)return"";const e=this.toDisplay(this.value)+"";return this.isAmount?l(e):e}toDisplay(e){return this.scaled?Number((100*e).toFixed(6)):e}fromDisplay(e){return this.scaled?e/100:e}emitValue(e){this.emptiedByUser=!1;const t=this.clamp(e);this.value=t,this.coChange.emit(t)}handleAmountInput(e){var t;const i=e.value,r=null!==(t=e.selectionStart)&&void 0!==t?t:i.length,o=f(i.slice(0,r)),n=a(i,this.allowNegative),s=l(n);if(e.value!==s){e.value=s;const t=h(s,o);e.setSelectionRange(t,t)}if(""===n)return void(this.clearable?(this.value=null,this.coChange.emit(null)):this.emptiedByUser=!0);if("-"===n)return;const c=parseFloat(n);isNaN(c)||this.emitValue(c)}syncInputText(){this.inputEl&&this.inputEl.value!==this.displayValue&&(this.inputEl.value=this.displayValue)}get affixText(){return void 0!==this.suffix?this.suffix||null:this.scaled&&!this.isEmpty?"%":null}render(){return o("div",{key:"cf4d63ce0278fd8f429aa4bf541497b7cf178ffe",class:{"co-number-field":!0,"co-number-field--disabled":this.disabled,"co-number-field--focused":this.focused,["co-number-field--"+this.size]:!0,["co-number-field--"+this.status]:"default"!==this.status}},(this.label||this.secondaryLabel)&&o("div",{key:"ecf9fd16f18a3ebbaef5b81b0c8ae41b80f04abf",class:"co-number-field__labels"},this.label&&o("label",{key:"7602d901e085e3380dff2a355d61ac1dc515428b",class:"co-number-field__label"},this.label,o(n,{key:"361743bf41c66ff28ceec00aba583328f2404713",required:this.required})),this.secondaryLabel&&o("span",{key:"ec9af4b27f3915704ef89ed6f51e7184eb5e1dd4",class:"co-number-field__secondary-label"},this.secondaryLabel)),o("div",{key:"30f1716aed06da03f924fa1da2b2ab29dbede0bb",class:"co-number-field__row",part:"row"},!this.hideControls&&o("button",{key:"1dbee1178df2a86e54ae42572a20ecf30484b8bc",class:"co-number-field__btn co-number-field__btn--minus",type:"button",tabIndex:-1,disabled:!this.canDecrement,onClick:this.handleDecrement,"aria-label":"Decrease"},o("co-icon",{key:"40f61bd3774fb03b6aaceaef006dd45f2df260d3",name:"minus",size:16})),o("input",{key:"254a74cd61a2776a69953965f8b289267b25fbb2",ref:e=>this.inputEl=e,class:"co-number-field__input",type:"text",inputMode:"decimal",value:this.focused&&this.inputEl?this.inputEl.value:this.displayValue,placeholder:this.placeholder,disabled:this.disabled,onInput:this.handleInput,onPaste:this.handlePaste,onFocus:this.handleFocus,onBlur:this.handleBlur,onKeyDown:this.handleKeyDown,part:"input"}),this.affixText&&o("span",{key:"836ea4e4cba0b21a36b1017d6fc0c4d621d69e56",class:"co-number-field__affix","aria-hidden":"true"},this.affixText),this.clearable&&!this.isEmpty&&!this.disabled&&o("button",{key:"5eefa937a055dea1855d1bc3bae675003d8382c8",class:"co-number-field__clear",type:"button",tabIndex:-1,onClick:this.handleClear,"aria-label":"Clear"},o("co-icon",{key:"096dec8d88b7b241cd83a04788bc4db05feec585",name:"x-circle",size:16,weight:"fill"})),o("slot",{key:"1ea7d1ff2c61d74b0884e7b8f1b7b54de4978876",name:"suffix"}),!this.hideControls&&o("button",{key:"9ae95697534b85dcd928c0d0e7f72a3b8c5402c7",class:"co-number-field__btn co-number-field__btn--plus",type:"button",tabIndex:-1,disabled:!this.canIncrement,onClick:this.handleIncrement,"aria-label":"Increase"},o("co-icon",{key:"af526ac4278865052f32ae7359a571aa485492af",name:"plus",size:16}))),this.helperText&&o("div",{key:"9bad3ffd8c70ce49205132f9f73294a83b6ebbc3",class:"co-number-field__helper co-number-field__helper--"+this.status},this.statusIcon&&o("co-icon",{key:"4987bc82011c4bb93e1bd00ef1ffd45d2e8681f4",name:this.statusIcon,size:14,weight:"fill",class:"co-number-field__helper-icon"}),o("span",{key:"8eac5821653dc0110113f94833e13f27411188a7",class:"co-number-field__helper-text"},this.helperText)))}get el(){return this}static get watchers(){return{value:[{onValueChange:0}]}}static get style(){return":host{display:block;font-family:var(--co-font-family-primary);font-size:var(--co-font-size-14);letter-spacing:var(--co-font-letter-spacing-body);line-height:1}.co-number-field{display:flex;flex-direction:column;gap:var(--co-spacing-sm2)}.co-number-field__labels{display:flex;align-items:center;justify-content:space-between;padding:0 var(--co-spacing-xs);gap:var(--co-spacing-sm2)}.co-number-field__label{color:var(--co-semantic-text-secondary);font-size:var(--co-font-size-14);font-weight:400;line-height:1;white-space:nowrap;cursor:default}.co-number-field__secondary-label{color:var(--co-semantic-text-muted);font-size:var(--co-font-size-14);font-weight:400;line-height:1;white-space:nowrap;text-align:right}.co-number-field--disabled .co-number-field__label,.co-number-field--disabled .co-number-field__secondary-label{color:var(--co-semantic-text-disabled)}.co-number-field__row{display:flex;align-items:center;gap:0;background:var(--co-semantic-surface-page);border:1px solid var(--co-semantic-border-field);border-radius:var(--co-border-radius-xs);min-height:40px;overflow:hidden;box-sizing:border-box;transition:border-color var(--co-motion-duration-fast) var(--co-motion-ease-out), box-shadow var(--co-motion-duration-fast) var(--co-motion-ease-out)}.co-number-field--s .co-number-field__row{min-height:28px}.co-number-field--s .co-number-field__btn{width:28px}.co-number-field--sm .co-number-field__row{min-height:32px}.co-number-field--sm .co-number-field__btn{width:32px}.co-number-field--lg .co-number-field__row{min-height:48px}.co-number-field--lg .co-number-field__btn{width:48px}.co-number-field--focused .co-number-field__row{border-color:var(--co-semantic-border-focus);box-shadow:0 0 0 2px var(--co-semantic-border-focus-ring)}.co-number-field--disabled .co-number-field__row{background:var(--co-semantic-surface-disabled);border-color:transparent}.co-number-field--error .co-number-field__row{border-color:var(--co-semantic-status-error)}.co-number-field--success .co-number-field__row{border-color:var(--co-semantic-status-success)}.co-number-field--warning .co-number-field__row{border-color:var(--co-color-status-review)}.co-number-field--info .co-number-field__row{border-color:var(--co-semantic-status-info)}.co-number-field__btn{display:flex;align-items:center;justify-content:center;width:40px;align-self:stretch;flex-shrink:0;border:none;background:transparent;color:var(--co-semantic-text-default);cursor:pointer;padding:0;transition:background var(--co-motion-duration-fast) var(--co-motion-ease-out)}.co-number-field__btn:hover:not(:disabled){background:var(--co-semantic-surface-hover)}.co-number-field__btn:active:not(:disabled){background:var(--co-semantic-surface-muted)}.co-number-field__btn:disabled{color:var(--co-semantic-text-disabled);cursor:not-allowed}.co-number-field__btn--minus{border-right:1px solid var(--co-semantic-border-subtle)}.co-number-field__btn--plus{border-left:1px solid var(--co-semantic-border-subtle)}.co-number-field__clear{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:24px;height:100%;padding:0;border:none;background:transparent;color:var(--co-semantic-text-muted);cursor:pointer;transition:color var(--co-motion-duration-fast) var(--co-motion-ease-out)}.co-number-field__clear:hover{color:var(--co-semantic-text-default)}.co-number-field__input{flex:1;min-width:0;border:none;background:transparent;text-align:center;color:var(--co-semantic-text-default);font-family:inherit;font-size:inherit;font-weight:400;line-height:1;padding:var(--co-spacing-sm2) var(--co-spacing-sm2);outline:none;-moz-appearance:textfield}.co-number-field__input::placeholder{color:var(--co-semantic-text-muted)}.co-number-field__affix{flex-shrink:0;padding-right:var(--co-spacing-sm2);color:var(--co-semantic-text-muted);font-size:inherit;font-weight:400;line-height:1;pointer-events:none;user-select:none}::slotted([slot='suffix']){flex-shrink:0;margin-right:var(--co-spacing-xxs)}:host([hide-controls]) .co-number-field__input{text-align:right;padding-left:var(--co-spacing-md);padding-right:var(--co-spacing-md)}:host([hide-controls]) .co-number-field__affix{padding-right:var(--co-spacing-md);font-size:var(--co-font-size-12)}.co-number-field__input::-webkit-outer-spin-button,.co-number-field__input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.co-number-field--disabled .co-number-field__input{color:var(--co-semantic-text-disabled);cursor:not-allowed}.co-number-field__helper{display:flex;align-items:center;gap:var(--co-spacing-xs);padding:0 var(--co-spacing-xs)}.co-number-field__helper-icon{flex-shrink:0;display:flex;align-items:center}.co-number-field__helper-text{color:var(--co-semantic-text-muted);font-size:var(--co-font-size-14);font-weight:400;line-height:1}.co-number-field__helper--error .co-number-field__helper-icon,.co-number-field__helper--error .co-number-field__helper-text{color:var(--co-semantic-status-error)}.co-number-field__helper--success .co-number-field__helper-icon,.co-number-field__helper--success .co-number-field__helper-text{color:var(--co-semantic-status-success)}.co-number-field__helper--warning .co-number-field__helper-icon,.co-number-field__helper--warning .co-number-field__helper-text{color:var(--co-color-status-review)}.co-number-field__helper--info .co-number-field__helper-icon,.co-number-field__helper--info .co-number-field__helper-text{color:var(--co-semantic-status-info)}:host([layout='horizontal']) .co-number-field{display:grid;grid-template-columns:var(--co-field-label-width, max-content) 1fr;align-items:start;column-gap:var(--co-spacing-md);row-gap:var(--co-spacing-sm2)}:host([layout='horizontal']) .co-number-field__labels{grid-column:1;grid-row:1;padding-top:var(--co-spacing-sm2);min-width:0}:host([layout='horizontal']) .co-number-field__label{white-space:normal}:host([layout='horizontal']) .co-number-field>:not(.co-number-field__labels){grid-column:2;min-width:0}.co-field-required{margin-left:2px;color:var(--co-color-status-error)}.co-field-required__caption{margin-left:4px;color:var(--co-semantic-text-muted);font-size:var(--co-font-size-12);font-weight:400}.co-field-required__caption::before{content:var(--co-field-required-text, ' (required)')}"}},[257,"co-number-field",{label:[1],required:[4],secondaryLabel:[1,"secondary-label"],helperText:[1,"helper-text"],layout:[513],status:[1],value:[1538],min:[2],max:[2],step:[2],disabled:[4],placeholder:[1],size:[513],clearable:[4],format:[513],hideControls:[516,"hide-controls"],suffix:[1],focused:[32]},void 0,{value:[{onValueChange:0}]}]);function m(){"undefined"!=typeof customElements&&["co-number-field","co-icon"].forEach((t=>{switch(t){case"co-number-field":customElements.get(e(t))||customElements.define(e(t),b);break;case"co-icon":customElements.get(e(t))||s()}}))}m();const p=b,_=m;export{p as CoNumberField,_ as defineCustomElement}
1
+ import{t as e,p as t,H as i,c as r,h as o}from"./index.js";import{R as n}from"./p-CWneqh4n.js";import{d as s}from"./p-KK2vAKs-.js";const c=(e,t)=>({integer:e.slice(0,15),decimals:t.slice(0,8)});function a(e,t=!1){const i=(null!=e?e:"")+"",r=t&&/^\s*-/.test(i),o=i.replace(/[^\d.]/g,""),[n,...s]=o.split("."),{integer:a,decimals:l}=c(n,s.join("")),d=s.length?`${a}.${l}`:a;return d?r?"-"+d:d:r?"-":""}function l(e){if(!e)return"";const t=e.startsWith("-"),i=t?e.slice(1):e,r=i.indexOf("."),o=r>=0?i.slice(0,r):i,n=r>=0?i.slice(r):"";let s="";for(let e=0;e<o.length;e++)e>0&&(o.length-e)%3==0&&(s+=","),s+=o[e];return`${t?"-":""}${s}${n}`}const d=/[^0-9.,-]/g,u=/[0-9.-]/,h=e=>{let t=0;for(const i of e)u.test(i)&&t++;return t},f=(e,t)=>{if(t<=0)return 0;let i=0;for(let r=0;r<e.length;r++)if(u.test(e[r])&&(i++,i===t))return r+1;return e.length},b=t(class extends i{constructor(e){super(),!1!==e&&this.__registerHost(),this.__attachShadow(),this.coChange=r(this,"coChange",7),this.coClear=r(this,"coClear",7),this.required=!1,this.layout="vertical",this.status="default",this.value=0,this.step=1,this.disabled=!1,this.placeholder="0",this.size="md",this.clearable=!1,this.hideControls=!1,this.focused=!1,this.emptiedByUser=!1,this.handleDecrement=()=>{this.canDecrement&&(this.emitValue(this.current-this.step),this.syncInputText())},this.handleIncrement=()=>{this.canIncrement&&(this.emitValue(this.current+this.step),this.syncInputText())},this.handleInput=e=>{var t;const i=e.target;if(this.isAmount)return void this.handleAmountInput(i);let r=i.value;const o=r.replace(d,"");if(o!==r){const e=(null!==(t=i.selectionStart)&&void 0!==t?t:r.length)-(r.length-o.length);i.value=o;const n=e<0?0:e;i.setSelectionRange(n,n),r=o}if(""===r)return void(this.clearable?(this.value=null,this.coChange.emit(null)):this.emptiedByUser=!0);if("-"===r)return;const n=parseFloat(function(e){return 1!=e.split(",").length-1||e.includes(".")?e:e.replace(",",".")}(r));isNaN(n)||this.emitValue(this.fromDisplay(n))},this.handlePaste=e=>{var t,i,r,o;if(!this.isAmount)return;e.preventDefault();const n=e.target,s=null!==(i=null===(t=e.clipboardData)||void 0===t?void 0:t.getData("text"))&&void 0!==i?i:"";let d=function(e){const t=((null!=e?e:"")+"").replace(/[^\d.,]/g,"");if(!t)return"";const i=t.lastIndexOf("."),r=t.lastIndexOf(",");let o=-1;if(i>=0&&r>=0)o=Math.max(i,r);else if(i>=0||r>=0){const e=i>=0?".":",",r=t.split(e);2===r.length&&3!==r[1].length&&(o=t.lastIndexOf(e))}const{integer:n,decimals:s}=c((o>=0?t.slice(0,o):t).replace(/[.,]/g,""),o>=0?t.slice(o+1).replace(/[.,]/g,""):"");return n||s?s?`${n||"0"}.${s}`:n:""}(s);d&&this.allowNegative&&/^\s*-/.test(s)&&(d="-"+d);const u=null!==(r=n.selectionStart)&&void 0!==r?r:n.value.length,b=null!==(o=n.selectionEnd)&&void 0!==o?o:u,m=a(n.value.slice(0,u),this.allowNegative),p=a(n.value.slice(b)),_=a(m+d+p,this.allowNegative),g=l(_);n.value=g;const v=f(g,h(m+d));if(n.setSelectionRange(v,v),""===_||"-"===_)return void(""===_&&this.clearable?(this.value=null,this.coChange.emit(null)):""===_&&(this.emptiedByUser=!0));const x=parseFloat(_);isNaN(x)||this.emitValue(x)},this.handleClear=()=>{var e;this.value=null,this.syncInputText(),this.coClear.emit(),this.coChange.emit(null),null===(e=this.inputEl)||void 0===e||e.focus()},this.handleBlur=()=>{var e;this.focused=!1,this.emptiedByUser?this.emitValue(null!==(e=this.min)&&void 0!==e?e:0):this.isEmpty||this.emitValue(this.value),this.syncInputText()},this.handleFocus=()=>{this.focused=!0},this.handleKeyDown=e=>{"ArrowUp"===e.key?(e.preventDefault(),this.handleIncrement()):"ArrowDown"===e.key&&(e.preventDefault(),this.handleDecrement())}}onValueChange(){this.inputEl&&!this.focused&&this.inputEl.value!==this.displayValue&&(this.inputEl.value=this.displayValue)}get isEmpty(){return null==this.value||Number.isNaN(this.value)}get current(){var e;return this.isEmpty?null!==(e=this.min)&&void 0!==e?e:0:this.value}get statusIcon(){return{error:"warning-circle",success:"check-circle",warning:"warning",info:"info"}[this.status]||null}get canDecrement(){return!this.disabled&&(void 0===this.min||this.current>this.min)}get canIncrement(){return!this.disabled&&(void 0===this.max||this.current<this.max)}clamp(e){return void 0!==this.min&&e<this.min?this.min:void 0!==this.max&&e>this.max?this.max:e}get scaled(){return"rate"===this.format||"percent"===this.format}get isAmount(){return"amount"===this.format}get allowNegative(){return void 0===this.min||this.min<0}get displayValue(){if(this.isEmpty)return"";const e=this.toDisplay(this.value)+"";return this.isAmount?l(e):e}toDisplay(e){return this.scaled?Number((100*e).toFixed(6)):e}fromDisplay(e){return this.scaled?e/100:e}emitValue(e){this.emptiedByUser=!1;const t=this.clamp(e);this.value=t,this.coChange.emit(t)}handleAmountInput(e){var t;const i=e.value,r=null!==(t=e.selectionStart)&&void 0!==t?t:i.length,o=h(i.slice(0,r)),n=a(i,this.allowNegative),s=l(n);if(e.value!==s){e.value=s;const t=f(s,o);e.setSelectionRange(t,t)}if(""===n)return void(this.clearable?(this.value=null,this.coChange.emit(null)):this.emptiedByUser=!0);if("-"===n)return;const c=parseFloat(n);isNaN(c)||this.emitValue(c)}syncInputText(){this.inputEl&&this.inputEl.value!==this.displayValue&&(this.inputEl.value=this.displayValue)}get affixText(){return void 0!==this.suffix?this.suffix||null:this.scaled&&!this.isEmpty?"%":null}render(){return o("div",{key:"87d77047c35e2f0b3b9a978839e5b3c99541dc2e",class:{"co-number-field":!0,"co-number-field--disabled":this.disabled,"co-number-field--focused":this.focused,["co-number-field--"+this.size]:!0,["co-number-field--"+this.status]:"default"!==this.status}},(this.label||this.secondaryLabel)&&o("div",{key:"22f0674035a68861fae6ce37aba4e51b10c03226",class:"co-number-field__labels"},this.label&&o("label",{key:"442ee74bed62bf7c800f231ad4992b791b85a30c",class:"co-number-field__label"},this.label,o(n,{key:"ff7517b83b8e942512dcb8c1f504eafab55cea90",required:this.required})),this.secondaryLabel&&o("span",{key:"736889aae85065fb28a56131e2f7a3da9fc86a48",class:"co-number-field__secondary-label"},this.secondaryLabel)),o("div",{key:"fd6163c7cb83073db2c19c3477447076372abb44",class:"co-number-field__row",part:"row"},!this.hideControls&&o("button",{key:"e1a52831b83c3255ce63c4ed48a228fe4fdb1629",class:"co-number-field__btn co-number-field__btn--minus",type:"button",tabIndex:-1,disabled:!this.canDecrement,onClick:this.handleDecrement,"aria-label":"Decrease"},o("co-icon",{key:"86e0ce9b9801e50d9bda6b18a8e1f6f96581b178",name:"minus",size:16})),o("input",{key:"4bdf89e9a4958841a948b3bd6e9886898917f464",ref:e=>this.inputEl=e,class:"co-number-field__input",type:"text",inputMode:"decimal",value:this.focused&&this.inputEl?this.inputEl.value:this.displayValue,placeholder:this.placeholder,disabled:this.disabled,onInput:this.handleInput,onPaste:this.handlePaste,onFocus:this.handleFocus,onBlur:this.handleBlur,onKeyDown:this.handleKeyDown,part:"input"}),this.affixText&&o("span",{key:"7bc7c12c6fe2708691d9a2972f9ed3037e82a1a8",class:"co-number-field__affix","aria-hidden":"true"},this.affixText),this.clearable&&!this.isEmpty&&!this.disabled&&o("button",{key:"2ef7c424a34a8a6cbfe8ac4cd0a5d8f0ed74e6d4",class:"co-number-field__clear",type:"button",tabIndex:-1,onClick:this.handleClear,"aria-label":"Clear"},o("co-icon",{key:"c428ca841c0de013cd151bd356686be43992de04",name:"x-circle",size:16,weight:"fill"})),o("slot",{key:"fb127a663d3839bbb10b8232b250eaf67faab898",name:"suffix"}),!this.hideControls&&o("button",{key:"684329d2f32a58f6be2a124f0cd20986a7fc49af",class:"co-number-field__btn co-number-field__btn--plus",type:"button",tabIndex:-1,disabled:!this.canIncrement,onClick:this.handleIncrement,"aria-label":"Increase"},o("co-icon",{key:"7ef48466e060efb314466d62216a8e00168ddbcd",name:"plus",size:16}))),this.helperText&&o("div",{key:"2cf6df2556160fef3c1dfd1b525991eb822cb285",class:"co-number-field__helper co-number-field__helper--"+this.status},this.statusIcon&&o("co-icon",{key:"905ad795d9714fa9ae8fc2b9d4b9f6a7bba2d2ce",name:this.statusIcon,size:14,weight:"fill",class:"co-number-field__helper-icon"}),o("span",{key:"0c4825c4145298700de006eb3170e9ec3b52d3b7",class:"co-number-field__helper-text"},this.helperText)))}get el(){return this}static get watchers(){return{value:[{onValueChange:0}]}}static get style(){return":host{display:block;font-family:var(--co-font-family-primary);font-size:var(--co-font-size-14);letter-spacing:var(--co-font-letter-spacing-body);line-height:1}.co-number-field{display:flex;flex-direction:column;gap:var(--co-spacing-sm2)}.co-number-field__labels{display:flex;align-items:center;justify-content:space-between;padding:0 var(--co-spacing-xs);gap:var(--co-spacing-sm2)}.co-number-field__label{color:var(--co-semantic-text-secondary);font-size:var(--co-font-size-14);font-weight:400;line-height:1;white-space:nowrap;cursor:default}.co-number-field__secondary-label{color:var(--co-semantic-text-muted);font-size:var(--co-font-size-14);font-weight:400;line-height:1;white-space:nowrap;text-align:right}.co-number-field--disabled .co-number-field__label,.co-number-field--disabled .co-number-field__secondary-label{color:var(--co-semantic-text-disabled)}.co-number-field__row{display:flex;align-items:center;gap:0;background:var(--co-semantic-surface-page);border:1px solid var(--co-semantic-border-field);border-radius:var(--co-border-radius-xs);min-height:40px;overflow:hidden;box-sizing:border-box;transition:border-color var(--co-motion-duration-fast) var(--co-motion-ease-out), box-shadow var(--co-motion-duration-fast) var(--co-motion-ease-out)}.co-number-field--s .co-number-field__row{min-height:28px}.co-number-field--s .co-number-field__btn{width:28px}.co-number-field--sm .co-number-field__row{min-height:32px}.co-number-field--sm .co-number-field__btn{width:32px}.co-number-field--lg .co-number-field__row{min-height:48px}.co-number-field--lg .co-number-field__btn{width:48px}.co-number-field--focused .co-number-field__row{border-color:var(--co-semantic-border-focus);box-shadow:0 0 0 2px var(--co-semantic-border-focus-ring)}.co-number-field--disabled .co-number-field__row{background:var(--co-semantic-surface-disabled);border-color:transparent}.co-number-field--error .co-number-field__row{border-color:var(--co-semantic-status-error)}.co-number-field--success .co-number-field__row{border-color:var(--co-semantic-status-success)}.co-number-field--warning .co-number-field__row{border-color:var(--co-color-status-review)}.co-number-field--info .co-number-field__row{border-color:var(--co-semantic-status-info)}.co-number-field__btn{display:flex;align-items:center;justify-content:center;width:40px;align-self:stretch;flex-shrink:0;border:none;background:transparent;color:var(--co-semantic-text-default);cursor:pointer;padding:0;transition:background var(--co-motion-duration-fast) var(--co-motion-ease-out)}.co-number-field__btn:hover:not(:disabled){background:var(--co-semantic-surface-hover)}.co-number-field__btn:active:not(:disabled){background:var(--co-semantic-surface-muted)}.co-number-field__btn:disabled{color:var(--co-semantic-text-disabled);cursor:not-allowed}.co-number-field__btn--minus{border-right:1px solid var(--co-semantic-border-subtle)}.co-number-field__btn--plus{border-left:1px solid var(--co-semantic-border-subtle)}.co-number-field__clear{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:24px;height:100%;padding:0;border:none;background:transparent;color:var(--co-semantic-text-muted);cursor:pointer;transition:color var(--co-motion-duration-fast) var(--co-motion-ease-out)}.co-number-field__clear:hover{color:var(--co-semantic-text-default)}.co-number-field__input{flex:1;min-width:0;border:none;background:transparent;text-align:center;color:var(--co-semantic-text-default);font-family:inherit;font-size:inherit;font-weight:400;line-height:1;padding:var(--co-spacing-sm2) var(--co-spacing-sm2);outline:none;-moz-appearance:textfield}.co-number-field__input::placeholder{color:var(--co-semantic-text-muted)}.co-number-field__affix{flex-shrink:0;padding-right:var(--co-spacing-sm2);color:var(--co-semantic-text-muted);font-size:inherit;font-weight:400;line-height:1;pointer-events:none;user-select:none}::slotted([slot='suffix']){flex-shrink:0;margin-right:var(--co-spacing-xxs)}:host([hide-controls]) .co-number-field__input{text-align:right;padding-left:var(--co-spacing-md);padding-right:var(--co-spacing-md)}:host([hide-controls]) .co-number-field__affix{padding-right:var(--co-spacing-md);font-size:var(--co-font-size-12)}.co-number-field__input::-webkit-outer-spin-button,.co-number-field__input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.co-number-field--disabled .co-number-field__input{color:var(--co-semantic-text-disabled);cursor:not-allowed}.co-number-field__helper{display:flex;align-items:center;gap:var(--co-spacing-xs);padding:0 var(--co-spacing-xs)}.co-number-field__helper-icon{flex-shrink:0;display:flex;align-items:center}.co-number-field__helper-text{color:var(--co-semantic-text-muted);font-size:var(--co-font-size-14);font-weight:400;line-height:1}.co-number-field__helper--error .co-number-field__helper-icon,.co-number-field__helper--error .co-number-field__helper-text{color:var(--co-semantic-status-error)}.co-number-field__helper--success .co-number-field__helper-icon,.co-number-field__helper--success .co-number-field__helper-text{color:var(--co-semantic-status-success)}.co-number-field__helper--warning .co-number-field__helper-icon,.co-number-field__helper--warning .co-number-field__helper-text{color:var(--co-color-status-review)}.co-number-field__helper--info .co-number-field__helper-icon,.co-number-field__helper--info .co-number-field__helper-text{color:var(--co-semantic-status-info)}:host([layout='horizontal']) .co-number-field{display:grid;grid-template-columns:var(--co-field-label-width, max-content) 1fr;align-items:start;column-gap:var(--co-spacing-md);row-gap:var(--co-spacing-sm2)}:host([layout='horizontal']) .co-number-field__labels{grid-column:1;grid-row:1;padding-top:var(--co-spacing-sm2);min-width:0}:host([layout='horizontal']) .co-number-field__label{white-space:normal}:host([layout='horizontal']) .co-number-field>:not(.co-number-field__labels){grid-column:2;min-width:0}.co-field-required{margin-left:2px;color:var(--co-color-status-error)}.co-field-required__caption{margin-left:4px;color:var(--co-semantic-text-muted);font-size:var(--co-font-size-12);font-weight:400}.co-field-required__caption::before{content:var(--co-field-required-text, ' (required)')}"}},[257,"co-number-field",{label:[1],required:[4],secondaryLabel:[1,"secondary-label"],helperText:[1,"helper-text"],layout:[513],status:[1],value:[1538],min:[2],max:[2],step:[2],disabled:[4],placeholder:[1],size:[513],clearable:[4],format:[513],hideControls:[516,"hide-controls"],suffix:[1],focused:[32]},void 0,{value:[{onValueChange:0}]}]);function m(){"undefined"!=typeof customElements&&["co-number-field","co-icon"].forEach((t=>{switch(t){case"co-number-field":customElements.get(e(t))||customElements.define(e(t),b);break;case"co-icon":customElements.get(e(t))||s()}}))}m();const p=b,_=m;export{p as CoNumberField,_ as defineCustomElement}
@@ -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
- // Decoded mono PCM, kept so bars can be re-resampled on resize without re-fetching.
112
- this.rawChannelData = null;
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 cached PCM if decoded, else the
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.rawChannelData
245
- ? resamplePeaks(this.rawChannelData, this.effectiveBars)
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.rawChannelData = null;
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
- * `OfflineAudioContext` FIRST, and a live `AudioContext` only as the
296
- * fallback: decoding a waveform needs no audio OUTPUT, and a live context
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
- this.rawChannelData = audioBuf.getChannelData(0);
316
- this.decodedDuration = audioBuf.duration;
317
- this.peaks = resamplePeaks(this.rawChannelData, this.effectiveBars);
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 (_b) {
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.rawChannelData = null;
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: '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 } }));
421
- return (h("div", { key: '0758a438c4a71e822de94f8b41219945f62ae62d', class: {
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: '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 }, h("co-icon", { key: 'ea749ee1cf627a49e3859029ca274170c8d69c63', name: this.hasError ? 'warning-circle' : this.isPlaying ? 'pause' : 'play', weight: "fill", size: this.compact ? 12 : 14 })), 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 }, 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)), h("clipPath", { key: '6014c9f96f5df60086265a5e01344be80ebff741', id: "co-ap-clip" }, h("rect", { key: '5857fa3098fc0eb9df58b643c2b44316cf819ae8', x: "0", y: "0", width: (pct / 100) * vbW, height: H })), h("g", { key: '241608ddfdb863ba5b18444ed2bffb39e72abeb5', "clip-path": "url(#co-ap-clip)" }, this.peaks.map((hFrac, i) => bar(hFrac, i, true))))), h("span", { key: '18f190b8b23f8939da5dd2c230214d42a5b330c1', class: "co-audio-player__time", part: "time" }, formatTime(this.currentTime), " / ", formatTime(this.duration)), !this.compact && (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 && (h("button", { key: 'c451449e629584bc3937b27b31b1124e510af91e', type: "button", class: "co-audio-player__dl", part: "download", "aria-label": "Descargar", onClick: () => this.download() }, h("co-icon", { key: 'f4d014129829ab57ddae0d3e32b1e0930f1bc288', name: "download-simple", size: 14 })))));
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": [{
@@ -108,6 +108,11 @@ const coNumberFieldCss = () => `:host{display:block;font-family:var(--co-font-fa
108
108
  * blur—, solo impide que el campo muestre algo que no es un número.
109
109
  */
110
110
  const NON_NUMERIC = /[^0-9.,-]/g;
111
+ /** «12,5» → «12.5»; con dos comas o con punto no se adivina (queda igual). */
112
+ function decimalCommaToDot(raw) {
113
+ const commas = raw.split(',').length - 1;
114
+ return commas === 1 && !raw.includes('.') ? raw.replace(',', '.') : raw;
115
+ }
111
116
  /** Caracteres que CUENTAN para el caret en modo amount (la coma es decorado). */
112
117
  const SIGNIFICANT = /[0-9.-]/;
113
118
  const countSignificant = (text) => {
@@ -215,7 +220,10 @@ const CoNumberField = class {
215
220
  }
216
221
  if (raw === '-')
217
222
  return;
218
- const num = parseFloat(raw);
223
+ // Una coma sola y sin punto es el separador decimal (es/pt: «12,5»): parseFloat
224
+ // cortaría en ella y commitearía 12. Solo se normaliza lo que se parsea — el
225
+ // texto del campo queda como lo tipeó el usuario (y el caret en su lugar).
226
+ const num = parseFloat(decimalCommaToDot(raw));
219
227
  if (!isNaN(num)) {
220
228
  this.emitValue(this.fromDisplay(num));
221
229
  }
@@ -430,18 +438,18 @@ const CoNumberField = class {
430
438
  return this.scaled && !this.isEmpty ? '%' : null;
431
439
  }
432
440
  render() {
433
- return (h("div", { key: 'cf4d63ce0278fd8f429aa4bf541497b7cf178ffe', class: {
441
+ return (h("div", { key: '87d77047c35e2f0b3b9a978839e5b3c99541dc2e', class: {
434
442
  'co-number-field': true,
435
443
  'co-number-field--disabled': this.disabled,
436
444
  'co-number-field--focused': this.focused,
437
445
  [`co-number-field--${this.size}`]: true,
438
446
  [`co-number-field--${this.status}`]: this.status !== 'default',
439
- } }, (this.label || this.secondaryLabel) && (h("div", { key: 'ecf9fd16f18a3ebbaef5b81b0c8ae41b80f04abf', class: "co-number-field__labels" }, this.label && h("label", { key: '7602d901e085e3380dff2a355d61ac1dc515428b', class: "co-number-field__label" }, this.label, h(RequiredMark, { key: '361743bf41c66ff28ceec00aba583328f2404713', required: this.required })), this.secondaryLabel && h("span", { key: 'ec9af4b27f3915704ef89ed6f51e7184eb5e1dd4', class: "co-number-field__secondary-label" }, this.secondaryLabel))), h("div", { key: '30f1716aed06da03f924fa1da2b2ab29dbede0bb', class: "co-number-field__row", part: "row" }, !this.hideControls && (h("button", { key: '1dbee1178df2a86e54ae42572a20ecf30484b8bc', class: "co-number-field__btn co-number-field__btn--minus", type: "button", tabIndex: -1, disabled: !this.canDecrement, onClick: this.handleDecrement, "aria-label": "Decrease" }, h("co-icon", { key: '40f61bd3774fb03b6aaceaef006dd45f2df260d3', name: "minus", size: 16 }))), h("input", { key: '254a74cd61a2776a69953965f8b289267b25fbb2', ref: el => (this.inputEl = el), class: "co-number-field__input", type: "text", inputMode: "decimal",
447
+ } }, (this.label || this.secondaryLabel) && (h("div", { key: '22f0674035a68861fae6ce37aba4e51b10c03226', class: "co-number-field__labels" }, this.label && h("label", { key: '442ee74bed62bf7c800f231ad4992b791b85a30c', class: "co-number-field__label" }, this.label, h(RequiredMark, { key: 'ff7517b83b8e942512dcb8c1f504eafab55cea90', required: this.required })), this.secondaryLabel && h("span", { key: '736889aae85065fb28a56131e2f7a3da9fc86a48', class: "co-number-field__secondary-label" }, this.secondaryLabel))), h("div", { key: 'fd6163c7cb83073db2c19c3477447076372abb44', class: "co-number-field__row", part: "row" }, !this.hideControls && (h("button", { key: 'e1a52831b83c3255ce63c4ed48a228fe4fdb1629', class: "co-number-field__btn co-number-field__btn--minus", type: "button", tabIndex: -1, disabled: !this.canDecrement, onClick: this.handleDecrement, "aria-label": "Decrease" }, h("co-icon", { key: '86e0ce9b9801e50d9bda6b18a8e1f6f96581b178', name: "minus", size: 16 }))), h("input", { key: '4bdf89e9a4958841a948b3bd6e9886898917f464', ref: el => (this.inputEl = el), class: "co-number-field__input", type: "text", inputMode: "decimal",
440
448
  // Mismo guard `focused` que el @Watch: mientras el usuario edita, su
441
449
  // texto manda. Sin esto, el re-render que dispara emitValue repinta
442
450
  // el input desde el NÚMERO y se come el texto en construcción
443
451
  // ("34,640.00" pegado quedaba "34,640"; un "12." perdería el punto).
444
- value: this.focused && this.inputEl ? this.inputEl.value : this.displayValue, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput, onPaste: this.handlePaste, onFocus: this.handleFocus, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown, part: "input" }), this.affixText && (h("span", { key: '836ea4e4cba0b21a36b1017d6fc0c4d621d69e56', class: "co-number-field__affix", "aria-hidden": "true" }, this.affixText)), this.clearable && !this.isEmpty && !this.disabled && (h("button", { key: '5eefa937a055dea1855d1bc3bae675003d8382c8', class: "co-number-field__clear", type: "button", tabIndex: -1, onClick: this.handleClear, "aria-label": "Clear" }, h("co-icon", { key: '096dec8d88b7b241cd83a04788bc4db05feec585', name: "x-circle", size: 16, weight: "fill" }))), h("slot", { key: '1ea7d1ff2c61d74b0884e7b8f1b7b54de4978876', name: "suffix" }), !this.hideControls && (h("button", { key: '9ae95697534b85dcd928c0d0e7f72a3b8c5402c7', class: "co-number-field__btn co-number-field__btn--plus", type: "button", tabIndex: -1, disabled: !this.canIncrement, onClick: this.handleIncrement, "aria-label": "Increase" }, h("co-icon", { key: 'af526ac4278865052f32ae7359a571aa485492af', name: "plus", size: 16 })))), this.helperText && (h("div", { key: '9bad3ffd8c70ce49205132f9f73294a83b6ebbc3', class: `co-number-field__helper co-number-field__helper--${this.status}` }, this.statusIcon && h("co-icon", { key: '4987bc82011c4bb93e1bd00ef1ffd45d2e8681f4', name: this.statusIcon, size: 14, weight: "fill", class: "co-number-field__helper-icon" }), h("span", { key: '8eac5821653dc0110113f94833e13f27411188a7', class: "co-number-field__helper-text" }, this.helperText)))));
452
+ value: this.focused && this.inputEl ? this.inputEl.value : this.displayValue, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput, onPaste: this.handlePaste, onFocus: this.handleFocus, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown, part: "input" }), this.affixText && (h("span", { key: '7bc7c12c6fe2708691d9a2972f9ed3037e82a1a8', class: "co-number-field__affix", "aria-hidden": "true" }, this.affixText)), this.clearable && !this.isEmpty && !this.disabled && (h("button", { key: '2ef7c424a34a8a6cbfe8ac4cd0a5d8f0ed74e6d4', class: "co-number-field__clear", type: "button", tabIndex: -1, onClick: this.handleClear, "aria-label": "Clear" }, h("co-icon", { key: 'c428ca841c0de013cd151bd356686be43992de04', name: "x-circle", size: 16, weight: "fill" }))), h("slot", { key: 'fb127a663d3839bbb10b8232b250eaf67faab898', name: "suffix" }), !this.hideControls && (h("button", { key: '684329d2f32a58f6be2a124f0cd20986a7fc49af', class: "co-number-field__btn co-number-field__btn--plus", type: "button", tabIndex: -1, disabled: !this.canIncrement, onClick: this.handleIncrement, "aria-label": "Increase" }, h("co-icon", { key: '7ef48466e060efb314466d62216a8e00168ddbcd', name: "plus", size: 16 })))), this.helperText && (h("div", { key: '2cf6df2556160fef3c1dfd1b525991eb822cb285', class: `co-number-field__helper co-number-field__helper--${this.status}` }, this.statusIcon && h("co-icon", { key: '905ad795d9714fa9ae8fc2b9d4b9f6a7bba2d2ce', name: this.statusIcon, size: 14, weight: "fill", class: "co-number-field__helper-icon" }), h("span", { key: '0c4825c4145298700de006eb3170e9ec3b52d3b7', class: "co-number-field__helper-text" }, this.helperText)))));
445
453
  }
446
454
  get el() { return getElement(this); }
447
455
  static get watchers() { return {
@@ -383,10 +383,6 @@ var loadModule = (cmpMeta, hostRef, hmrVersionId) => {
383
383
  return import(
384
384
  /* webpackMode: "lazy" */
385
385
  './co-loader.entry.js').then(processMod, consoleError);
386
- case 'co-audio-player':
387
- return import(
388
- /* webpackMode: "lazy" */
389
- './co-audio-player.entry.js').then(processMod, consoleError);
390
386
  case 'co-bottom-sheet':
391
387
  return import(
392
388
  /* webpackMode: "lazy" */
@@ -455,6 +451,10 @@ var loadModule = (cmpMeta, hostRef, hmrVersionId) => {
455
451
  return import(
456
452
  /* webpackMode: "lazy" */
457
453
  './co-icon.entry.js').then(processMod, consoleError);
454
+ case 'co-audio-player':
455
+ return import(
456
+ /* webpackMode: "lazy" */
457
+ './co-audio-player.entry.js').then(processMod, consoleError);
458
458
  case 'co-color-picker':
459
459
  return import(
460
460
  /* webpackMode: "lazy" */
@@ -60,7 +60,8 @@ export declare class CoAudioPlayer {
60
60
  }>;
61
61
  private audio?;
62
62
  private waveEl?;
63
- private rawChannelData;
63
+ private peakEnvelope;
64
+ private exactPeaks;
64
65
  private decodedDuration;
65
66
  private loadToken;
66
67
  private resizeObserver?;
@@ -77,8 +78,14 @@ export declare class CoAudioPlayer {
77
78
  disconnectedCallback(): void;
78
79
  onSrcChange(): void;
79
80
  private rebuildBars;
81
+ private decodedBars;
80
82
  private init;
81
83
  private teardown;
84
+ /**
85
+ * Picos + duración del decode, vía el caché de módulo (`co-audio-player.peaks-cache`):
86
+ * dos instancias con la misma `src` comparten UNA descarga y UN decode, y lo que
87
+ * vuelve es la envolvente acotada, no el PCM.
88
+ */
82
89
  private extractPeaks;
83
90
  /** Re-derive the shown duration from whichever source knows it. Called from both
84
91
  * `loadedmetadata` and the end of the decode, because either can land first. */
@@ -0,0 +1,58 @@
1
+ import { type PeakEnvelope } from './co-audio-player.utils';
2
+ /**
3
+ * Caché de picos de `co-audio-player`, a nivel de MÓDULO (compartido por todas las
4
+ * instancias del documento).
5
+ *
6
+ * Por qué: la misma grabación suele estar montada dos veces (la tarjeta de la
7
+ * llamada y su detalle) y cada instancia descargaba y decodificaba el audio
8
+ * completo por su cuenta. Acá se guarda solo lo que la onda necesita —la
9
+ * envolvente acotada y la duración decodificada— y los pedidos simultáneos de la
10
+ * misma URL comparten una sola promesa.
11
+ *
12
+ * Fidelidad de la onda: mientras el PCM existe (dentro del decode) se calculan las
13
+ * barras EXACTAS —`resamplePeaks`, lo mismo que antes— para el ancho vigente de cada
14
+ * instancia que está esperando ese decode; esas se pintan idénticas a como se
15
+ * pintaban. Solo un ancho que aparece después (resize, u otra instancia con el caché
16
+ * ya caliente) sale de la envolvente (`resampleEnvelope`, alta resolución).
17
+ *
18
+ * Key = la URL TAL CUAL. No se normaliza: una URL firmada (S3 `?X-Amz-Signature=…`)
19
+ * que se renueva es otra entrada y vuelve a descargar. Es un límite aceptado: una
20
+ * normalización propia (quitar la query) podría confundir dos recursos distintos.
21
+ */
22
+ export interface AudioPeaks {
23
+ envelope: PeakEnvelope;
24
+ /** Barras exactas (del PCM) por cantidad de barras, para los anchos pedidos
25
+ * mientras el decode estaba en vuelo. Unas pocas entradas de cientos de valores. */
26
+ exact: ReadonlyMap<number, number[]>;
27
+ /** `AudioBuffer.duration` (s): la única duración fiable de un blob sin cabecera. */
28
+ duration: number;
29
+ }
30
+ /** Cantidad de barras que una instancia quiere AHORA; se consulta al terminar el
31
+ * decode (no al pedirlo: el ancho se mide después de `componentWillLoad`). Un valor
32
+ * no positivo = ya no le interesa (src cambiada, desmontada). */
33
+ export type BarsWanted = () => number;
34
+ /** `null` = el entorno no puede decodificar (sin WebAudio o sin fetch): no es un error.
35
+ * `wanted` es un conjunto VIVO: puede crecer mientras la carga está en vuelo. */
36
+ export type PeaksLoader = (url: string, wanted: ReadonlySet<BarsWanted>) => Promise<AudioPeaks | null>;
37
+ /** Tope de entradas. Cada una pesa ≤ 256 KB (`PEAK_ENVELOPE_BINS` Float32) más unas
38
+ * pocas listas de barras exactas, así que el caché lleno queda en ~4 MB. Una
39
+ * instancia montada conserva SU envolvente aunque la entrada se desaloje (es la
40
+ * misma referencia, no una copia). */
41
+ export declare const PEAKS_CACHE_MAX = 16;
42
+ /**
43
+ * Picos + duración de `url`: del caché si están, uniéndose a la carga en vuelo si
44
+ * otra instancia ya la pidió, o cargándolos. Solo se cachea un resultado real: un
45
+ * error (CORS, red, códec) o un `null` se devuelven tal cual y el próximo pedido
46
+ * reintenta.
47
+ */
48
+ export declare function loadAudioPeaks(url: string, wantBars?: BarsWanted, loader?: PeaksLoader): Promise<AudioPeaks | null>;
49
+ /** Vacía el caché (tests; y un host que necesite liberar memoria a mano). */
50
+ export declare function clearAudioPeaksCache(): void;
51
+ /**
52
+ * Descarga + decode con Web Audio, reducido a la envolvente. El `AudioBuffer`
53
+ * (y con él el PCM completo) no sale de esta función: queda sin referencias
54
+ * apenas se calcula la envolvente.
55
+ */
56
+ export declare function decodeAudioPeaks(url: string, wanted?: ReadonlySet<BarsWanted>): Promise<AudioPeaks | null>;
57
+ /** Barras exactas para cada ancho que las instancias en espera quieren ahora mismo. */
58
+ export declare function exactBarsFor(pcm: Float32Array | number[], wanted: ReadonlySet<BarsWanted>): Map<number, number[]>;
@@ -19,6 +19,39 @@ export declare function formatTime(seconds: number): string;
19
19
  export declare function resolveDuration(fromAudio: number, fromDecode: number): number;
20
20
  /** Downsample raw PCM to `bars` peak heights, each normalized to a 0..1 fraction. */
21
21
  export declare function resamplePeaks(channelData: Float32Array | number[], bars: number): number[];
22
+ /**
23
+ * Resolución de la envolvente que se retiene por grabación, en bloques.
24
+ *
25
+ * Las barras EXACTAS del ancho vigente se calculan del PCM antes de soltarlo (ver
26
+ * `co-audio-player.peaks-cache`); la envolvente solo sirve los anchos que aparecen
27
+ * DESPUÉS (resize, o una segunda instancia que llega con el caché ya caliente). El
28
+ * reproductor deriva las barras del ancho (`floor(ancho / barDensity)`, ~853 a
29
+ * 2560 px), así que la envolvente tiene que ir muy por encima de eso: 65 536 bloques
30
+ * = ≥ 76 bloques por barra hasta 2560 px, y pesan 256 KB (Float32) frente a ~32 MB
31
+ * del PCM de 3 min a 44,1 kHz. Medido contra el exacto con PCM sintético de 3 min:
32
+ * desvío máx. ≈ 1 px de 40 en las barras que difieren (≈ 0,8 px con ruido blanco).
33
+ */
34
+ export declare const PEAK_ENVELOPE_BINS = 65536;
35
+ /** Máximos absolutos por bloque de `blockSize` muestras contiguas: lo único que se
36
+ * retiene de un decode. `length` es la cantidad de muestras del PCM original, que
37
+ * `resampleEnvelope` necesita para reproducir el paso de `resamplePeaks`. */
38
+ export interface PeakEnvelope {
39
+ peaks: Float32Array;
40
+ blockSize: number;
41
+ length: number;
42
+ }
43
+ /** PCM → envolvente de a lo sumo `maxBins` bloques. Con `length ≤ maxBins` el bloque
44
+ * es de 1 muestra y la envolvente ES |PCM| (sin pérdida). */
45
+ export declare function buildPeakEnvelope(channelData: Float32Array | number[], maxBins?: number): PeakEnvelope;
46
+ /**
47
+ * Las mismas barras que `resamplePeaks(pcm, bars)`, calculadas desde la envolvente:
48
+ * mismo paso (`floor(length / bars)` muestras), mismo recorte de la cola y misma
49
+ * fórmula de altura. Cada barra toma el máximo de los bloques que TOCA su rango de
50
+ * muestras, así que el resultado es IDÉNTICO cuando `blockSize` es 1 o divide el
51
+ * paso, y en el caso general solo puede sumar el pico de un bloque parcial en cada
52
+ * borde (nunca queda por debajo del exacto).
53
+ */
54
+ export declare function resampleEnvelope(env: PeakEnvelope, bars: number): number[];
22
55
  /** Deterministic pseudo-waveform (stable render + testable — no Math.random). */
23
56
  export declare function fallbackPeaks(bars: number): number[];
24
57
  /** Clamped 0..1 position of a click within a track rect. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prolibu-suite/cobalt-core",
3
- "version": "0.11.3",
3
+ "version": "0.11.5",
4
4
  "description": "Cobalt Design System — Framework-agnostic Web Components built with Stencil",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.js",
@@ -50,10 +50,10 @@
50
50
  "@phosphor-icons/core": "^2.1.1",
51
51
  "dompurify": "^3.2.6",
52
52
  "overlayscrollbars": "^2.16.0",
53
- "@prolibu-suite/cobalt-calendar-utils": "0.1.1",
54
- "@prolibu-suite/cobalt-tokens": "0.6.0",
55
53
  "@prolibu-suite/cobalt-date-utils": "0.1.2",
56
- "@prolibu-suite/cobalt-rich-text": "0.2.1"
54
+ "@prolibu-suite/cobalt-tokens": "0.6.0",
55
+ "@prolibu-suite/cobalt-rich-text": "0.2.1",
56
+ "@prolibu-suite/cobalt-calendar-utils": "0.1.1"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@stencil/core": "^4.43.4",