spectraview 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,8 @@ Interactive React component for vibrational spectroscopy (IR, Raman, NIR).
6
6
  [![npm downloads](https://img.shields.io/npm/dm/spectraview)](https://www.npmjs.com/package/spectraview)
7
7
  [![bundle size](https://img.shields.io/bundlephobia/minzip/spectraview)](https://bundlephobia.com/package/spectraview)
8
8
  [![license](https://img.shields.io/npm/l/spectraview)](https://github.com/ktubhyam/spectraview/blob/main/LICENSE)
9
- [![CI](https://img.shields.io/github/actions/workflow/status/ktubhyam/spectraview/ci.yml)](https://github.com/ktubhyam/spectraview/actions)
9
+ [![CI](https://img.shields.io/github/actions/workflow/status/ktubhyam/spectraview/ci.yml?branch=main)](https://github.com/ktubhyam/spectraview/actions)
10
+ [![Storybook](https://img.shields.io/badge/storybook-deployed-ff4785)](https://ktubhyam.github.io/spectraview/)
10
11
 
11
12
  ## Features
12
13
 
package/dist/index.cjs CHANGED
@@ -1,7 +1,16 @@
1
1
  "use client";
2
- "use strict";var le=Object.defineProperty;var Be=Object.getOwnPropertyDescriptor;var He=Object.getOwnPropertyNames;var Je=Object.prototype.hasOwnProperty;var Ye=(e,t)=>{for(var o in t)le(e,o,{get:t[o],enumerable:!0})},We=(e,t,o,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of He(t))!Je.call(e,n)&&n!==o&&le(e,n,{get:()=>t[n],enumerable:!(r=Be(t,n))||r.enumerable});return e};var Ge=e=>We(le({},"__esModule",{value:!0}),e);var gt={};Ye(gt,{AxisLayer:()=>Y,Crosshair:()=>K,DARK_THEME:()=>pe,LIGHT_THEME:()=>ue,PeakMarkers:()=>W,RegionSelector:()=>G,SPECTRUM_COLORS:()=>X,SpectraView:()=>Re,SpectrumCanvas:()=>J,Toolbar:()=>q,computeXExtent:()=>Z,computeYExtent:()=>V,createXScale:()=>_,createYScale:()=>z,detectPeaks:()=>Q,getSpectrumColor:()=>j,getThemeColors:()=>B,parseCsv:()=>ee,parseCsvMulti:()=>Ie,parseJcamp:()=>oe,parseJson:()=>te,useExport:()=>$e,usePeakPicking:()=>Ae,useSpectrumData:()=>Ue,useZoomPan:()=>H});module.exports=Ge(gt);var x=require("react");var ce=require("d3-scale"),me=require("d3-array"),Ke=.05;function Z(e){let t=1/0,o=-1/0;for(let r of e){if(r.visible===!1)continue;let[n,i]=(0,me.extent)(r.x);n<t&&(t=n),i>o&&(o=i)}return isFinite(t)?[t,o]:[0,1]}function V(e){let t=1/0,o=-1/0;for(let i of e){if(i.visible===!1)continue;let[m,c]=(0,me.extent)(i.y);m<t&&(t=m),c>o&&(o=c)}if(!isFinite(t))return[0,1];let n=(o-t)*Ke;return[t-n,o+n]}function _(e,t,o,r){let n=t-o.left-o.right,i=r?[e[1],e[0]]:e;return(0,ce.scaleLinear)().domain(i).range([0,n])}function z(e,t,o){let r=t-o.top-o.bottom;return(0,ce.scaleLinear)().domain(e).range([r,0])}var X=["#2563eb","#dc2626","#16a34a","#9333ea","#ea580c","#0891b2","#be185d","#854d0e","#4f46e5","#65a30d"],ue={background:"#ffffff",axisColor:"#374151",gridColor:"#e5e7eb",tickColor:"#6b7280",labelColor:"#111827",crosshairColor:"#9ca3af",regionFill:"rgba(37, 99, 235, 0.1)",regionStroke:"rgba(37, 99, 235, 0.4)",tooltipBg:"#ffffff",tooltipBorder:"#d1d5db",tooltipText:"#111827"},pe={background:"#111827",axisColor:"#d1d5db",gridColor:"#374151",tickColor:"#9ca3af",labelColor:"#f9fafb",crosshairColor:"#6b7280",regionFill:"rgba(96, 165, 250, 0.15)",regionStroke:"rgba(96, 165, 250, 0.5)",tooltipBg:"#1f2937",tooltipBorder:"#4b5563",tooltipText:"#f9fafb"};function j(e){return X[e%X.length]}function B(e){return e==="dark"?pe:ue}var C=require("react"),M=require("d3-zoom"),T=require("d3-selection"),St=require("d3-transition"),ve=1.5;function H(e){let{plotWidth:t,plotHeight:o,xScale:r,yScale:n,scaleExtent:i=[1,50],enabled:m=!0,onViewChange:c}=e,s=(0,C.useRef)(null),u=(0,C.useRef)(null),a=(0,C.useRef)(c);a.current=c;let b=(0,C.useRef)(i);b.current=i;let[p,l]=(0,C.useState)(M.zoomIdentity),f=(0,C.useMemo)(()=>p.rescaleX(r.copy()),[p,r]),d=(0,C.useMemo)(()=>p.rescaleY(n.copy()),[p,n]);(0,C.useEffect)(()=>{let v=s.current;if(!v||!m)return;let E=(0,M.zoom)().scaleExtent(b.current).extent([[0,0],[t,o]]).translateExtent([[-1/0,-1/0],[1/0,1/0]]).on("zoom",ae=>{let D=ae.transform;if(l(D),a.current){let N=D.rescaleX(r.copy()),ie=D.rescaleY(n.copy());a.current(N.domain(),ie.domain())}});return u.current=E,(0,T.select)(v).call(E),(0,T.select)(v).on("dblclick.zoom",()=>{(0,T.select)(v).transition().duration(300).call(E.transform,M.zoomIdentity)}),()=>{(0,T.select)(v).on(".zoom",null)}},[t,o,m,r,n]);let g=(0,C.useCallback)(()=>{!s.current||!u.current||(0,T.select)(s.current).transition().duration(300).call(u.current.transform,M.zoomIdentity)},[]),y=(0,C.useCallback)(()=>{!s.current||!u.current||(0,T.select)(s.current).transition().duration(200).call(u.current.scaleBy,ve)},[]),w=(0,C.useCallback)(()=>{!s.current||!u.current||(0,T.select)(s.current).transition().duration(200).call(u.current.scaleBy,1/ve)},[]);return{zoomRef:s,state:{transform:p,isZoomed:p.k!==1||p.x!==0||p.y!==0},zoomedXScale:f,zoomedYScale:d,resetZoom:g,zoomIn:y,zoomOut:w}}var A=require("react");var qe=1.5,Qe=2.5;function et(e,t,o){e.clearRect(0,0,t,o)}function tt(e,t,o,r,n,i){let{highlighted:m=!1,opacity:c=1}=i??{},s=Math.min(t.x.length,t.y.length);if(s<2)return;let u=t.color??j(o),a=m?Qe:qe,[b,p]=r.domain(),l=Math.min(b,p),f=Math.max(b,p);e.save(),e.beginPath(),e.strokeStyle=u,e.lineWidth=a,e.globalAlpha=c,e.lineJoin="round";let d=!1;for(let g=0;g<s;g++){let y=t.x[g];if(y<l&&g<s-1&&t.x[g+1]<l||y>f&&g>0&&t.x[g-1]>f)continue;let w=r(y),v=n(t.y[g]);d?e.lineTo(w,v):(e.moveTo(w,v),d=!0)}e.stroke(),e.restore()}function ke(e,t,o,r,n,i,m){et(e,n,i),t.forEach((c,s)=>{c.visible!==!1&&tt(e,c,s,o,r,{highlighted:c.id===m,opacity:m&&c.id!==m?.3:1})})}var we=require("react/jsx-runtime");function J({spectra:e,xScale:t,yScale:o,width:r,height:n,highlightedId:i}){let m=(0,A.useRef)(null),c=(0,A.useRef)(1);return(0,A.useEffect)(()=>{let s=m.current;if(!s)return;let u=window.devicePixelRatio||1;c.current=u,s.width=r*u,s.height=n*u},[r,n]),(0,A.useEffect)(()=>{let s=m.current;if(!s)return;let u=s.getContext("2d");if(!u)return;let a=c.current;u.setTransform(a,0,0,a,0,0),ke(u,e,t,o,r,n,i)},[e,t,o,r,n,i]),(0,we.jsx)("canvas",{ref:m,style:{width:r,height:n,position:"absolute",top:0,left:0,pointerEvents:"none"}})}var S=require("react/jsx-runtime");function Te(e,t){let[o,r]=e.domain(),n=Math.min(o,r),m=(Math.max(o,r)-n)/(t-1);return Array.from({length:t},(c,s)=>n+s*m)}function Pe(e){return Math.abs(e)>=1e3?Math.round(e).toString():Math.abs(e)>=1?e.toFixed(1):Math.abs(e)>=.01?e.toFixed(3):e.toExponential(1)}function Y({xScale:e,yScale:t,width:o,height:r,xLabel:n,yLabel:i,showGrid:m=!0,colors:c}){let s=Te(e,8),u=Te(t,6);return(0,S.jsxs)("g",{children:[m&&(0,S.jsxs)("g",{children:[s.map(a=>(0,S.jsx)("line",{x1:e(a),x2:e(a),y1:0,y2:r,stroke:c.gridColor,strokeWidth:.5},`xgrid-${a}`)),u.map(a=>(0,S.jsx)("line",{x1:0,x2:o,y1:t(a),y2:t(a),stroke:c.gridColor,strokeWidth:.5},`ygrid-${a}`))]}),(0,S.jsxs)("g",{transform:`translate(0, ${r})`,children:[(0,S.jsx)("line",{x1:0,x2:o,y1:0,y2:0,stroke:c.axisColor}),s.map(a=>(0,S.jsxs)("g",{transform:`translate(${e(a)}, 0)`,children:[(0,S.jsx)("line",{y1:0,y2:6,stroke:c.axisColor}),(0,S.jsx)("text",{y:20,textAnchor:"middle",fill:c.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:Pe(a)})]},`xtick-${a}`)),n&&(0,S.jsx)("text",{x:o/2,y:42,textAnchor:"middle",fill:c.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:n})]}),(0,S.jsxs)("g",{children:[(0,S.jsx)("line",{x1:0,x2:0,y1:0,y2:r,stroke:c.axisColor}),u.map(a=>(0,S.jsxs)("g",{transform:`translate(0, ${t(a)})`,children:[(0,S.jsx)("line",{x1:-6,x2:0,stroke:c.axisColor}),(0,S.jsx)("text",{x:-10,textAnchor:"end",dominantBaseline:"middle",fill:c.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:Pe(a)})]},`ytick-${a}`)),i&&(0,S.jsx)("text",{transform:`translate(-50, ${r/2}) rotate(-90)`,textAnchor:"middle",fill:c.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:i})]})]})}var L=require("react/jsx-runtime");function W({peaks:e,xScale:t,yScale:o,colors:r,onPeakClick:n}){let[i,m]=t.domain(),c=Math.min(i,m),s=Math.max(i,m),u=e.filter(a=>a.x>=c&&a.x<=s);return(0,L.jsx)("g",{className:"spectraview-peaks",children:u.map((a,b)=>{let p=t(a.x),l=o(a.y);return(0,L.jsxs)("g",{transform:`translate(${p}, ${l})`,style:{cursor:n?"pointer":"default"},onClick:()=>n?.(a),children:[(0,L.jsx)("polygon",{points:`0,-5 -5,${-5*2.5} 5,${-5*2.5}`,fill:r.labelColor,opacity:.8}),a.label&&(0,L.jsx)("text",{y:-5*2.5-14,textAnchor:"middle",fill:r.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",fontWeight:500,children:a.label})]},`peak-${a.x}-${b}`)})})}var I=require("react/jsx-runtime");function G({regions:e,xScale:t,height:o,colors:r}){return(0,I.jsx)("g",{className:"spectraview-regions",children:e.map((n,i)=>{let m=t(n.xStart),c=t(n.xEnd),s=Math.min(m,c),u=Math.abs(c-m);return(0,I.jsxs)("g",{children:[(0,I.jsx)("rect",{x:s,y:0,width:u,height:o,fill:n.color??r.regionFill,stroke:r.regionStroke,strokeWidth:1}),n.label&&(0,I.jsx)("text",{x:s+u/2,y:12,textAnchor:"middle",fill:r.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",children:n.label})]},`region-${i}`)})})}var P=require("react/jsx-runtime");function K({position:e,width:t,height:o,colors:r}){return e?(0,P.jsxs)("g",{className:"spectraview-crosshair",pointerEvents:"none",children:[(0,P.jsx)("line",{x1:e.px,x2:e.px,y1:0,y2:o,stroke:r.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),(0,P.jsx)("line",{x1:0,x2:t,y1:e.py,y2:e.py,stroke:r.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),(0,P.jsxs)("g",{transform:`translate(${Math.min(e.px+10,t-100)}, ${Math.max(e.py-10,20)})`,children:[(0,P.jsx)("rect",{x:0,y:-14,width:90,height:18,rx:3,fill:r.tooltipBg,stroke:r.tooltipBorder,strokeWidth:.5,opacity:.9}),(0,P.jsxs)("text",{x:5,y:0,fill:r.tooltipText,fontSize:10,fontFamily:"monospace",children:[Ee(e.dataX),", ",Ee(e.dataY)]})]})]}):null}function Ee(e){return Math.abs(e)>=100?Math.round(e).toString():Math.abs(e)>=1?e.toFixed(1):e.toFixed(4)}var F=require("react/jsx-runtime"),fe=e=>({display:"inline-flex",alignItems:"center",justifyContent:"center",width:28,height:28,border:`1px solid ${e==="dark"?"#4b5563":"#d1d5db"}`,borderRadius:4,background:e==="dark"?"#1f2937":"#ffffff",color:e==="dark"?"#d1d5db":"#374151",fontSize:14,cursor:"pointer",padding:0,lineHeight:1}),rt=e=>({display:"flex",gap:4,padding:"4px 0",borderBottom:`1px solid ${e==="dark"?"#374151":"#e5e7eb"}`});function q({onZoomIn:e,onZoomOut:t,onReset:o,isZoomed:r,theme:n}){return(0,F.jsxs)("div",{style:rt(n),className:"spectraview-toolbar",children:[(0,F.jsx)("button",{type:"button",style:fe(n),onClick:e,title:"Zoom in","aria-label":"Zoom in",children:"+"}),(0,F.jsx)("button",{type:"button",style:fe(n),onClick:t,title:"Zoom out","aria-label":"Zoom out",children:"\u2212"}),(0,F.jsx)("button",{type:"button",style:{...fe(n),opacity:r?1:.4},onClick:o,disabled:!r,title:"Reset zoom","aria-label":"Reset zoom",children:"\u21BA"})]})}var h=require("react/jsx-runtime"),ot={top:20,right:20,bottom:50,left:65},nt=800,st=400;function at(e){return{width:e.width??nt,height:e.height??st,reverseX:e.reverseX??!1,showGrid:e.showGrid??!0,showCrosshair:e.showCrosshair??!0,showToolbar:e.showToolbar??!0,displayMode:e.displayMode??"overlay",margin:{...ot,...e.margin},theme:e.theme??"light"}}function it(e,t,o){let r=e[0];return{xLabel:t??r?.xUnit??"x",yLabel:o??r?.yUnit??"y"}}function Re(e){let{spectra:t,peaks:o=[],regions:r=[],onPeakClick:n,onViewChange:i,onCrosshairMove:m}=e,s=`spectraview-clip-${(0,x.useId)().replace(/:/g,"")}`,u=(0,x.useMemo)(()=>at(e),[e.width,e.height,e.reverseX,e.showGrid,e.showCrosshair,e.showToolbar,e.displayMode,e.margin,e.theme]),{width:a,height:b,margin:p,reverseX:l,theme:f}=u,d=a-p.left-p.right,g=b-p.top-p.bottom,y=(0,x.useMemo)(()=>B(f),[f]),w=(0,x.useMemo)(()=>it(t,e.xLabel,e.yLabel),[t,e.xLabel,e.yLabel]),v=(0,x.useMemo)(()=>Z(t),[t]),E=(0,x.useMemo)(()=>V(t),[t]),ae=(0,x.useMemo)(()=>_(v,a,p,l),[v,a,p,l]),D=(0,x.useMemo)(()=>z(E,b,p),[E,b,p]),N=(0,x.useRef)(i);N.current=i;let ie=(0,x.useMemo)(()=>($,O)=>{N.current?.({xDomain:$,yDomain:O})},[]),{zoomRef:Ne,state:Oe,zoomedXScale:R,zoomedYScale:U,resetZoom:Ze,zoomIn:Ve,zoomOut:_e}=H({plotWidth:d,plotHeight:g,xScale:ae,yScale:D,onViewChange:i?ie:void 0}),[ze,be]=(0,x.useState)(null),ge=(0,x.useRef)(m);ge.current=m;let Xe=(0,x.useCallback)($=>{if(!u.showCrosshair)return;let O=$.currentTarget.getBoundingClientRect(),xe=$.clientX-O.left,ye=$.clientY-O.top,Se=R.invert(xe),Ce=U.invert(ye);be({px:xe,py:ye,dataX:Se,dataY:Ce}),ge.current?.(Se,Ce)},[R,U,u.showCrosshair]),je=(0,x.useCallback)(()=>{be(null)},[]);if(t.length===0)return(0,h.jsx)("div",{style:{width:a,height:b,display:"flex",alignItems:"center",justifyContent:"center",border:`1px dashed ${y.gridColor}`,borderRadius:8,color:y.tickColor,fontFamily:"system-ui, sans-serif",fontSize:14},className:e.className,children:"No spectra loaded"});let he=u.showToolbar?37:0;return(0,h.jsxs)("div",{style:{width:a,background:y.background,borderRadius:4,overflow:"hidden"},className:e.className,children:[u.showToolbar&&(0,h.jsx)(q,{onZoomIn:Ve,onZoomOut:_e,onReset:Ze,isZoomed:Oe.isZoomed,theme:f}),(0,h.jsxs)("div",{style:{position:"relative",width:a,height:b-he},children:[(0,h.jsx)("div",{style:{position:"absolute",top:p.top,left:p.left,width:d,height:g,overflow:"hidden"},children:(0,h.jsx)(J,{spectra:t,xScale:R,yScale:U,width:d,height:g})}),(0,h.jsx)("svg",{width:a,height:b-he,style:{position:"absolute",top:0,left:0},children:(0,h.jsxs)("g",{transform:`translate(${p.left}, ${p.top})`,children:[(0,h.jsx)(Y,{xScale:R,yScale:U,width:d,height:g,xLabel:w.xLabel,yLabel:w.yLabel,showGrid:u.showGrid,colors:y}),(0,h.jsx)("defs",{children:(0,h.jsx)("clipPath",{id:s,children:(0,h.jsx)("rect",{x:0,y:0,width:d,height:g})})}),(0,h.jsxs)("g",{clipPath:`url(#${s})`,children:[r.length>0&&(0,h.jsx)(G,{regions:r,xScale:R,height:g,colors:y}),o.length>0&&(0,h.jsx)(W,{peaks:o,xScale:R,yScale:U,colors:y,onPeakClick:n})]}),u.showCrosshair&&(0,h.jsx)(K,{position:ze,width:d,height:g,colors:y}),(0,h.jsx)("rect",{ref:Ne,x:0,y:0,width:d,height:g,fill:"transparent",style:{cursor:u.showCrosshair?"crosshair":"grab"},onMouseMove:Xe,onMouseLeave:je})]})})]})]})}var Me=require("react");function Q(e,t,o={}){let{prominence:r=.01,minDistance:n=5,maxPeaks:i}=o;if(e.length<3||t.length<3)return[];let m=1/0,c=-1/0;for(let l=0;l<t.length;l++)t[l]<m&&(m=t[l]),t[l]>c&&(c=t[l]);let s=c-m;if(s===0)return[];let u=r*s,a=[];for(let l=1;l<t.length-1;l++)if(t[l]>t[l-1]&&t[l]>t[l+1]){let f=lt(t,l),d=ct(t,l),g=t[l]-Math.max(f,d);g>=u&&a.push({index:l,prom:g})}a.sort((l,f)=>f.prom-l.prom);let b=[];for(let l of a)b.some(d=>Math.abs(d.index-l.index)<n)||b.push(l);return(i?b.slice(0,i):b).map(l=>({x:e[l.index],y:t[l.index],label:mt(e[l.index])})).sort((l,f)=>l.x-f.x)}function lt(e,t){let o=e[t];for(let r=t-1;r>=0&&!(e[r]>e[t]);r--)e[r]<o&&(o=e[r]);return o}function ct(e,t){let o=e[t];for(let r=t+1;r<e.length&&!(e[r]>e[t]);r++)e[r]<o&&(o=e[r]);return o}function mt(e){return Math.round(e).toString()}function Ae(e,t={}){let{enabled:o=!0,spectrumIds:r,prominence:n,minDistance:i,maxPeaks:m}=t;return(0,Me.useMemo)(()=>{if(!o)return[];let c=r?e.filter(u=>r.includes(u.id)):e,s=[];for(let u of c){if(u.visible===!1)continue;let a=Q(u.x,u.y,{prominence:n,minDistance:i,maxPeaks:m});for(let b of a)s.push({...b,spectrumId:u.id})}return s},[e,o,r,n,i,m])}var k=require("react");var ut=[" ",",",";"," "];function Le(e){let t=e.trim().split(/\r?\n/).slice(0,5),o=",",r=0;for(let n of ut){let i=t.map(c=>c.split(n).length-1),m=Math.min(...i);m>0&&m>=r&&(i.every(s=>s===i[0])||m>r)&&(r=m,o=n)}return o}function ee(e,t={}){let{xColumn:o=0,yColumn:r=1,hasHeader:n=!0,label:i="CSV Spectrum"}=t,m=t.delimiter??Le(e),c=e.trim().split(/\r?\n/);if(c.length<2)throw new Error("CSV file must contain at least 2 lines");let s=i,u=0;if(n){let p=c[0].split(m).map(l=>l.trim());!t.label&&p[r]&&(s=p[r]),u=1}let a=[],b=[];for(let p=u;p<c.length;p++){let l=c[p].trim();if(l===""||l.startsWith("#"))continue;let f=l.split(m),d=parseFloat(f[o]),g=parseFloat(f[r]);!isNaN(d)&&!isNaN(g)&&(a.push(d),b.push(g))}if(a.length===0)throw new Error("No valid numeric data found in CSV");return{id:`csv-${Date.now()}`,label:s,x:new Float64Array(a),y:new Float64Array(b)}}function Ie(e,t={}){let{hasHeader:o=!0,label:r}=t,n=t.delimiter??Le(e),i=e.trim().split(/\r?\n/);if(i.length<2)throw new Error("CSV file must contain at least 2 lines");let c=i[o?1:0].split(n).length;if(c<2)throw new Error("CSV must have at least 2 columns (x + y)");let s,u=0;o&&(s=i[0].split(n).map(l=>l.trim()),u=1);let a=[],b=Array.from({length:c-1},()=>[]);for(let l=u;l<i.length;l++){let f=i[l].trim();if(f===""||f.startsWith("#"))continue;let d=f.split(n),g=parseFloat(d[0]);if(!isNaN(g)){a.push(g);for(let y=1;y<c;y++){let w=parseFloat(d[y]);b[y-1].push(isNaN(w)?0:w)}}}let p=new Float64Array(a);return b.map((l,f)=>({id:`csv-${Date.now()}-${f}`,label:r??s?.[f+1]??`Spectrum ${f+1}`,x:p,y:new Float64Array(l)}))}function te(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Invalid JSON: failed to parse input")}if(Array.isArray(t))return t.map((o,r)=>de(o,r));if(typeof t=="object"&&t!==null){let o=t;return Array.isArray(o.spectra)?o.spectra.map((r,n)=>de(r,n)):[de(t,0)]}throw new Error("Invalid JSON structure: expected an object or array")}function de(e,t){let o=e.x??e.wavenumbers??e.wavelengths;if(!o||!Array.isArray(o))throw new Error(`Spectrum ${t}: missing x-axis data (expected "x", "wavenumbers", or "wavelengths")`);let r=e.y??e.intensities??e.absorbance;if(!r||!Array.isArray(r))throw new Error(`Spectrum ${t}: missing y-axis data (expected "y", "intensities", or "absorbance")`);if(o.length!==r.length)throw new Error(`Spectrum ${t}: x and y arrays must have the same length (got ${o.length} and ${r.length})`);let n=e.label??e.title??e.name??`Spectrum ${t+1}`;return{id:`json-${Date.now()}-${t}`,label:n,x:new Float64Array(o),y:new Float64Array(r),xUnit:e.xUnit,yUnit:e.yUnit,type:e.type,meta:e.meta}}var re=null,Fe=!1;async function pt(){if(Fe)return re;Fe=!0;try{re=await import("jcampconverter")}catch{re=null}return re}function De(e){let t=(e["DATA TYPE"]??e.DATATYPE??"").toLowerCase();return t.includes("infrared")||t.includes("ir")?"IR":t.includes("raman")?"Raman":t.includes("nir")||t.includes("near")?"NIR":t.includes("uv")||t.includes("vis")?"UV-Vis":t.includes("fluor")?"fluorescence":"other"}async function oe(e){let t=await pt();return t?ft(e,t):[dt(e)]}function ft(e,t){return t.convert(e,{keepRecordsRegExp:/.*/}).flatten.map((r,n)=>{let i=r.spectra?.[0]?.data?.[0];if(!i)throw new Error(`JCAMP block ${n}: no spectral data found`);return{id:`jcamp-${Date.now()}-${n}`,label:r.info?.TITLE??`Spectrum ${n+1}`,x:new Float64Array(i.x),y:new Float64Array(i.y),xUnit:r.info?.XUNITS??"cm\u207B\xB9",yUnit:r.info?.YUNITS??"Absorbance",type:De(r.info),meta:r.info}})}function dt(e){let t=e.split(/\r?\n/),o={},r=[],n=[],i=!1;for(let m of t){let c=m.trim();if(c.startsWith("##")){let s=c.match(/^##(.+?)=\s*(.*)$/);if(s){let u=s[1].trim().toUpperCase(),a=s[2].trim();if(u==="XYDATA"||u==="XYPOINTS"){i=!0;continue}if(u==="END"){i=!1;continue}o[u]=a}continue}if(i&&c!==""){let s=c.split(/[\s,]+/).map(Number);if(s.length>=2&&!s.some(isNaN)){let u=s[0],a=parseFloat(o.FIRSTX??"0"),b=parseFloat(o.LASTX??"0"),p=parseInt(o.NPOINTS??"0",10);if(p>0&&s.length===2)r.push(s[0]),n.push(s[1]);else if(s.length>1){let l=p>1?(b-a)/(p-1):0;for(let f=1;f<s.length;f++)r.push(u+(f-1)*l),n.push(s[f])}}}}if(r.length===0)throw new Error("Failed to parse JCAMP-DX: no data found. Install jcampconverter for full format support.");return{id:`jcamp-${Date.now()}`,label:o.TITLE??"JCAMP Spectrum",x:new Float64Array(r),y:new Float64Array(n),xUnit:o.XUNITS??"cm\u207B\xB9",yUnit:o.YUNITS??"Absorbance",type:De(o),meta:o}}function bt(e){switch(e.toLowerCase().split(".").pop()){case"dx":case"jdx":case"jcamp":return"jcamp";case"csv":case"tsv":case"txt":return"csv";case"json":return"json";default:return null}}function Ue(e=[]){let[t,o]=(0,k.useState)(e),[r,n]=(0,k.useState)(!1),[i,m]=(0,k.useState)(null),c=(0,k.useCallback)(async(l,f)=>{n(!0),m(null);try{let d;switch(f){case"jcamp":d=await oe(l);break;case"csv":d=[ee(l)];break;case"json":d=te(l);break}o(g=>[...g,...d])}catch(d){let g=d instanceof Error?d.message:"Failed to parse file";m(g)}finally{n(!1)}},[]),s=(0,k.useCallback)(async l=>{let f=bt(l.name);if(!f){m(`Unsupported file format: ${l.name}`);return}let d=await l.text();await c(d,f)},[c]),u=(0,k.useCallback)(l=>{o(f=>[...f,l])},[]),a=(0,k.useCallback)(l=>{o(f=>f.filter(d=>d.id!==l))},[]),b=(0,k.useCallback)(l=>{o(f=>f.map(d=>d.id===l?{...d,visible:d.visible===!1}:d))},[]),p=(0,k.useCallback)(()=>{o([]),m(null)},[]);return{spectra:t,loading:r,error:i,loadFile:s,loadText:c,addSpectrum:u,removeSpectrum:a,toggleVisibility:b,clear:p}}var se=require("react");function ne(e,t){let o=URL.createObjectURL(e),r=document.createElement("a");r.href=o,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(o)}function $e(){let e=(0,se.useCallback)((r,n="spectrum.png")=>{r.toBlob(i=>{i&&ne(i,n)},"image/png")},[]),t=(0,se.useCallback)((r,n="spectra.csv")=>{let i=r.filter(m=>m.visible!==!1);if(i.length!==0)if(i.length===1){let m=i[0],c=`${m.xUnit??"x"},${m.yUnit??"y"}
3
- `,s=Array.from(m.x).map((a,b)=>`${a},${m.y[b]}`),u=c+s.join(`
4
- `);ne(new Blob([u],{type:"text/csv"}),n)}else{let m=Math.max(...i.map(a=>a.x.length)),c=i.map(a=>`${a.label}_x,${a.label}_y`).join(","),s=[];for(let a=0;a<m;a++){let b=i.map(p=>a<p.x.length?`${p.x[a]},${p.y[a]}`:",");s.push(b.join(","))}let u=c+`
5
- `+s.join(`
6
- `);ne(new Blob([u],{type:"text/csv"}),n)}},[]),o=(0,se.useCallback)((r,n="spectra.json")=>{let m=r.filter(s=>s.visible!==!1).map(s=>({label:s.label,x:Array.from(s.x),y:Array.from(s.y),xUnit:s.xUnit,yUnit:s.yUnit,type:s.type})),c=JSON.stringify(m,null,2);ne(new Blob([c],{type:"application/json"}),n)},[]);return{exportPng:e,exportCsv:t,exportJson:o}}0&&(module.exports={AxisLayer,Crosshair,DARK_THEME,LIGHT_THEME,PeakMarkers,RegionSelector,SPECTRUM_COLORS,SpectraView,SpectrumCanvas,Toolbar,computeXExtent,computeYExtent,createXScale,createYScale,detectPeaks,getSpectrumColor,getThemeColors,parseCsv,parseCsvMulti,parseJcamp,parseJson,useExport,usePeakPicking,useSpectrumData,useZoomPan});
2
+ "use strict";var We=Object.defineProperty;var jt=Object.getOwnPropertyDescriptor;var Kt=Object.getOwnPropertyNames;var Gt=Object.prototype.hasOwnProperty;var Jt=(e,t)=>{for(var r in t)We(e,r,{get:t[r],enumerable:!0})},qt=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Kt(t))!Gt.call(e,o)&&o!==r&&We(e,o,{get:()=>t[o],enumerable:!(n=jt(t,o))||n.enumerable});return e};var Qt=e=>qt(We({},"__esModule",{value:!0}),e);var Ur={};Jt(Ur,{AnnotationLayer:()=>Se,AxisLayer:()=>te,Crosshair:()=>xe,DARK_THEME:()=>Ge,DropZone:()=>we,ExportMenu:()=>Ft,KEYBOARD_SHORTCUTS:()=>wt,LIGHT_THEME:()=>Ke,LINE_DASH_PATTERNS:()=>et,Legend:()=>se,PeakMarkers:()=>he,RegionSelector:()=>ye,SPECTRUM_COLORS:()=>de,SpectraView:()=>kt,SpectrumCanvas:()=>ee,StackedView:()=>Re,Toolbar:()=>Ce,binarySearchClosest:()=>Je,computeXExtent:()=>pe,computeYExtent:()=>G,createXScale:()=>fe,createYScale:()=>J,detectPeaks:()=>Pe,downloadSvg:()=>$e,generateChartDescription:()=>Le,generateSvg:()=>Fe,getSpectrumColor:()=>U,getThemeColors:()=>q,lttbDownsample:()=>ge,parseCsv:()=>Ae,parseCsvMulti:()=>Lt,parseJcamp:()=>Ue,parseJson:()=>De,parseSpc:()=>$t,prefersReducedMotion:()=>Ct,snapToNearestSpectrum:()=>ve,useExport:()=>Ut,useKeyboardNavigation:()=>Me,usePeakPicking:()=>Tt,useRegionSelect:()=>Te,useResizeObserver:()=>Ee,useSpectrumData:()=>It,useZoomPan:()=>be});module.exports=Qt(Ur);var E=require("react");var Ye=require("d3-scale"),je=require("d3-array"),er=.05;function pe(e){let t=1/0,r=-1/0;for(let n of e){if(n.visible===!1)continue;let[o,i]=(0,je.extent)(n.x);o<t&&(t=o),i>r&&(r=i)}return isFinite(t)?[t,r]:[0,1]}function G(e){let t=1/0,r=-1/0;for(let i of e){if(i.visible===!1)continue;let[l,s]=(0,je.extent)(i.y);l<t&&(t=l),s>r&&(r=s)}if(!isFinite(t))return[0,1];let o=(r-t)*er;return[t-o,r+o]}function fe(e,t,r,n){let o=t-r.left-r.right,i=n?[e[1],e[0]]:e;return(0,Ye.scaleLinear)().domain(i).range([0,o])}function J(e,t,r){let n=t-r.top-r.bottom;return(0,Ye.scaleLinear)().domain(e).range([n,0])}var de=["#2563eb","#dc2626","#16a34a","#9333ea","#ea580c","#0891b2","#be185d","#854d0e","#4f46e5","#65a30d"],Ke={background:"#ffffff",axisColor:"#374151",gridColor:"#e5e7eb",tickColor:"#6b7280",labelColor:"#111827",crosshairColor:"#9ca3af",regionFill:"rgba(37, 99, 235, 0.1)",regionStroke:"rgba(37, 99, 235, 0.4)",tooltipBg:"#ffffff",tooltipBorder:"#d1d5db",tooltipText:"#111827"},Ge={background:"#111827",axisColor:"#d1d5db",gridColor:"#374151",tickColor:"#9ca3af",labelColor:"#f9fafb",crosshairColor:"#6b7280",regionFill:"rgba(96, 165, 250, 0.15)",regionStroke:"rgba(96, 165, 250, 0.5)",tooltipBg:"#1f2937",tooltipBorder:"#4b5563",tooltipText:"#f9fafb"};function U(e){return de[e%de.length]}function q(e){return e==="dark"?Ge:Ke}var P=require("react"),Q=require("d3-zoom"),X=require("d3-selection"),Nr=require("d3-transition"),ut=1.5;function be(e){let{plotWidth:t,plotHeight:r,xScale:n,yScale:o,scaleExtent:i=[1,50],enabled:l=!0,onViewChange:s}=e,c=(0,P.useRef)(null),m=(0,P.useRef)(null),u=(0,P.useRef)(s);u.current=s;let p=(0,P.useRef)(i);p.current=i;let[f,a]=(0,P.useState)(Q.zoomIdentity),d=(0,P.useMemo)(()=>f.rescaleX(n.copy()),[f,n]),g=(0,P.useMemo)(()=>f.rescaleY(o.copy()),[f,o]);(0,P.useEffect)(()=>{let v=c.current;if(!v||!l)return;let k=(0,Q.zoom)().scaleExtent(p.current).extent([[0,0],[t,r]]).translateExtent([[-1/0,-1/0],[1/0,1/0]]).on("zoom",h=>{let y=h.transform;if(a(y),u.current){let w=y.rescaleX(n.copy()),x=y.rescaleY(o.copy());u.current(w.domain(),x.domain())}});return m.current=k,(0,X.select)(v).call(k),(0,X.select)(v).on("dblclick.zoom",()=>{(0,X.select)(v).transition().duration(300).call(k.transform,Q.zoomIdentity)}),()=>{(0,X.select)(v).on(".zoom",null)}},[t,r,l,n,o]);let S=(0,P.useCallback)(()=>{!c.current||!m.current||(0,X.select)(c.current).transition().duration(300).call(m.current.transform,Q.zoomIdentity)},[]),b=(0,P.useCallback)(()=>{!c.current||!m.current||(0,X.select)(c.current).transition().duration(200).call(m.current.scaleBy,ut)},[]),C=(0,P.useCallback)(()=>{!c.current||!m.current||(0,X.select)(c.current).transition().duration(200).call(m.current.scaleBy,1/ut)},[]);return{zoomRef:c,state:{transform:f,isZoomed:f.k!==1||f.x!==0||f.y!==0},zoomedXScale:d,zoomedYScale:g,resetZoom:S,zoomIn:b,zoomOut:C}}var $=require("react");function ge(e,t,r,n,o,i,l){let s=n-r;if(s<=l){let f=[];for(let a=r;a<n;a++)f.push({px:o(e[a]),py:i(t[a]),index:a});return f}let c=[];c.push({px:o(e[r]),py:i(t[r]),index:r});let m=l-2,u=(s-2)/m,p=r;for(let f=0;f<m;f++){let a=r+1+Math.floor(f*u),d=r+1+Math.min(Math.floor((f+1)*u),s-2),g=d,S=r+1+Math.min(Math.floor((f+2)*u),s-2),b,C;if(f===m-1)b=o(e[n-1]),C=i(t[n-1]);else{b=0,C=0;let w=S-g;for(let x=g;x<S;x++)b+=o(e[x]),C+=i(t[x]);w>0&&(b/=w,C/=w)}let v=o(e[p]),k=i(t[p]),h=-1,y=a;for(let w=a;w<d;w++){let x=o(e[w]),T=i(t[w]),L=Math.abs((v-b)*(T-k)-(v-x)*(C-k));L>h&&(h=L,y=w)}c.push({px:o(e[y]),py:i(t[y]),index:y}),p=y}return c.push({px:o(e[n-1]),py:i(t[n-1]),index:n-1}),c}var tr=1.5,rr={solid:[],dashed:[8,4],dotted:[2,2],"dash-dot":[8,4,2,4]},or=2e3;function nr(e,t,r){e.clearRect(0,0,t,r)}function ir(e,t,r,n,o,i,l){let{highlighted:s=!1,opacity:c=1}=l??{},m=Math.min(t.x.length,t.y.length);if(m<2)return;let u=t.color??U(r),p=t.lineWidth??tr,f=s?p+1:p,a=rr[t.lineStyle??"solid"]??[],[d,g]=n.domain(),S=Math.min(d,g),b=Math.max(d,g),C=0,v=m;for(let h=0;h<m;h++)if(t.x[h]>=S||h<m-1&&t.x[h+1]>=S){C=Math.max(0,h-1);break}for(let h=m-1;h>=0;h--)if(t.x[h]<=b||h>0&&t.x[h-1]<=b){v=Math.min(m,h+2);break}let k=v-C;if(e.save(),e.beginPath(),e.strokeStyle=u,e.lineWidth=f,e.globalAlpha=c,e.lineJoin="round",e.setLineDash(a),k>or){let h=Math.max(Math.ceil(i*2),200),y=ge(t.x,t.y,C,v,n,o,h);if(y.length>0){e.moveTo(y[0].px,y[0].py);for(let w=1;w<y.length;w++)e.lineTo(y[w].px,y[w].py)}}else{let h=!1;for(let y=C;y<v;y++){let w=n(t.x[y]),x=o(t.y[y]);h?e.lineTo(w,x):(e.moveTo(w,x),h=!0)}}e.stroke(),e.restore()}function pt(e,t,r,n,o,i,l){nr(e,o,i),t.forEach((s,c)=>{s.visible!==!1&&ir(e,s,c,r,n,o,{highlighted:s.id===l,opacity:l&&s.id!==l?.3:1})})}var ft=require("react/jsx-runtime"),ee=(0,$.forwardRef)(function({spectra:t,xScale:r,yScale:n,width:o,height:i,highlightedId:l},s){let c=(0,$.useRef)(null),m=(0,$.useRef)(1);return(0,$.useImperativeHandle)(s,()=>c.current,[]),(0,$.useEffect)(()=>{let u=c.current;if(!u)return;let p=window.devicePixelRatio||1;m.current=p,u.width=o*p,u.height=i*p},[o,i]),(0,$.useEffect)(()=>{let u=c.current;if(!u)return;let p=u.getContext("2d");if(!p)return;let f=m.current;p.setTransform(f,0,0,f,0,0),pt(p,t,r,n,o,i,l)},[t,r,n,o,i,l]),(0,ft.jsx)("canvas",{ref:c,style:{width:o,height:i,position:"absolute",top:0,left:0,pointerEvents:"none"}})});var M=require("react/jsx-runtime");function dt(e,t){let[r,n]=e.domain(),o=Math.min(r,n),l=(Math.max(r,n)-o)/(t-1);return Array.from({length:t},(s,c)=>o+c*l)}function bt(e){return Math.abs(e)>=1e3?Math.round(e).toString():Math.abs(e)>=1?e.toFixed(1):Math.abs(e)>=.01?e.toFixed(3):e.toExponential(1)}function te({xScale:e,yScale:t,width:r,height:n,xLabel:o,yLabel:i,showGrid:l=!0,colors:s}){let c=dt(e,8),m=dt(t,6);return(0,M.jsxs)("g",{children:[l&&(0,M.jsxs)("g",{children:[c.map(u=>(0,M.jsx)("line",{x1:e(u),x2:e(u),y1:0,y2:n,stroke:s.gridColor,strokeWidth:.5},`xgrid-${u}`)),m.map(u=>(0,M.jsx)("line",{x1:0,x2:r,y1:t(u),y2:t(u),stroke:s.gridColor,strokeWidth:.5},`ygrid-${u}`))]}),(0,M.jsxs)("g",{transform:`translate(0, ${n})`,children:[(0,M.jsx)("line",{x1:0,x2:r,y1:0,y2:0,stroke:s.axisColor}),c.map(u=>(0,M.jsxs)("g",{transform:`translate(${e(u)}, 0)`,children:[(0,M.jsx)("line",{y1:0,y2:6,stroke:s.axisColor}),(0,M.jsx)("text",{y:20,textAnchor:"middle",fill:s.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:bt(u)})]},`xtick-${u}`)),o&&(0,M.jsx)("text",{x:r/2,y:42,textAnchor:"middle",fill:s.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:o})]}),(0,M.jsxs)("g",{children:[(0,M.jsx)("line",{x1:0,x2:0,y1:0,y2:n,stroke:s.axisColor}),m.map(u=>(0,M.jsxs)("g",{transform:`translate(0, ${t(u)})`,children:[(0,M.jsx)("line",{x1:-6,x2:0,stroke:s.axisColor}),(0,M.jsx)("text",{x:-10,textAnchor:"end",dominantBaseline:"middle",fill:s.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:bt(u)})]},`ytick-${u}`)),i&&(0,M.jsx)("text",{transform:`translate(-50, ${n/2}) rotate(-90)`,textAnchor:"middle",fill:s.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:i})]})]})}var re=require("react/jsx-runtime");function he({peaks:e,xScale:t,yScale:r,colors:n,onPeakClick:o}){let[i,l]=t.domain(),s=Math.min(i,l),c=Math.max(i,l),m=e.filter(u=>u.x>=s&&u.x<=c);return(0,re.jsx)("g",{className:"spectraview-peaks",children:m.map((u,p)=>{let f=t(u.x),a=r(u.y);return(0,re.jsxs)("g",{transform:`translate(${f}, ${a})`,style:{cursor:o?"pointer":"default"},onClick:()=>o?.(u),children:[(0,re.jsx)("polygon",{points:`0,-5 -5,${-5*2.5} 5,${-5*2.5}`,fill:n.labelColor,opacity:.8}),u.label&&(0,re.jsx)("text",{y:-5*2.5-14,textAnchor:"middle",fill:n.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",fontWeight:500,children:u.label})]},`peak-${u.x}-${p}`)})})}var oe=require("react/jsx-runtime");function ye({regions:e,xScale:t,height:r,colors:n}){return(0,oe.jsx)("g",{className:"spectraview-regions",children:e.map((o,i)=>{let l=t(o.xStart),s=t(o.xEnd),c=Math.min(l,s),m=Math.abs(s-l);return(0,oe.jsxs)("g",{children:[(0,oe.jsx)("rect",{x:c,y:0,width:m,height:r,fill:o.color??n.regionFill,stroke:n.regionStroke,strokeWidth:1}),o.label&&(0,oe.jsx)("text",{x:c+m/2,y:12,textAnchor:"middle",fill:n.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",children:o.label})]},`region-${i}`)})})}var N=require("react/jsx-runtime");function xe({position:e,width:t,height:r,colors:n,snapPoint:o}){return e?(0,N.jsxs)("g",{className:"spectraview-crosshair",pointerEvents:"none",children:[(0,N.jsx)("line",{x1:e.px,x2:e.px,y1:0,y2:r,stroke:n.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),(0,N.jsx)("line",{x1:0,x2:t,y1:e.py,y2:e.py,stroke:n.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),o&&(0,N.jsx)("circle",{cx:o.px,cy:o.py,r:4,fill:o.color??n.crosshairColor,stroke:n.background,strokeWidth:1.5}),(0,N.jsxs)("g",{transform:`translate(${Math.min(e.px+10,t-100)}, ${Math.max(e.py-10,20)})`,children:[(0,N.jsx)("rect",{x:0,y:-14,width:90,height:18,rx:3,fill:n.tooltipBg,stroke:n.tooltipBorder,strokeWidth:.5,opacity:.9}),(0,N.jsxs)("text",{x:5,y:0,fill:n.tooltipText,fontSize:10,fontFamily:"monospace",children:[gt(o?.dataX??e.dataX),","," ",gt(o?.dataY??e.dataY)]})]})]}):null}function gt(e){return Math.abs(e)>=100?Math.round(e).toString():Math.abs(e)>=1?e.toFixed(1):e.toFixed(4)}var H=require("react/jsx-runtime");function Se({annotations:e,xScale:t,yScale:r,colors:n}){return e.length===0?null:(0,H.jsx)("g",{className:"spectraview-annotations",pointerEvents:"none",children:e.map(o=>{let i=t(o.x),l=r(o.y),[s,c]=o.offset??[0,-20],m=i+s,u=l+c,p=o.fontSize??11,f=o.color??n.tickColor,a=o.showAnchorLine!==!1;return(0,H.jsxs)("g",{children:[a&&(0,H.jsx)("line",{x1:i,y1:l,x2:m,y2:u,stroke:f,strokeWidth:.75,strokeDasharray:"3 2",opacity:.6}),(0,H.jsx)("circle",{cx:i,cy:l,r:2.5,fill:f,opacity:.8}),(0,H.jsx)("text",{x:m,y:u,fill:n.background,fontSize:p,fontFamily:"system-ui, sans-serif",textAnchor:"middle",dominantBaseline:"auto",stroke:n.background,strokeWidth:3,strokeLinejoin:"round",children:o.text}),(0,H.jsx)("text",{x:m,y:u,fill:f,fontSize:p,fontFamily:"system-ui, sans-serif",textAnchor:"middle",dominantBaseline:"auto",children:o.text})]},o.id)})})}function Je(e,t,r){if(r===0)return-1;if(r===1)return 0;let n=e[r-1]>=e[0],o=0,i=r-1;for(;o<i-1;){let c=o+i>>>1,m=e[c];n?m<=t?o=c:i=c:m>=t?o=c:i=c}let l=Math.abs(e[o]-t),s=Math.abs(e[i]-t);return l<=s?o:i}function ve(e,t,r,n,o){let i=null;for(let l of e){if(l.visible===!1)continue;let s=Math.min(l.x.length,l.y.length);if(s<2)continue;let c=Je(l.x,t,s);if(c<0)continue;let m=l.x[c],u=l.y[c],p=Math.abs(n(m)-n(t)),f=Math.abs(o(u)-r),a=Math.sqrt(p*p+f*f);(!i||a<i.distance)&&(i={spectrumId:l.id,index:c,x:m,y:u,distance:a})}return i}var ht=require("react"),ne=require("react/jsx-runtime"),qe=e=>({display:"inline-flex",alignItems:"center",justifyContent:"center",width:28,height:28,border:`1px solid ${e==="dark"?"#4b5563":"#d1d5db"}`,borderRadius:4,background:e==="dark"?"#1f2937":"#ffffff",color:e==="dark"?"#d1d5db":"#374151",fontSize:14,cursor:"pointer",padding:0,lineHeight:1}),sr=e=>({display:"flex",gap:4,padding:"4px 0",borderBottom:`1px solid ${e==="dark"?"#374151":"#e5e7eb"}`}),Ce=(0,ht.memo)(function({onZoomIn:t,onZoomOut:r,onReset:n,isZoomed:o,theme:i}){return(0,ne.jsxs)("div",{style:sr(i),className:"spectraview-toolbar",children:[(0,ne.jsx)("button",{type:"button",style:qe(i),onClick:t,title:"Zoom in","aria-label":"Zoom in",children:"+"}),(0,ne.jsx)("button",{type:"button",style:qe(i),onClick:r,title:"Zoom out","aria-label":"Zoom out",children:"\u2212"}),(0,ne.jsx)("button",{type:"button",style:{...qe(i),opacity:o?1:.4},onClick:n,disabled:!o,title:"Reset zoom","aria-label":"Reset zoom",children:"\u21BA"})]})});var yt=require("react");var ie=require("react/jsx-runtime"),ar=(e,t)=>({display:"flex",flexDirection:t==="left"||t==="right"?"column":"row",flexWrap:"wrap",gap:6,padding:"4px 8px",fontSize:12,fontFamily:"system-ui, sans-serif",borderTop:t==="bottom"?`1px solid ${e==="dark"?"#374151":"#e5e7eb"}`:void 0,borderBottom:t==="top"?`1px solid ${e==="dark"?"#374151":"#e5e7eb"}`:void 0,borderLeft:t==="right"?`1px solid ${e==="dark"?"#374151":"#e5e7eb"}`:void 0,borderRight:t==="left"?`1px solid ${e==="dark"?"#374151":"#e5e7eb"}`:void 0}),lr=(e,t,r)=>({display:"inline-flex",alignItems:"center",gap:4,cursor:"pointer",opacity:t?.4:1,fontWeight:r?600:400,color:e==="dark"?"#e5e7eb":"#374151",userSelect:"none",padding:"2px 4px",borderRadius:3,background:r?e==="dark"?"rgba(255,255,255,0.08)":"rgba(0,0,0,0.04)":"transparent",transition:"background 0.15s, opacity 0.15s"}),cr=(e,t)=>({width:12,height:3,borderRadius:1,background:e,opacity:t?.4:1,flexShrink:0}),se=(0,yt.memo)(function({spectra:t,theme:r,position:n,onToggleVisibility:o,onHighlight:i,highlightedId:l}){return t.length<=1?null:(0,ie.jsx)("div",{className:"spectraview-legend",style:ar(r,n),role:"list","aria-label":"Spectrum legend",children:t.map((s,c)=>{let m=s.color??U(c),u=s.visible===!1,p=l===s.id;return(0,ie.jsxs)("div",{role:"listitem",style:lr(r,u,p),onClick:()=>o?.(s.id),onMouseEnter:()=>i?.(s.id),onMouseLeave:()=>i?.(null),title:u?`Show ${s.label}`:`Hide ${s.label}`,children:[(0,ie.jsx)("span",{style:cr(m,u)}),(0,ie.jsx)("span",{style:{textDecoration:u?"line-through":"none",maxWidth:120,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:s.label})]},s.id)})})});var K=require("react"),ke=require("react/jsx-runtime");function we({enabled:e,theme:t,width:r,height:n,onDrop:o,children:i}){let[l,s]=(0,K.useState)(!1),c={current:0},m=(0,K.useCallback)(a=>{e&&(a.preventDefault(),c.current++,s(!0))},[e]),u=(0,K.useCallback)(a=>{e&&(a.preventDefault(),c.current--,c.current<=0&&(c.current=0,s(!1)))},[e]),p=(0,K.useCallback)(a=>{e&&(a.preventDefault(),a.dataTransfer.dropEffect="copy")},[e]),f=(0,K.useCallback)(a=>{if(!e)return;a.preventDefault(),c.current=0,s(!1);let d=Array.from(a.dataTransfer.files);d.length>0&&o?.(d)},[e,o]);return(0,ke.jsxs)("div",{style:{position:"relative",width:r,height:n},onDragEnter:m,onDragLeave:u,onDragOver:p,onDrop:f,children:[i,l&&(0,ke.jsx)("div",{"data-testid":"dropzone-overlay",style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:t==="dark"?"rgba(30, 58, 138, 0.6)":"rgba(59, 130, 246, 0.15)",border:`2px dashed ${t==="dark"?"#60a5fa":"#3b82f6"}`,borderRadius:4,zIndex:100,pointerEvents:"none",fontSize:14,fontFamily:"system-ui, sans-serif",color:t==="dark"?"#93c5fd":"#1d4ed8",fontWeight:500},children:"Drop spectrum files here"})]})}var St=require("react");var z=require("react/jsx-runtime"),xt=8;function Re({spectra:e,xScale:t,plotWidth:r,plotHeight:n,margin:o,theme:i,showGrid:l,xLabel:s,yLabel:c}){let m=e.filter(d=>d.visible!==!1),u=(0,St.useMemo)(()=>q(i),[i]),p=m.length,f=(p-1)*xt,a=Math.max(40,Math.floor((n-f)/Math.max(p,1)));return(0,z.jsx)("g",{className:"spectraview-stacked",children:m.map((d,g)=>{let S=g*(a+xt),b=G([d]),C=J(b,a+o.top+o.bottom,{...o,top:0,bottom:0}),v=d.color??U(g),k={...d,color:v};return(0,z.jsxs)("g",{transform:`translate(0, ${S})`,children:[(0,z.jsx)("rect",{x:0,y:0,width:r,height:a,fill:"transparent",stroke:u.gridColor,strokeWidth:.5,rx:2}),(0,z.jsx)(te,{xScale:t,yScale:C,width:r,height:a,xLabel:g===p-1?s:"",yLabel:c,showGrid:l,colors:u}),(0,z.jsx)("text",{x:4,y:14,fill:v,fontSize:11,fontFamily:"system-ui, sans-serif",fontWeight:500,children:d.label}),(0,z.jsx)("foreignObject",{x:0,y:0,width:r,height:a,children:(0,z.jsx)(ee,{spectra:[k],xScale:t,yScale:C,width:r,height:a})})]},d.id)})})}var W=require("react");function Te(e){let{enabled:t,xScale:r,onRegionSelect:n}=e,[o,i]=(0,W.useState)(null),l=(0,W.useRef)(null),s=(0,W.useCallback)(u=>{if(!t||!u.shiftKey)return;u.preventDefault();let p=u.currentTarget.getBoundingClientRect(),f=u.clientX-p.left,a=r.invert(f);l.current=a,i({xStart:a,xEnd:a})},[t,r]),c=(0,W.useCallback)(u=>{if(l.current===null)return;let p=u.currentTarget.getBoundingClientRect(),f=u.clientX-p.left,a=r.invert(f),d=l.current;i({xStart:Math.min(d,a),xEnd:Math.max(d,a)})},[r]),m=(0,W.useCallback)(()=>{if(l.current===null||!o)return;Math.abs(o.xEnd-o.xStart)>0&&n?.(o),l.current=null,i(null)},[o,n]);return{pendingRegion:o,handleMouseDown:s,handleMouseMove:c,handleMouseUp:m}}var V=require("react");function Ee(){let[e,t]=(0,V.useState)(null),r=(0,V.useRef)(null),n=(0,V.useRef)(null),o=(0,V.useCallback)(i=>{if(r.current&&(r.current.disconnect(),r.current=null),n.current=i,!i)return;let l=new ResizeObserver(m=>{let u=m[0];if(!u)return;let{width:p,height:f}=u.contentRect;t({width:Math.round(p),height:Math.round(f)})});l.observe(i),r.current=l;let{width:s,height:c}=i.getBoundingClientRect();t({width:Math.round(s),height:Math.round(c)})},[]);return(0,V.useEffect)(()=>()=>{r.current?.disconnect()},[]),{ref:o,size:e}}var vt=require("react");function Me(e){let{onZoomIn:t,onZoomOut:r,onReset:n,enabled:o=!0}=e;return(0,vt.useCallback)(l=>{if(o)switch(l.key){case"+":case"=":l.preventDefault(),t();break;case"-":l.preventDefault(),r();break;case"Escape":l.preventDefault(),n();break}},[o,t,r,n])}function Ct(){return typeof window>"u"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches}function Le(e,t,r){return e===0?"Empty spectrum viewer":`Interactive spectrum viewer showing ${e} ${e===1?"spectrum":"spectra"}. X-axis: ${t}. Y-axis: ${r}. Use arrow keys to pan, +/- to zoom, Escape to reset.`}var wt={PAN_LEFT:"ArrowLeft",PAN_RIGHT:"ArrowRight",PAN_UP:"ArrowUp",PAN_DOWN:"ArrowDown",ZOOM_IN:"+",ZOOM_IN_ALT:"=",ZOOM_OUT:"-",RESET:"Escape",NEXT_PEAK:"Tab",PREV_PEAK:"Shift+Tab"};var R=require("react/jsx-runtime"),mr={top:20,right:20,bottom:50,left:65},ur=800,pr=400;function fr(e){return{width:e.width??ur,height:e.height??pr,reverseX:e.reverseX??!1,showGrid:e.showGrid??!0,showCrosshair:e.showCrosshair??!0,showToolbar:e.showToolbar??!0,showLegend:e.showLegend??!0,legendPosition:e.legendPosition??"bottom",displayMode:e.displayMode??"overlay",margin:{...mr,...e.margin},theme:e.theme??"light",responsive:e.responsive??!1,enableDragDrop:e.enableDragDrop??!1,enableRegionSelect:e.enableRegionSelect??!1}}function dr(e,t,r){let n=e[0];return{xLabel:t??n?.xUnit??"x",yLabel:r??n?.yUnit??"y"}}function kt(e){let{spectra:t,peaks:r=[],regions:n=[],annotations:o=[],onPeakClick:i,onViewChange:l,onCrosshairMove:s,onToggleVisibility:c,onFileDrop:m,onRegionSelect:u,canvasRef:p,snapCrosshair:f=!0}=e,{ref:a,size:d}=Ee(),S=`spectraview-clip-${(0,E.useId)().replace(/:/g,"")}`,b=(0,E.useMemo)(()=>fr(e),[e.width,e.height,e.reverseX,e.showGrid,e.showCrosshair,e.showToolbar,e.showLegend,e.legendPosition,e.displayMode,e.margin,e.theme,e.responsive,e.enableDragDrop,e.enableRegionSelect]),C=b.responsive&&d?d.width:b.width,{height:v,margin:k,reverseX:h,theme:y}=b,w=C-k.left-k.right,x=v-k.top-k.bottom,T=(0,E.useMemo)(()=>q(y),[y]),L=(0,E.useMemo)(()=>dr(t,e.xLabel,e.yLabel),[t,e.xLabel,e.yLabel]),D=(0,E.useMemo)(()=>pe(t),[t]),I=(0,E.useMemo)(()=>G(t),[t]),Ve=(0,E.useMemo)(()=>fe(D,C,k,h),[D,C,k,h]),Y=(0,E.useMemo)(()=>J(I,v,k),[I,v,k]),j=(0,E.useRef)(l);j.current=l;let Ot=(0,E.useMemo)(()=>(B,me)=>{j.current?.({xDomain:B,yDomain:me})},[]),{zoomRef:tt,state:Nt,zoomedXScale:A,zoomedYScale:_,resetZoom:rt,zoomIn:ot,zoomOut:nt}=be({plotWidth:w,plotHeight:x,xScale:Ve,yScale:Y,onViewChange:l?Ot:void 0}),{pendingRegion:le,handleMouseDown:zt,handleMouseMove:Vt,handleMouseUp:Zt}=Te({enabled:b.enableRegionSelect,xScale:A,onRegionSelect:u}),[Ze,it]=(0,E.useState)(null),[_t,st]=(0,E.useState)(null),[Bt,_e]=(0,E.useState)(null),ce=(0,E.useRef)(s);ce.current=s;let at=(0,E.useCallback)(B=>{if(!b.showCrosshair)return;let me=B.currentTarget.getBoundingClientRect(),ct=B.clientX-me.left,Xe=B.clientY-me.top,ue=A.invert(ct),He=_.invert(Xe);if(st({px:ct,py:Xe,dataX:ue,dataY:He}),f&&t.length>0){let O=ve(t,ue,Xe,A,_);if(O&&O.distance<50){let mt=t.findIndex(Yt=>Yt.id===O.spectrumId);_e({px:A(O.x),py:_(O.y),dataX:O.x,dataY:O.y,color:t[mt]?.color??U(mt)}),ce.current?.(O.x,O.y)}else _e(null),ce.current?.(ue,He)}else ce.current?.(ue,He)},[A,_,b.showCrosshair,f,t]),lt=(0,E.useCallback)(()=>{st(null),_e(null)},[]),Xt=Me({onZoomIn:ot,onZoomOut:nt,onReset:rt}),Ht=(0,E.useMemo)(()=>Le(t.length,L.xLabel,L.yLabel),[t.length,L.xLabel,L.yLabel]),Wt=b.displayMode==="stacked";if(t.length===0)return(0,R.jsx)("div",{ref:b.responsive?a:void 0,style:{width:b.responsive?"100%":C,height:v,display:"flex",alignItems:"center",justifyContent:"center",border:`1px dashed ${T.gridColor}`,borderRadius:8,color:T.tickColor,fontFamily:"system-ui, sans-serif",fontSize:14},className:e.className,children:"No spectra loaded"});let Be=b.showToolbar?37:0;return(0,R.jsxs)("div",{ref:b.responsive?a:void 0,style:{width:b.responsive?"100%":C,background:T.background,borderRadius:4,overflow:"hidden"},className:e.className,role:"img","aria-label":Ht,tabIndex:0,onKeyDown:Xt,children:[b.showToolbar&&(0,R.jsx)(Ce,{onZoomIn:ot,onZoomOut:nt,onReset:rt,isZoomed:Nt.isZoomed,theme:y}),b.showLegend&&b.legendPosition==="top"&&(0,R.jsx)(se,{spectra:t,theme:y,position:"top",onToggleVisibility:c,onHighlight:it,highlightedId:Ze}),(0,R.jsx)(we,{enabled:b.enableDragDrop,theme:y,width:C,height:v-Be,onDrop:m,children:Wt?(0,R.jsx)("svg",{width:C,height:v-Be,style:{position:"absolute",top:0,left:0},children:(0,R.jsxs)("g",{transform:`translate(${k.left}, ${k.top})`,children:[(0,R.jsx)(Re,{spectra:t,xScale:A,plotWidth:w,plotHeight:x,margin:k,theme:y,showGrid:b.showGrid,xLabel:L.xLabel,yLabel:L.yLabel}),(0,R.jsx)("rect",{ref:tt,x:0,y:0,width:w,height:x,fill:"transparent",style:{cursor:"grab"},onMouseMove:at,onMouseLeave:lt})]})}):(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)("div",{style:{position:"absolute",top:k.top,left:k.left,width:w,height:x,overflow:"hidden"},children:(0,R.jsx)(ee,{ref:p,spectra:t,xScale:A,yScale:_,width:w,height:x,highlightedId:Ze??void 0})}),(0,R.jsx)("svg",{width:C,height:v-Be,style:{position:"absolute",top:0,left:0},children:(0,R.jsxs)("g",{transform:`translate(${k.left}, ${k.top})`,children:[(0,R.jsx)(te,{xScale:A,yScale:_,width:w,height:x,xLabel:L.xLabel,yLabel:L.yLabel,showGrid:b.showGrid,colors:T}),(0,R.jsx)("defs",{children:(0,R.jsx)("clipPath",{id:S,children:(0,R.jsx)("rect",{x:0,y:0,width:w,height:x})})}),(0,R.jsxs)("g",{clipPath:`url(#${S})`,children:[n.length>0&&(0,R.jsx)(ye,{regions:n,xScale:A,height:x,colors:T}),r.length>0&&(0,R.jsx)(he,{peaks:r,xScale:A,yScale:_,colors:T,onPeakClick:i})]}),o.length>0&&(0,R.jsx)(Se,{annotations:o,xScale:A,yScale:_,colors:T}),b.showCrosshair&&(0,R.jsx)(xe,{position:_t,width:w,height:x,colors:T,snapPoint:Bt}),le&&(0,R.jsx)("rect",{x:A(le.xStart),y:0,width:Math.abs(A(le.xEnd)-A(le.xStart)),height:x,fill:T.regionFill,stroke:T.regionStroke,strokeWidth:1,pointerEvents:"none"}),(0,R.jsx)("rect",{ref:tt,x:0,y:0,width:w,height:x,fill:"transparent",style:{cursor:b.showCrosshair?"crosshair":"grab"},onMouseDown:zt,onMouseMove:B=>{at(B),Vt(B)},onMouseUp:Zt,onMouseLeave:lt})]})})]})}),b.showLegend&&b.legendPosition==="bottom"&&(0,R.jsx)(se,{spectra:t,theme:y,position:"bottom",onToggleVisibility:c,onHighlight:it,highlightedId:Ze})]})}var Rt=require("react");function Pe(e,t,r={}){let{prominence:n=.01,minDistance:o=5,maxPeaks:i}=r;if(e.length<3||t.length<3)return[];let l=1/0,s=-1/0;for(let a=0;a<t.length;a++)t[a]<l&&(l=t[a]),t[a]>s&&(s=t[a]);let c=s-l;if(c===0)return[];let m=n*c,u=[];for(let a=1;a<t.length-1;a++)if(t[a]>t[a-1]&&t[a]>t[a+1]){let d=br(t,a),g=gr(t,a),S=t[a]-Math.max(d,g);S>=m&&u.push({index:a,prom:S})}u.sort((a,d)=>d.prom-a.prom);let p=[];for(let a of u)p.some(g=>Math.abs(g.index-a.index)<o)||p.push(a);return(i?p.slice(0,i):p).map(a=>({x:e[a.index],y:t[a.index],label:hr(e[a.index])})).sort((a,d)=>a.x-d.x)}function br(e,t){let r=e[t];for(let n=t-1;n>=0&&!(e[n]>e[t]);n--)e[n]<r&&(r=e[n]);return r}function gr(e,t){let r=e[t];for(let n=t+1;n<e.length&&!(e[n]>e[t]);n++)e[n]<r&&(r=e[n]);return r}function hr(e){return Math.round(e).toString()}function Tt(e,t={}){let{enabled:r=!0,spectrumIds:n,prominence:o,minDistance:i,maxPeaks:l}=t;return(0,Rt.useMemo)(()=>{if(!r)return[];let s=n?e.filter(m=>n.includes(m.id)):e,c=[];for(let m of s){if(m.visible===!1)continue;let u=Pe(m.x,m.y,{prominence:o,minDistance:i,maxPeaks:l});for(let p of u)c.push({...p,spectrumId:m.id})}return c},[e,r,n,o,i,l])}var F=require("react");var Et=0,yr=[" ",",",";"," "];function Mt(e){let t=e.trim().split(/\r?\n/).slice(0,5),r=",",n=0;for(let o of yr){let i=t.map(s=>s.split(o).length-1),l=Math.min(...i);l>0&&l>=n&&(i.every(c=>c===i[0])||l>n)&&(n=l,r=o)}return r}function Ae(e,t={}){let{xColumn:r=0,yColumn:n=1,hasHeader:o=!0,label:i="CSV Spectrum"}=t,l=t.delimiter??Mt(e),s=e.trim().split(/\r?\n/);if(s.length<2)throw new Error("CSV file must contain at least 2 lines");let c=i,m=0;if(o){let f=s[0].split(l).map(a=>a.trim());!t.label&&f[n]&&(c=f[n]),m=1}let u=[],p=[];for(let f=m;f<s.length;f++){let a=s[f].trim();if(a===""||a.startsWith("#"))continue;let d=a.split(l),g=parseFloat(d[r]),S=parseFloat(d[n]);!isNaN(g)&&!isNaN(S)&&(u.push(g),p.push(S))}if(u.length===0)throw new Error("No valid numeric data found in CSV");return{id:`csv-${++Et}`,label:c,x:new Float64Array(u),y:new Float64Array(p)}}function Lt(e,t={}){let{hasHeader:r=!0,label:n}=t,o=t.delimiter??Mt(e),i=e.trim().split(/\r?\n/);if(i.length<2)throw new Error("CSV file must contain at least 2 lines");let s=i[r?1:0].split(o).length;if(s<2)throw new Error("CSV must have at least 2 columns (x + y)");let c,m=0;r&&(c=i[0].split(o).map(a=>a.trim()),m=1);let u=[],p=Array.from({length:s-1},()=>[]);for(let a=m;a<i.length;a++){let d=i[a].trim();if(d===""||d.startsWith("#"))continue;let g=d.split(o),S=parseFloat(g[0]);if(!isNaN(S)){u.push(S);for(let b=1;b<s;b++){let C=parseFloat(g[b]);p[b-1].push(isNaN(C)?0:C)}}}let f=new Float64Array(u);return p.map((a,d)=>({id:`csv-${++Et}`,label:n??c?.[d+1]??`Spectrum ${d+1}`,x:f,y:new Float64Array(a)}))}var xr=0;function De(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Invalid JSON: failed to parse input")}if(Array.isArray(t))return t.map((r,n)=>Qe(r,n));if(typeof t=="object"&&t!==null){let r=t;return Array.isArray(r.spectra)?r.spectra.map((n,o)=>Qe(n,o)):[Qe(t,0)]}throw new Error("Invalid JSON structure: expected an object or array")}function Qe(e,t){let r=e.x??e.wavenumbers??e.wavelengths;if(!r||!Array.isArray(r))throw new Error(`Spectrum ${t}: missing x-axis data (expected "x", "wavenumbers", or "wavelengths")`);let n=e.y??e.intensities??e.absorbance;if(!n||!Array.isArray(n))throw new Error(`Spectrum ${t}: missing y-axis data (expected "y", "intensities", or "absorbance")`);if(r.length!==n.length)throw new Error(`Spectrum ${t}: x and y arrays must have the same length (got ${r.length} and ${n.length})`);let o=e.label??e.title??e.name??`Spectrum ${t+1}`;return{id:`json-${++xr}`,label:o,x:new Float64Array(r),y:new Float64Array(n),xUnit:e.xUnit,yUnit:e.yUnit,type:e.type,meta:e.meta}}var At=0,Ie=null,Pt=!1;async function Sr(){if(Pt)return Ie;Pt=!0;try{Ie=await import("jcampconverter")}catch{Ie=null}return Ie}function Dt(e){let t=(e["DATA TYPE"]??e.DATATYPE??"").toLowerCase();return t.includes("infrared")||t.includes("ir")?"IR":t.includes("raman")?"Raman":t.includes("nir")||t.includes("near")?"NIR":t.includes("uv")||t.includes("vis")?"UV-Vis":t.includes("fluor")?"fluorescence":"other"}async function Ue(e){let t=await Sr();return t?vr(e,t):[Cr(e)]}function vr(e,t){return t.convert(e,{keepRecordsRegExp:/.*/}).flatten.map((n,o)=>{let i=n.spectra?.[0]?.data?.[0];if(!i)throw new Error(`JCAMP block ${o}: no spectral data found`);return{id:`jcamp-${++At}`,label:n.info?.TITLE??`Spectrum ${o+1}`,x:new Float64Array(i.x),y:new Float64Array(i.y),xUnit:n.info?.XUNITS??"cm\u207B\xB9",yUnit:n.info?.YUNITS??"Absorbance",type:Dt(n.info),meta:n.info}})}function Cr(e){let t=e.split(/\r?\n/),r={},n=[],o=[],i=!1;for(let l of t){let s=l.trim();if(s.startsWith("##")){let c=s.match(/^##(.+?)=\s*(.*)$/);if(c){let m=c[1].trim().toUpperCase(),u=c[2].trim();if(m==="XYDATA"||m==="XYPOINTS"){i=!0;continue}if(m==="END"){i=!1;continue}r[m]=u}continue}if(i&&s!==""){let c=s.split(/[\s,]+/).map(Number);if(c.length>=2&&!c.some(isNaN)){let m=c[0],u=parseFloat(r.FIRSTX??"0"),p=parseFloat(r.LASTX??"0"),f=parseInt(r.NPOINTS??"0",10);if(f>0&&c.length===2)n.push(c[0]),o.push(c[1]);else if(c.length>1){let a=f>1?(p-u)/(f-1):0;for(let d=1;d<c.length;d++)n.push(m+(d-1)*a),o.push(c[d])}}}}if(n.length===0)throw new Error("Failed to parse JCAMP-DX: no data found. Install jcampconverter for full format support.");return{id:`jcamp-${++At}`,label:r.TITLE??"JCAMP Spectrum",x:new Float64Array(n),y:new Float64Array(o),xUnit:r.XUNITS??"cm\u207B\xB9",yUnit:r.YUNITS??"Absorbance",type:Dt(r),meta:r}}function wr(e){switch(e.toLowerCase().split(".").pop()){case"dx":case"jdx":case"jcamp":return"jcamp";case"csv":case"tsv":case"txt":return"csv";case"json":return"json";default:return null}}function It(e=[]){let[t,r]=(0,F.useState)(e),[n,o]=(0,F.useState)(!1),[i,l]=(0,F.useState)(null),s=(0,F.useCallback)(async(a,d)=>{o(!0),l(null);try{let g;switch(d){case"jcamp":g=await Ue(a);break;case"csv":g=[Ae(a)];break;case"json":g=De(a);break}r(S=>[...S,...g])}catch(g){let S=g instanceof Error?g.message:"Failed to parse file";l(S)}finally{o(!1)}},[]),c=(0,F.useCallback)(async a=>{let d=wr(a.name);if(!d){l(`Unsupported file format: ${a.name}`);return}let g=await a.text();await s(g,d)},[s]),m=(0,F.useCallback)(a=>{r(d=>[...d,a])},[]),u=(0,F.useCallback)(a=>{r(d=>d.filter(g=>g.id!==a))},[]),p=(0,F.useCallback)(a=>{r(d=>d.map(g=>g.id===a?{...g,visible:g.visible===!1}:g))},[]),f=(0,F.useCallback)(()=>{r([]),l(null)},[]);return{spectra:t,loading:n,error:i,loadFile:c,loadText:s,addSpectrum:m,removeSpectrum:u,toggleVisibility:p,clear:f}}var ae=require("react");var et={solid:"",dashed:"8 4",dotted:"2 2","dash-dot":"8 4 2 4"};function Fe(e,t,r,n){let{width:o,height:i,background:l="#ffffff",title:s}=n,c=e.filter(m=>m.visible!==!1).map((m,u)=>{let p=m.color??U(u),f=m.lineStyle??"solid",a=m.lineWidth??1.5,d=et[f]??"",g=Math.min(m.x.length,m.y.length);if(g<2)return"";let S=[];for(let b=0;b<g;b++){let C=t(m.x[b]).toFixed(2),v=r(m.y[b]).toFixed(2);S.push(`${b===0?"M":"L"}${C},${v}`)}return`<path d="${S.join(" ")}" fill="none" stroke="${p}" stroke-width="${a}"${d?` stroke-dasharray="${d}"`:""}/>
3
+ <!-- ${m.label} -->`}).filter(Boolean).join(`
4
+ `);return`<?xml version="1.0" encoding="UTF-8"?>
5
+ <svg xmlns="http://www.w3.org/2000/svg" width="${o}" height="${i}" viewBox="0 0 ${o} ${i}">
6
+ <rect width="${o}" height="${i}" fill="${l}"/>
7
+ ${s?`<text x="${o/2}" y="20" text-anchor="middle" font-family="system-ui" font-size="14">${s}</text>`:""}
8
+ <g>
9
+ ${c}
10
+ </g>
11
+ </svg>`}function $e(e,t="spectrum.svg"){let r=new Blob([e],{type:"image/svg+xml"}),n=URL.createObjectURL(r),o=document.createElement("a");o.href=n,o.download=t,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n)}function Oe(e,t){let r=URL.createObjectURL(e),n=document.createElement("a");n.href=r,n.download=t,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(r)}function Ut(){let e=(0,ae.useCallback)((o,i="spectrum.png")=>{o.toBlob(l=>{l&&Oe(l,i)},"image/png")},[]),t=(0,ae.useCallback)((o,i="spectra.csv")=>{let l=o.filter(s=>s.visible!==!1);if(l.length!==0)if(l.length===1){let s=l[0],c=`${s.xUnit??"x"},${s.yUnit??"y"}
12
+ `,m=Array.from(s.x).map((p,f)=>`${p},${s.y[f]}`),u=c+m.join(`
13
+ `);Oe(new Blob([u],{type:"text/csv"}),i)}else{let s=Math.max(...l.map(p=>p.x.length)),c=l.map(p=>`${p.label}_x,${p.label}_y`).join(","),m=[];for(let p=0;p<s;p++){let f=l.map(a=>p<a.x.length?`${a.x[p]},${a.y[p]}`:",");m.push(f.join(","))}let u=c+`
14
+ `+m.join(`
15
+ `);Oe(new Blob([u],{type:"text/csv"}),i)}},[]),r=(0,ae.useCallback)((o,i="spectra.json")=>{let s=o.filter(m=>m.visible!==!1).map(m=>({label:m.label,x:Array.from(m.x),y:Array.from(m.y),xUnit:m.xUnit,yUnit:m.yUnit,type:m.type})),c=JSON.stringify(s,null,2);Oe(new Blob([c],{type:"application/json"}),i)},[]),n=(0,ae.useCallback)((o,i,l,s,c,m="spectrum.svg")=>{let u=Fe(o,i,l,{width:s,height:c});$e(u,m)},[]);return{exportPng:e,exportSvg:n,exportCsv:t,exportJson:r}}var ze=require("react"),Z=require("react/jsx-runtime"),kr=e=>({display:"inline-flex",alignItems:"center",justifyContent:"center",height:28,padding:"0 8px",border:`1px solid ${e==="dark"?"#4b5563":"#d1d5db"}`,borderRadius:4,background:e==="dark"?"#1f2937":"#ffffff",color:e==="dark"?"#d1d5db":"#374151",fontSize:12,cursor:"pointer",lineHeight:1,position:"relative"}),Rr=e=>({position:"absolute",top:30,left:0,background:e==="dark"?"#1f2937":"#ffffff",border:`1px solid ${e==="dark"?"#4b5563":"#d1d5db"}`,borderRadius:4,boxShadow:"0 2px 8px rgba(0,0,0,0.15)",zIndex:200,minWidth:100,overflow:"hidden"}),Ne=e=>({display:"block",width:"100%",padding:"6px 12px",border:"none",background:"transparent",color:e==="dark"?"#d1d5db":"#374151",fontSize:12,textAlign:"left",cursor:"pointer"});function Ft({theme:e,onExportPng:t,onExportSvg:r,onExportCsv:n,onExportJson:o}){let[i,l]=(0,ze.useState)(!1),s=(0,ze.useCallback)(c=>{l(!1),c?.()},[]);return(0,Z.jsxs)("div",{style:{position:"relative",display:"inline-block"},children:[(0,Z.jsx)("button",{type:"button",style:kr(e),onClick:()=>l(!i),"aria-label":"Export","aria-expanded":i,"aria-haspopup":"true",children:"Export"}),i&&(0,Z.jsxs)("div",{style:Rr(e),role:"menu",children:[t&&(0,Z.jsx)("button",{type:"button",role:"menuitem",style:Ne(e),onClick:()=>s(t),children:"PNG Image"}),r&&(0,Z.jsx)("button",{type:"button",role:"menuitem",style:Ne(e),onClick:()=>s(r),children:"SVG Vector"}),n&&(0,Z.jsx)("button",{type:"button",role:"menuitem",style:Ne(e),onClick:()=>s(n),children:"CSV Data"}),o&&(0,Z.jsx)("button",{type:"button",role:"menuitem",style:Ne(e),onClick:()=>s(o),children:"JSON Data"})]})]})}var Tr=0,Er=1,Mr=4,Lr=128,Pr={0:"Arbitrary",1:"cm\u207B\xB9",2:"\xB5m",3:"nm",4:"s",5:"min",6:"Hz",7:"kHz",8:"MHz",9:"m/z",10:"Da",11:"ppm",12:"days",13:"years",14:"Raman shift (cm\u207B\xB9)",15:"eV",16:"Text label",255:"Double interferogram"},Ar={0:"Arbitrary",1:"Interferogram",2:"Absorbance",3:"Kubelka-Munk",4:"Counts",5:"V",6:"\xB0",7:"mA",8:"mm",9:"mV",10:"log(1/R)",11:"%",12:"Intensity",13:"Relative intensity",14:"Energy",16:"dB",19:"\xB0C",20:"\xB0F",21:"K",22:"Index of refraction [n]",23:"Extinction coeff. [k]",24:"Real",25:"Imaginary",26:"Complex",128:"Transmittance",129:"Reflectance",130:"Arbitrary (Valley to peak)",131:"Emission"};function Dr(e,t){return e===1?"IR":e===14?"Raman":e===3&&(t===2||t===128)?"UV-Vis":e===2?"NIR":t===131?"fluorescence":"other"}function $t(e){let t=new DataView(e);if(e.byteLength<512)throw new Error("Invalid SPC file: too small for SPC header");let n=t.getUint8(0),o=t.getUint8(1);if(o!==75&&o!==77)throw new Error(`Unsupported SPC version: 0x${o.toString(16)}. Expected 0x4B or 0x4D.`);let i=t.getUint8(2),l=t.getUint8(3),s=t.getUint32(4,!0),c=t.getFloat64(8,!0),m=t.getFloat64(16,!0),u=t.getUint32(24,!0),p=Pr[i]??"Arbitrary",f=Ar[l]??"Arbitrary",a=new Uint8Array(e,30,130),d=Ir(a),g=(n&Mr)!==0,S=(n&Lr)!==0,b=(n&Er)!==0,C=Dr(i,l),v=null;if(!S&&s>0){v=new Float64Array(s);let x=s>1?(m-c)/(s-1):0;for(let T=0;T<s;T++)v[T]=c+T*x}let k=[],h=512,y=null;if(S&&!g){y=new Float64Array(s);for(let x=0;x<s;x++)y[x]=t.getFloat32(h,!0),h+=4}let w=g?u:1;for(let x=0;x<w;x++){let T,L,D=s;if(g){if(h+32>e.byteLength)break;let I=t.getFloat32(h+4,!0),Ve=t.getFloat32(h+8,!0);if(D=t.getUint32(h+12,!0)||s,h+=32,S){T=new Float64Array(D);for(let Y=0;Y<D&&!(h+4>e.byteLength);Y++)T[Y]=t.getFloat32(h,!0),h+=4}else if(v)T=v;else{T=new Float64Array(D);let Y=D>1?(Ve-I)/(D-1):0;for(let j=0;j<D;j++)T[j]=I+j*Y}}else T=y??v??new Float64Array(0);if(L=new Float64Array(D),b)for(let I=0;I<D&&!(h+2>e.byteLength);I++)L[I]=t.getInt16(h,!0),h+=2;else for(let I=0;I<D&&!(h+4>e.byteLength);I++)L[I]=t.getFloat32(h,!0),h+=4;k.push({id:`spc-${++Tr}`,label:d||`SPC Spectrum ${x+1}`,x:T,y:L,xUnit:p,yUnit:f,type:C,meta:{format:"SPC",version:o===75?"new":"old",xType:i.toString(),yType:l.toString()}})}if(k.length===0)throw new Error("Invalid SPC file: no spectra found");return k}function Ir(e){let t=e.indexOf(0),r=t>=0?e.slice(0,t):e;return new TextDecoder("ascii").decode(r).trim()}0&&(module.exports={AnnotationLayer,AxisLayer,Crosshair,DARK_THEME,DropZone,ExportMenu,KEYBOARD_SHORTCUTS,LIGHT_THEME,LINE_DASH_PATTERNS,Legend,PeakMarkers,RegionSelector,SPECTRUM_COLORS,SpectraView,SpectrumCanvas,StackedView,Toolbar,binarySearchClosest,computeXExtent,computeYExtent,createXScale,createYScale,detectPeaks,downloadSvg,generateChartDescription,generateSvg,getSpectrumColor,getThemeColors,lttbDownsample,parseCsv,parseCsvMulti,parseJcamp,parseJson,parseSpc,prefersReducedMotion,snapToNearestSpectrum,useExport,useKeyboardNavigation,usePeakPicking,useRegionSelect,useResizeObserver,useSpectrumData,useZoomPan});
7
16
  //# sourceMappingURL=index.cjs.map