spectraview 1.2.0 → 1.4.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/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -1
- package/dist/index.d.ts +79 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -402,6 +402,32 @@ interface MinimapProps {
|
|
|
402
402
|
}
|
|
403
403
|
declare const Minimap: react.NamedExoticComponent<MinimapProps>;
|
|
404
404
|
|
|
405
|
+
interface TooltipData {
|
|
406
|
+
/** Cursor pixel position. */
|
|
407
|
+
px: number;
|
|
408
|
+
py: number;
|
|
409
|
+
/** Cursor data-space position. */
|
|
410
|
+
dataX: number;
|
|
411
|
+
dataY: number;
|
|
412
|
+
}
|
|
413
|
+
interface TooltipProps {
|
|
414
|
+
/** Tooltip position, or null when hidden. */
|
|
415
|
+
data: TooltipData | null;
|
|
416
|
+
/** Spectra for multi-value readout. */
|
|
417
|
+
spectra: Spectrum[];
|
|
418
|
+
/** Peaks for nearest-peak indicator. */
|
|
419
|
+
peaks?: Peak[];
|
|
420
|
+
/** Plot area width. */
|
|
421
|
+
plotWidth: number;
|
|
422
|
+
/** Plot area height. */
|
|
423
|
+
plotHeight: number;
|
|
424
|
+
/** Theme colors. */
|
|
425
|
+
colors: ThemeColors;
|
|
426
|
+
/** Number format for values. Defaults to "auto". */
|
|
427
|
+
numberFormat?: "auto" | "fixed2" | "fixed4" | "scientific";
|
|
428
|
+
}
|
|
429
|
+
declare const Tooltip: react.NamedExoticComponent<TooltipProps>;
|
|
430
|
+
|
|
405
431
|
/**
|
|
406
432
|
* Hook for zoom and pan behavior backed by d3-zoom.
|
|
407
433
|
*
|
|
@@ -832,6 +858,58 @@ declare function derivative1st(x: Float64Array | number[], y: Float64Array | num
|
|
|
832
858
|
*/
|
|
833
859
|
declare function derivative2nd(x: Float64Array | number[], y: Float64Array | number[]): Float64Array;
|
|
834
860
|
|
|
861
|
+
/**
|
|
862
|
+
* Spectrum comparison and mathematical operations.
|
|
863
|
+
*
|
|
864
|
+
* Provides functions for computing difference spectra, correlation
|
|
865
|
+
* coefficients, spectral arithmetic, and residuals.
|
|
866
|
+
*
|
|
867
|
+
* All functions assume spectra share the same X-axis (same length
|
|
868
|
+
* and point spacing). Use `interpolateToGrid` to align spectra
|
|
869
|
+
* before comparison if needed.
|
|
870
|
+
*
|
|
871
|
+
* @module comparison
|
|
872
|
+
*/
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Compute the difference spectrum (a - b).
|
|
876
|
+
*
|
|
877
|
+
* @param a - First spectrum
|
|
878
|
+
* @param b - Second spectrum
|
|
879
|
+
* @returns New Spectrum representing a - b
|
|
880
|
+
*/
|
|
881
|
+
declare function differenceSpectrum(a: Spectrum, b: Spectrum): Spectrum;
|
|
882
|
+
/**
|
|
883
|
+
* Add two spectra element-wise.
|
|
884
|
+
*/
|
|
885
|
+
declare function addSpectra(a: Spectrum, b: Spectrum): Spectrum;
|
|
886
|
+
/**
|
|
887
|
+
* Multiply spectrum Y values by a scalar factor.
|
|
888
|
+
*/
|
|
889
|
+
declare function scaleSpectrum(spectrum: Spectrum, factor: number): Spectrum;
|
|
890
|
+
/**
|
|
891
|
+
* Pearson correlation coefficient between two spectra's Y values.
|
|
892
|
+
*
|
|
893
|
+
* Returns a value in [-1, 1] where 1 means perfect positive
|
|
894
|
+
* correlation and 0 means no linear correlation.
|
|
895
|
+
*/
|
|
896
|
+
declare function correlationCoefficient(a: Spectrum, b: Spectrum): number;
|
|
897
|
+
/**
|
|
898
|
+
* Compute the residual (absolute difference) between two spectra.
|
|
899
|
+
*/
|
|
900
|
+
declare function residualSpectrum(a: Spectrum, b: Spectrum): Spectrum;
|
|
901
|
+
/**
|
|
902
|
+
* Interpolate a spectrum to a new X grid using linear interpolation.
|
|
903
|
+
*
|
|
904
|
+
* Useful for aligning two spectra that have different X-axis points
|
|
905
|
+
* before performing comparison operations.
|
|
906
|
+
*
|
|
907
|
+
* @param spectrum - Source spectrum
|
|
908
|
+
* @param newX - Target X values
|
|
909
|
+
* @returns Spectrum interpolated to the new X grid
|
|
910
|
+
*/
|
|
911
|
+
declare function interpolateToGrid(spectrum: Spectrum, newX: Float64Array | number[]): Spectrum;
|
|
912
|
+
|
|
835
913
|
/**
|
|
836
914
|
* JCAMP-DX parser for spectral data.
|
|
837
915
|
*
|
|
@@ -953,4 +1031,4 @@ declare function createXScale(domain: [number, number], width: number, margin: M
|
|
|
953
1031
|
*/
|
|
954
1032
|
declare function createYScale(domain: [number, number], height: number, margin: Margin): d3_scale.ScaleLinear<number, number, never>;
|
|
955
1033
|
|
|
956
|
-
export { type Annotation, AnnotationLayer, type AnnotationLayerProps, AxisLayer, Crosshair, type CrosshairPosition, type CrosshairProps, type CsvParseOptions, DARK_THEME, type DisplayMode, DropZone, type DropZoneProps, ExportMenu, type ExportMenuProps, KEYBOARD_SHORTCUTS, LIGHT_THEME, LINE_DASH_PATTERNS, type LTTBPoint, Legend, type LegendPosition$1 as LegendPosition, type LegendProps, type LineStyle, type Margin, Minimap, type MinimapProps, type Peak, type PeakDetectionOptions, PeakMarkers, type Region, RegionSelector, type ResolvedConfig, SPECTRUM_COLORS, type SnapPoint, type SnapResult, SpectraView, type SpectraViewProps, type Spectrum, SpectrumCanvas, type SpectrumType, StackedView, type SvgExportOptions, type Theme, Toolbar, type UseExportReturn, type UseKeyboardNavigationOptions, type UsePeakPickingOptions, type UseRegionSelectOptions, type UseRegionSelectReturn, type UseSpectrumDataReturn, type UseZoomPanOptions, type UseZoomPanReturn, type ViewState, type ZoomPanState, baselineRubberBand, binarySearchClosest, computeXExtent, computeYExtent, createXScale, createYScale, derivative1st, derivative2nd, detectPeaks, downloadSvg, generateChartDescription, generateSvg, getSpectrumColor, getThemeColors, lttbDownsample, normalizeArea, normalizeMinMax, normalizeSNV, parseCsv, parseCsvMulti, parseJcamp, parseJson, parseSpc, prefersReducedMotion, smoothSavitzkyGolay, snapToNearestSpectrum, useExport, useKeyboardNavigation, usePeakPicking, useRegionSelect, useResizeObserver, useSpectrumData, useZoomPan };
|
|
1034
|
+
export { type Annotation, AnnotationLayer, type AnnotationLayerProps, AxisLayer, Crosshair, type CrosshairPosition, type CrosshairProps, type CsvParseOptions, DARK_THEME, type DisplayMode, DropZone, type DropZoneProps, ExportMenu, type ExportMenuProps, KEYBOARD_SHORTCUTS, LIGHT_THEME, LINE_DASH_PATTERNS, type LTTBPoint, Legend, type LegendPosition$1 as LegendPosition, type LegendProps, type LineStyle, type Margin, Minimap, type MinimapProps, type Peak, type PeakDetectionOptions, PeakMarkers, type Region, RegionSelector, type ResolvedConfig, SPECTRUM_COLORS, type SnapPoint, type SnapResult, SpectraView, type SpectraViewProps, type Spectrum, SpectrumCanvas, type SpectrumType, StackedView, type SvgExportOptions, type Theme, Toolbar, Tooltip, type TooltipData, type TooltipProps, type UseExportReturn, type UseKeyboardNavigationOptions, type UsePeakPickingOptions, type UseRegionSelectOptions, type UseRegionSelectReturn, type UseSpectrumDataReturn, type UseZoomPanOptions, type UseZoomPanReturn, type ViewState, type ZoomPanState, addSpectra, baselineRubberBand, binarySearchClosest, computeXExtent, computeYExtent, correlationCoefficient, createXScale, createYScale, derivative1st, derivative2nd, detectPeaks, differenceSpectrum, downloadSvg, generateChartDescription, generateSvg, getSpectrumColor, getThemeColors, interpolateToGrid, lttbDownsample, normalizeArea, normalizeMinMax, normalizeSNV, parseCsv, parseCsvMulti, parseJcamp, parseJson, parseSpc, prefersReducedMotion, residualSpectrum, scaleSpectrum, smoothSavitzkyGolay, snapToNearestSpectrum, useExport, useKeyboardNavigation, usePeakPicking, useRegionSelect, useResizeObserver, useSpectrumData, useZoomPan };
|
package/dist/index.d.ts
CHANGED
|
@@ -402,6 +402,32 @@ interface MinimapProps {
|
|
|
402
402
|
}
|
|
403
403
|
declare const Minimap: react.NamedExoticComponent<MinimapProps>;
|
|
404
404
|
|
|
405
|
+
interface TooltipData {
|
|
406
|
+
/** Cursor pixel position. */
|
|
407
|
+
px: number;
|
|
408
|
+
py: number;
|
|
409
|
+
/** Cursor data-space position. */
|
|
410
|
+
dataX: number;
|
|
411
|
+
dataY: number;
|
|
412
|
+
}
|
|
413
|
+
interface TooltipProps {
|
|
414
|
+
/** Tooltip position, or null when hidden. */
|
|
415
|
+
data: TooltipData | null;
|
|
416
|
+
/** Spectra for multi-value readout. */
|
|
417
|
+
spectra: Spectrum[];
|
|
418
|
+
/** Peaks for nearest-peak indicator. */
|
|
419
|
+
peaks?: Peak[];
|
|
420
|
+
/** Plot area width. */
|
|
421
|
+
plotWidth: number;
|
|
422
|
+
/** Plot area height. */
|
|
423
|
+
plotHeight: number;
|
|
424
|
+
/** Theme colors. */
|
|
425
|
+
colors: ThemeColors;
|
|
426
|
+
/** Number format for values. Defaults to "auto". */
|
|
427
|
+
numberFormat?: "auto" | "fixed2" | "fixed4" | "scientific";
|
|
428
|
+
}
|
|
429
|
+
declare const Tooltip: react.NamedExoticComponent<TooltipProps>;
|
|
430
|
+
|
|
405
431
|
/**
|
|
406
432
|
* Hook for zoom and pan behavior backed by d3-zoom.
|
|
407
433
|
*
|
|
@@ -832,6 +858,58 @@ declare function derivative1st(x: Float64Array | number[], y: Float64Array | num
|
|
|
832
858
|
*/
|
|
833
859
|
declare function derivative2nd(x: Float64Array | number[], y: Float64Array | number[]): Float64Array;
|
|
834
860
|
|
|
861
|
+
/**
|
|
862
|
+
* Spectrum comparison and mathematical operations.
|
|
863
|
+
*
|
|
864
|
+
* Provides functions for computing difference spectra, correlation
|
|
865
|
+
* coefficients, spectral arithmetic, and residuals.
|
|
866
|
+
*
|
|
867
|
+
* All functions assume spectra share the same X-axis (same length
|
|
868
|
+
* and point spacing). Use `interpolateToGrid` to align spectra
|
|
869
|
+
* before comparison if needed.
|
|
870
|
+
*
|
|
871
|
+
* @module comparison
|
|
872
|
+
*/
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Compute the difference spectrum (a - b).
|
|
876
|
+
*
|
|
877
|
+
* @param a - First spectrum
|
|
878
|
+
* @param b - Second spectrum
|
|
879
|
+
* @returns New Spectrum representing a - b
|
|
880
|
+
*/
|
|
881
|
+
declare function differenceSpectrum(a: Spectrum, b: Spectrum): Spectrum;
|
|
882
|
+
/**
|
|
883
|
+
* Add two spectra element-wise.
|
|
884
|
+
*/
|
|
885
|
+
declare function addSpectra(a: Spectrum, b: Spectrum): Spectrum;
|
|
886
|
+
/**
|
|
887
|
+
* Multiply spectrum Y values by a scalar factor.
|
|
888
|
+
*/
|
|
889
|
+
declare function scaleSpectrum(spectrum: Spectrum, factor: number): Spectrum;
|
|
890
|
+
/**
|
|
891
|
+
* Pearson correlation coefficient between two spectra's Y values.
|
|
892
|
+
*
|
|
893
|
+
* Returns a value in [-1, 1] where 1 means perfect positive
|
|
894
|
+
* correlation and 0 means no linear correlation.
|
|
895
|
+
*/
|
|
896
|
+
declare function correlationCoefficient(a: Spectrum, b: Spectrum): number;
|
|
897
|
+
/**
|
|
898
|
+
* Compute the residual (absolute difference) between two spectra.
|
|
899
|
+
*/
|
|
900
|
+
declare function residualSpectrum(a: Spectrum, b: Spectrum): Spectrum;
|
|
901
|
+
/**
|
|
902
|
+
* Interpolate a spectrum to a new X grid using linear interpolation.
|
|
903
|
+
*
|
|
904
|
+
* Useful for aligning two spectra that have different X-axis points
|
|
905
|
+
* before performing comparison operations.
|
|
906
|
+
*
|
|
907
|
+
* @param spectrum - Source spectrum
|
|
908
|
+
* @param newX - Target X values
|
|
909
|
+
* @returns Spectrum interpolated to the new X grid
|
|
910
|
+
*/
|
|
911
|
+
declare function interpolateToGrid(spectrum: Spectrum, newX: Float64Array | number[]): Spectrum;
|
|
912
|
+
|
|
835
913
|
/**
|
|
836
914
|
* JCAMP-DX parser for spectral data.
|
|
837
915
|
*
|
|
@@ -953,4 +1031,4 @@ declare function createXScale(domain: [number, number], width: number, margin: M
|
|
|
953
1031
|
*/
|
|
954
1032
|
declare function createYScale(domain: [number, number], height: number, margin: Margin): d3_scale.ScaleLinear<number, number, never>;
|
|
955
1033
|
|
|
956
|
-
export { type Annotation, AnnotationLayer, type AnnotationLayerProps, AxisLayer, Crosshair, type CrosshairPosition, type CrosshairProps, type CsvParseOptions, DARK_THEME, type DisplayMode, DropZone, type DropZoneProps, ExportMenu, type ExportMenuProps, KEYBOARD_SHORTCUTS, LIGHT_THEME, LINE_DASH_PATTERNS, type LTTBPoint, Legend, type LegendPosition$1 as LegendPosition, type LegendProps, type LineStyle, type Margin, Minimap, type MinimapProps, type Peak, type PeakDetectionOptions, PeakMarkers, type Region, RegionSelector, type ResolvedConfig, SPECTRUM_COLORS, type SnapPoint, type SnapResult, SpectraView, type SpectraViewProps, type Spectrum, SpectrumCanvas, type SpectrumType, StackedView, type SvgExportOptions, type Theme, Toolbar, type UseExportReturn, type UseKeyboardNavigationOptions, type UsePeakPickingOptions, type UseRegionSelectOptions, type UseRegionSelectReturn, type UseSpectrumDataReturn, type UseZoomPanOptions, type UseZoomPanReturn, type ViewState, type ZoomPanState, baselineRubberBand, binarySearchClosest, computeXExtent, computeYExtent, createXScale, createYScale, derivative1st, derivative2nd, detectPeaks, downloadSvg, generateChartDescription, generateSvg, getSpectrumColor, getThemeColors, lttbDownsample, normalizeArea, normalizeMinMax, normalizeSNV, parseCsv, parseCsvMulti, parseJcamp, parseJson, parseSpc, prefersReducedMotion, smoothSavitzkyGolay, snapToNearestSpectrum, useExport, useKeyboardNavigation, usePeakPicking, useRegionSelect, useResizeObserver, useSpectrumData, useZoomPan };
|
|
1034
|
+
export { type Annotation, AnnotationLayer, type AnnotationLayerProps, AxisLayer, Crosshair, type CrosshairPosition, type CrosshairProps, type CsvParseOptions, DARK_THEME, type DisplayMode, DropZone, type DropZoneProps, ExportMenu, type ExportMenuProps, KEYBOARD_SHORTCUTS, LIGHT_THEME, LINE_DASH_PATTERNS, type LTTBPoint, Legend, type LegendPosition$1 as LegendPosition, type LegendProps, type LineStyle, type Margin, Minimap, type MinimapProps, type Peak, type PeakDetectionOptions, PeakMarkers, type Region, RegionSelector, type ResolvedConfig, SPECTRUM_COLORS, type SnapPoint, type SnapResult, SpectraView, type SpectraViewProps, type Spectrum, SpectrumCanvas, type SpectrumType, StackedView, type SvgExportOptions, type Theme, Toolbar, Tooltip, type TooltipData, type TooltipProps, type UseExportReturn, type UseKeyboardNavigationOptions, type UsePeakPickingOptions, type UseRegionSelectOptions, type UseRegionSelectReturn, type UseSpectrumDataReturn, type UseZoomPanOptions, type UseZoomPanReturn, type ViewState, type ZoomPanState, addSpectra, baselineRubberBand, binarySearchClosest, computeXExtent, computeYExtent, correlationCoefficient, createXScale, createYScale, derivative1st, derivative2nd, detectPeaks, differenceSpectrum, downloadSvg, generateChartDescription, generateSvg, getSpectrumColor, getThemeColors, interpolateToGrid, lttbDownsample, normalizeArea, normalizeMinMax, normalizeSNV, parseCsv, parseCsvMulti, parseJcamp, parseJson, parseSpc, prefersReducedMotion, residualSpectrum, scaleSpectrum, smoothSavitzkyGolay, snapToNearestSpectrum, useExport, useKeyboardNavigation, usePeakPicking, useRegionSelect, useResizeObserver, useSpectrumData, useZoomPan };
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{useCallback as kt,useId as Tr,useMemo as I,useRef as wt,useState as _e}from"react";import{scaleLinear as lt}from"d3-scale";import{extent as ct}from"d3-array";var Xt=.05;function ge(e){let t=1/0,r=-1/0;for(let o of e){if(o.visible===!1)continue;let[n,a]=ct(o.x);n<t&&(t=n),a>r&&(r=a)}return isFinite(t)?[t,r]:[0,1]}function H(e){let t=1/0,r=-1/0;for(let a of e){if(a.visible===!1)continue;let[s,i]=ct(a.y);s<t&&(t=s),i>r&&(r=i)}if(!isFinite(t))return[0,1];let n=(r-t)*Xt;return[t-n,r+n]}function he(e,t,r,o){let n=t-r.left-r.right,a=o?[e[1],e[0]]:e;return lt().domain(a).range([0,n])}function X(e,t,r){let o=t-r.top-r.bottom;return lt().domain(e).range([o,0])}var ye=["#2563eb","#dc2626","#16a34a","#9333ea","#ea580c","#0891b2","#be185d","#854d0e","#4f46e5","#65a30d"],mt={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"},ut={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 P(e){return ye[e%ye.length]}function z(e){return e==="dark"?ut:mt}import{useCallback as xe,useEffect as Wt,useMemo as pt,useRef as te,useState as Yt}from"react";import{zoom as jt,zoomIdentity as Se}from"d3-zoom";import{select as V}from"d3-selection";import"d3-transition";var ft=1.5;function ve(e){let{plotWidth:t,plotHeight:r,xScale:o,yScale:n,scaleExtent:a=[1,50],enabled:s=!0,onViewChange:i}=e,l=te(null),c=te(null),u=te(i);u.current=i;let p=te(a);p.current=a;let[f,m]=Yt(Se),d=pt(()=>f.rescaleX(o.copy()),[f,o]),g=pt(()=>f.rescaleY(n.copy()),[f,n]);Wt(()=>{let S=l.current;if(!S||!s)return;let w=jt().scaleExtent(p.current).extent([[0,0],[t,r]]).translateExtent([[-1/0,-1/0],[1/0,1/0]]).on("zoom",h=>{let v=h.transform;if(m(v),u.current){let C=v.rescaleX(o.copy()),k=v.rescaleY(n.copy());u.current(C.domain(),k.domain())}});return c.current=w,V(S).call(w),V(S).on("dblclick.zoom",()=>{V(S).transition().duration(300).call(w.transform,Se)}),()=>{V(S).on(".zoom",null)}},[t,r,s,o,n]);let y=xe(()=>{!l.current||!c.current||V(l.current).transition().duration(300).call(c.current.transform,Se)},[]),b=xe(()=>{!l.current||!c.current||V(l.current).transition().duration(200).call(c.current.scaleBy,ft)},[]),x=xe(()=>{!l.current||!c.current||V(l.current).transition().duration(200).call(c.current.scaleBy,1/ft)},[]);return{zoomRef:l,state:{transform:f,isZoomed:f.k!==1||f.x!==0||f.y!==0},zoomedXScale:d,zoomedYScale:g,resetZoom:y,zoomIn:b,zoomOut:x}}import{forwardRef as er,useEffect as bt,useImperativeHandle as tr,useRef as gt}from"react";function Ce(e,t,r,o,n,a,s){let i=o-r;if(i<=s){let f=[];for(let m=r;m<o;m++)f.push({px:n(e[m]),py:a(t[m]),index:m});return f}let l=[];l.push({px:n(e[r]),py:a(t[r]),index:r});let c=s-2,u=(i-2)/c,p=r;for(let f=0;f<c;f++){let m=r+1+Math.floor(f*u),d=r+1+Math.min(Math.floor((f+1)*u),i-2),g=d,y=r+1+Math.min(Math.floor((f+2)*u),i-2),b,x;if(f===c-1)b=n(e[o-1]),x=a(t[o-1]);else{b=0,x=0;let C=y-g;for(let k=g;k<y;k++)b+=n(e[k]),x+=a(t[k]);C>0&&(b/=C,x/=C)}let S=n(e[p]),w=a(t[p]),h=-1,v=m;for(let C=m;C<d;C++){let k=n(e[C]),R=a(t[C]),T=Math.abs((S-b)*(R-w)-(S-k)*(x-w));T>h&&(h=T,v=C)}l.push({px:n(e[v]),py:a(t[v]),index:v}),p=v}return l.push({px:n(e[o-1]),py:a(t[o-1]),index:o-1}),l}var Gt=1.5,Kt={solid:[],dashed:[8,4],dotted:[2,2],"dash-dot":[8,4,2,4]},Jt=2e3;function qt(e,t,r){e.clearRect(0,0,t,r)}function Qt(e,t,r,o,n,a,s){let{highlighted:i=!1,opacity:l=1}=s??{},c=Math.min(t.x.length,t.y.length);if(c<2)return;let u=t.color??P(r),p=t.lineWidth??Gt,f=i?p+1:p,m=Kt[t.lineStyle??"solid"]??[],[d,g]=o.domain(),y=Math.min(d,g),b=Math.max(d,g),x=0,S=c;for(let h=0;h<c;h++)if(t.x[h]>=y||h<c-1&&t.x[h+1]>=y){x=Math.max(0,h-1);break}for(let h=c-1;h>=0;h--)if(t.x[h]<=b||h>0&&t.x[h-1]<=b){S=Math.min(c,h+2);break}let w=S-x;if(e.save(),e.beginPath(),e.strokeStyle=u,e.lineWidth=f,e.globalAlpha=l,e.lineJoin="round",e.setLineDash(m),w>Jt){let h=Math.max(Math.ceil(a*2),200),v=Ce(t.x,t.y,x,S,o,n,h);if(v.length>0){e.moveTo(v[0].px,v[0].py);for(let C=1;C<v.length;C++)e.lineTo(v[C].px,v[C].py)}}else{let h=!1;for(let v=x;v<S;v++){let C=o(t.x[v]),k=n(t.y[v]);h?e.lineTo(C,k):(e.moveTo(C,k),h=!0)}}e.stroke(),e.restore()}function dt(e,t,r,o,n,a,s){qt(e,n,a),t.forEach((i,l)=>{i.visible!==!1&&Qt(e,i,l,r,o,n,{highlighted:i.id===s,opacity:s&&i.id!==s?.3:1})})}import{jsx as rr}from"react/jsx-runtime";var W=er(function({spectra:t,xScale:r,yScale:o,width:n,height:a,highlightedId:s},i){let l=gt(null),c=gt(1);return tr(i,()=>l.current,[]),bt(()=>{let u=l.current;if(!u)return;let p=window.devicePixelRatio||1;c.current=p,u.width=n*p,u.height=a*p},[n,a]),bt(()=>{let u=l.current;if(!u)return;let p=u.getContext("2d");if(!p)return;let f=c.current;p.setTransform(f,0,0,f,0,0),dt(p,t,r,o,n,a,s)},[t,r,o,n,a,s]),rr("canvas",{ref:l,style:{width:n,height:a,position:"absolute",top:0,left:0,pointerEvents:"none"}})});import{jsx as D,jsxs as Z}from"react/jsx-runtime";function ht(e,t){let[r,o]=e.domain(),n=Math.min(r,o),s=(Math.max(r,o)-n)/(t-1);return Array.from({length:t},(i,l)=>n+l*s)}function yt(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:r,height:o,xLabel:n,yLabel:a,showGrid:s=!0,colors:i}){let l=ht(e,8),c=ht(t,6);return Z("g",{children:[s&&Z("g",{children:[l.map(u=>D("line",{x1:e(u),x2:e(u),y1:0,y2:o,stroke:i.gridColor,strokeWidth:.5},`xgrid-${u}`)),c.map(u=>D("line",{x1:0,x2:r,y1:t(u),y2:t(u),stroke:i.gridColor,strokeWidth:.5},`ygrid-${u}`))]}),Z("g",{transform:`translate(0, ${o})`,children:[D("line",{x1:0,x2:r,y1:0,y2:0,stroke:i.axisColor}),l.map(u=>Z("g",{transform:`translate(${e(u)}, 0)`,children:[D("line",{y1:0,y2:6,stroke:i.axisColor}),D("text",{y:20,textAnchor:"middle",fill:i.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:yt(u)})]},`xtick-${u}`)),n&&D("text",{x:r/2,y:42,textAnchor:"middle",fill:i.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:n})]}),Z("g",{children:[D("line",{x1:0,x2:0,y1:0,y2:o,stroke:i.axisColor}),c.map(u=>Z("g",{transform:`translate(0, ${t(u)})`,children:[D("line",{x1:-6,x2:0,stroke:i.axisColor}),D("text",{x:-10,textAnchor:"end",dominantBaseline:"middle",fill:i.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:yt(u)})]},`ytick-${u}`)),a&&D("text",{transform:`translate(-50, ${o/2}) rotate(-90)`,textAnchor:"middle",fill:i.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:a})]})]})}import{jsx as ke,jsxs as nr}from"react/jsx-runtime";function we({peaks:e,xScale:t,yScale:r,colors:o,onPeakClick:n}){let[a,s]=t.domain(),i=Math.min(a,s),l=Math.max(a,s),c=e.filter(u=>u.x>=i&&u.x<=l);return ke("g",{className:"spectraview-peaks",children:c.map((u,p)=>{let f=t(u.x),m=r(u.y);return nr("g",{transform:`translate(${f}, ${m})`,style:{cursor:n?"pointer":"default"},onClick:()=>n?.(u),children:[ke("polygon",{points:`0,-5 -5,${-5*2.5} 5,${-5*2.5}`,fill:o.labelColor,opacity:.8}),u.label&&ke("text",{y:-5*2.5-14,textAnchor:"middle",fill:o.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",fontWeight:500,children:u.label})]},`peak-${u.x}-${p}`)})})}import{jsx as Re,jsxs as or}from"react/jsx-runtime";function Me({regions:e,xScale:t,height:r,colors:o}){return Re("g",{className:"spectraview-regions",children:e.map((n,a)=>{let s=t(n.xStart),i=t(n.xEnd),l=Math.min(s,i),c=Math.abs(i-s);return or("g",{children:[Re("rect",{x:l,y:0,width:c,height:r,fill:n.color??o.regionFill,stroke:o.regionStroke,strokeWidth:1}),n.label&&Re("text",{x:l+c/2,y:12,textAnchor:"middle",fill:o.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",children:n.label})]},`region-${a}`)})})}import{jsx as re,jsxs as Te}from"react/jsx-runtime";function Ae({position:e,width:t,height:r,colors:o,snapPoint:n}){return e?Te("g",{className:"spectraview-crosshair",pointerEvents:"none",children:[re("line",{x1:e.px,x2:e.px,y1:0,y2:r,stroke:o.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),re("line",{x1:0,x2:t,y1:e.py,y2:e.py,stroke:o.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),n&&re("circle",{cx:n.px,cy:n.py,r:4,fill:n.color??o.crosshairColor,stroke:o.background,strokeWidth:1.5}),Te("g",{transform:`translate(${Math.min(e.px+10,t-100)}, ${Math.max(e.py-10,20)})`,children:[re("rect",{x:0,y:-14,width:90,height:18,rx:3,fill:o.tooltipBg,stroke:o.tooltipBorder,strokeWidth:.5,opacity:.9}),Te("text",{x:5,y:0,fill:o.tooltipText,fontSize:10,fontFamily:"monospace",children:[xt(n?.dataX??e.dataX),","," ",xt(n?.dataY??e.dataY)]})]})]}):null}function xt(e){return Math.abs(e)>=100?Math.round(e).toString():Math.abs(e)>=1?e.toFixed(1):e.toFixed(4)}import{jsx as j,jsxs as ir}from"react/jsx-runtime";function Ee({annotations:e,xScale:t,yScale:r,colors:o}){return e.length===0?null:j("g",{className:"spectraview-annotations",pointerEvents:"none",children:e.map(n=>{let a=t(n.x),s=r(n.y),[i,l]=n.offset??[0,-20],c=a+i,u=s+l,p=n.fontSize??11,f=n.color??o.tickColor,m=n.showAnchorLine!==!1;return ir("g",{children:[m&&j("line",{x1:a,y1:s,x2:c,y2:u,stroke:f,strokeWidth:.75,strokeDasharray:"3 2",opacity:.6}),j("circle",{cx:a,cy:s,r:2.5,fill:f,opacity:.8}),j("text",{x:c,y:u,fill:o.background,fontSize:p,fontFamily:"system-ui, sans-serif",textAnchor:"middle",dominantBaseline:"auto",stroke:o.background,strokeWidth:3,strokeLinejoin:"round",children:n.text}),j("text",{x:c,y:u,fill:f,fontSize:p,fontFamily:"system-ui, sans-serif",textAnchor:"middle",dominantBaseline:"auto",children:n.text})]},n.id)})})}function St(e,t,r){if(r===0)return-1;if(r===1)return 0;let o=e[r-1]>=e[0],n=0,a=r-1;for(;n<a-1;){let l=n+a>>>1,c=e[l];o?c<=t?n=l:a=l:c>=t?n=l:a=l}let s=Math.abs(e[n]-t),i=Math.abs(e[a]-t);return s<=i?n:a}function Le(e,t,r,o,n){let a=null;for(let s of e){if(s.visible===!1)continue;let i=Math.min(s.x.length,s.y.length);if(i<2)continue;let l=St(s.x,t,i);if(l<0)continue;let c=s.x[l],u=s.y[l],p=Math.abs(o(c)-o(t)),f=Math.abs(n(u)-r),m=Math.sqrt(p*p+f*f);(!a||m<a.distance)&&(a={spectrumId:s.id,index:l,x:c,y:u,distance:m})}return a}import{memo as ar}from"react";import{jsx as De,jsxs as lr}from"react/jsx-runtime";var Pe=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"}`}),Fe=ar(function({onZoomIn:t,onZoomOut:r,onReset:o,isZoomed:n,theme:a}){return lr("div",{style:sr(a),className:"spectraview-toolbar",children:[De("button",{type:"button",style:Pe(a),onClick:t,title:"Zoom in","aria-label":"Zoom in",children:"+"}),De("button",{type:"button",style:Pe(a),onClick:r,title:"Zoom out","aria-label":"Zoom out",children:"\u2212"}),De("button",{type:"button",style:{...Pe(a),opacity:n?1:.4},onClick:o,disabled:!n,title:"Reset zoom","aria-label":"Reset zoom",children:"\u21BA"})]})});import{memo as cr}from"react";import{jsx as Ie,jsxs as fr}from"react/jsx-runtime";var mr=(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}),ur=(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"}),pr=(e,t)=>({width:12,height:3,borderRadius:1,background:e,opacity:t?.4:1,flexShrink:0}),ne=cr(function({spectra:t,theme:r,position:o,onToggleVisibility:n,onHighlight:a,highlightedId:s}){return t.length<=1?null:Ie("div",{className:"spectraview-legend",style:mr(r,o),role:"list","aria-label":"Spectrum legend",children:t.map((i,l)=>{let c=i.color??P(l),u=i.visible===!1,p=s===i.id;return fr("div",{role:"listitem",style:ur(r,u,p),onClick:()=>n?.(i.id),onMouseEnter:()=>a?.(i.id),onMouseLeave:()=>a?.(null),title:u?`Show ${i.label}`:`Hide ${i.label}`,children:[Ie("span",{style:pr(c,u)}),Ie("span",{style:{textDecoration:u?"line-through":"none",maxWidth:120,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:i.label})]},i.id)})})});import{useCallback as oe,useState as dr}from"react";import{jsx as br,jsxs as gr}from"react/jsx-runtime";function Ue({enabled:e,theme:t,width:r,height:o,onDrop:n,children:a}){let[s,i]=dr(!1),l={current:0},c=oe(m=>{e&&(m.preventDefault(),l.current++,i(!0))},[e]),u=oe(m=>{e&&(m.preventDefault(),l.current--,l.current<=0&&(l.current=0,i(!1)))},[e]),p=oe(m=>{e&&(m.preventDefault(),m.dataTransfer.dropEffect="copy")},[e]),f=oe(m=>{if(!e)return;m.preventDefault(),l.current=0,i(!1);let d=Array.from(m.dataTransfer.files);d.length>0&&n?.(d)},[e,n]);return gr("div",{style:{position:"relative",width:r,height:o},onDragEnter:c,onDragLeave:u,onDragOver:p,onDrop:f,children:[a,s&&br("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"})]})}import{useMemo as hr}from"react";import{jsx as _,jsxs as yr}from"react/jsx-runtime";var vt=8;function $e({spectra:e,xScale:t,plotWidth:r,plotHeight:o,margin:n,theme:a,showGrid:s,xLabel:i,yLabel:l}){let c=e.filter(d=>d.visible!==!1),u=hr(()=>z(a),[a]),p=c.length,f=(p-1)*vt,m=Math.max(40,Math.floor((o-f)/Math.max(p,1)));return _("g",{className:"spectraview-stacked",children:c.map((d,g)=>{let y=g*(m+vt),b=H([d]),x=X(b,m+n.top+n.bottom,{...n,top:0,bottom:0}),S=d.color??P(g),w={...d,color:S};return yr("g",{transform:`translate(0, ${y})`,children:[_("rect",{x:0,y:0,width:r,height:m,fill:"transparent",stroke:u.gridColor,strokeWidth:.5,rx:2}),_(Y,{xScale:t,yScale:x,width:r,height:m,xLabel:g===p-1?i:"",yLabel:l,showGrid:s,colors:u}),_("text",{x:4,y:14,fill:S,fontSize:11,fontFamily:"system-ui, sans-serif",fontWeight:500,children:d.label}),_("foreignObject",{x:0,y:0,width:r,height:m,children:_(W,{spectra:[w],xScale:t,yScale:x,width:r,height:m})})]},d.id)})})}import{useCallback as Ne,useRef as xr,useState as Sr}from"react";function Oe(e){let{enabled:t,xScale:r,onRegionSelect:o}=e,[n,a]=Sr(null),s=xr(null),i=Ne(u=>{if(!t||!u.shiftKey)return;u.preventDefault();let p=u.currentTarget.getBoundingClientRect(),f=u.clientX-p.left,m=r.invert(f);s.current=m,a({xStart:m,xEnd:m})},[t,r]),l=Ne(u=>{if(s.current===null)return;let p=u.currentTarget.getBoundingClientRect(),f=u.clientX-p.left,m=r.invert(f),d=s.current;a({xStart:Math.min(d,m),xEnd:Math.max(d,m)})},[r]),c=Ne(()=>{if(s.current===null||!n)return;Math.abs(n.xEnd-n.xStart)>0&&o?.(n),s.current=null,a(null)},[n,o]);return{pendingRegion:n,handleMouseDown:i,handleMouseMove:l,handleMouseUp:c}}import{useCallback as vr,useEffect as Cr,useRef as Ct,useState as kr}from"react";function ze(){let[e,t]=kr(null),r=Ct(null),o=Ct(null),n=vr(a=>{if(r.current&&(r.current.disconnect(),r.current=null),o.current=a,!a)return;let s=new ResizeObserver(c=>{let u=c[0];if(!u)return;let{width:p,height:f}=u.contentRect;t({width:Math.round(p),height:Math.round(f)})});s.observe(a),r.current=s;let{width:i,height:l}=a.getBoundingClientRect();t({width:Math.round(i),height:Math.round(l)})},[]);return Cr(()=>()=>{r.current?.disconnect()},[]),{ref:n,size:e}}import{useCallback as wr}from"react";function Ve(e){let{onZoomIn:t,onZoomOut:r,onReset:o,enabled:n=!0}=e;return wr(s=>{if(n)switch(s.key){case"+":case"=":s.preventDefault(),t();break;case"-":s.preventDefault(),r();break;case"Escape":s.preventDefault(),o();break}},[n,t,r,o])}function Rr(){return typeof window>"u"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches}function Ze(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 Mr={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"};import{Fragment as Ir,jsx as M,jsxs as G}from"react/jsx-runtime";var Ar={top:20,right:20,bottom:50,left:65},Er=800,Lr=400;function Pr(e){return{width:e.width??Er,height:e.height??Lr,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:{...Ar,...e.margin},theme:e.theme??"light",responsive:e.responsive??!1,enableDragDrop:e.enableDragDrop??!1,enableRegionSelect:e.enableRegionSelect??!1}}function Dr(e,t,r){let o=e[0];return{xLabel:t??o?.xUnit??"x",yLabel:r??o?.yUnit??"y"}}function Fr(e){let{spectra:t,peaks:r=[],regions:o=[],annotations:n=[],onPeakClick:a,onViewChange:s,onCrosshairMove:i,onToggleVisibility:l,onFileDrop:c,onRegionSelect:u,canvasRef:p,snapCrosshair:f=!0}=e,{ref:m,size:d}=ze(),y=`spectraview-clip-${Tr().replace(/:/g,"")}`,b=I(()=>Pr(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]),x=b.responsive&&d?d.width:b.width,{height:S,margin:w,reverseX:h,theme:v}=b,C=x-w.left-w.right,k=S-w.top-w.bottom,R=I(()=>z(v),[v]),T=I(()=>Dr(t,e.xLabel,e.yLabel),[t,e.xLabel,e.yLabel]),E=I(()=>ge(t),[t]),L=I(()=>H(t),[t]),me=I(()=>he(E,x,w,h),[E,x,w,h]),N=I(()=>X(L,S,w),[L,S,w]),O=wt(s);O.current=s;let It=I(()=>($,Q)=>{O.current?.({xDomain:$,yDomain:Q})},[]),{zoomRef:qe,state:Ut,zoomedXScale:A,zoomedYScale:U,resetZoom:Qe,zoomIn:et,zoomOut:tt}=ve({plotWidth:C,plotHeight:k,xScale:me,yScale:N,onViewChange:s?It:void 0}),{pendingRegion:J,handleMouseDown:$t,handleMouseMove:Nt,handleMouseUp:Ot}=Oe({enabled:b.enableRegionSelect,xScale:A,onRegionSelect:u}),[ue,rt]=_e(null),[zt,nt]=_e(null),[Vt,pe]=_e(null),q=wt(i);q.current=i;let ot=kt($=>{if(!b.showCrosshair)return;let Q=$.currentTarget.getBoundingClientRect(),at=$.clientX-Q.left,de=$.clientY-Q.top,ee=A.invert(at),be=U.invert(de);if(nt({px:at,py:de,dataX:ee,dataY:be}),f&&t.length>0){let F=Le(t,ee,de,A,U);if(F&&F.distance<50){let st=t.findIndex(Ht=>Ht.id===F.spectrumId);pe({px:A(F.x),py:U(F.y),dataX:F.x,dataY:F.y,color:t[st]?.color??P(st)}),q.current?.(F.x,F.y)}else pe(null),q.current?.(ee,be)}else q.current?.(ee,be)},[A,U,b.showCrosshair,f,t]),it=kt(()=>{nt(null),pe(null)},[]),Zt=Ve({onZoomIn:et,onZoomOut:tt,onReset:Qe}),_t=I(()=>Ze(t.length,T.xLabel,T.yLabel),[t.length,T.xLabel,T.yLabel]),Bt=b.displayMode==="stacked";if(t.length===0)return M("div",{ref:b.responsive?m:void 0,style:{width:b.responsive?"100%":x,height:S,display:"flex",alignItems:"center",justifyContent:"center",border:`1px dashed ${R.gridColor}`,borderRadius:8,color:R.tickColor,fontFamily:"system-ui, sans-serif",fontSize:14},className:e.className,children:"No spectra loaded"});let fe=b.showToolbar?37:0;return G("div",{ref:b.responsive?m:void 0,style:{width:b.responsive?"100%":x,background:R.background,borderRadius:4,overflow:"hidden"},className:e.className,role:"img","aria-label":_t,tabIndex:0,onKeyDown:Zt,children:[b.showToolbar&&M(Fe,{onZoomIn:et,onZoomOut:tt,onReset:Qe,isZoomed:Ut.isZoomed,theme:v}),b.showLegend&&b.legendPosition==="top"&&M(ne,{spectra:t,theme:v,position:"top",onToggleVisibility:l,onHighlight:rt,highlightedId:ue}),M(Ue,{enabled:b.enableDragDrop,theme:v,width:x,height:S-fe,onDrop:c,children:Bt?M("svg",{width:x,height:S-fe,style:{position:"absolute",top:0,left:0},children:G("g",{transform:`translate(${w.left}, ${w.top})`,children:[M($e,{spectra:t,xScale:A,plotWidth:C,plotHeight:k,margin:w,theme:v,showGrid:b.showGrid,xLabel:T.xLabel,yLabel:T.yLabel}),M("rect",{ref:qe,x:0,y:0,width:C,height:k,fill:"transparent",style:{cursor:"grab"},onMouseMove:ot,onMouseLeave:it})]})}):G(Ir,{children:[M("div",{style:{position:"absolute",top:w.top,left:w.left,width:C,height:k,overflow:"hidden"},children:M(W,{ref:p,spectra:t,xScale:A,yScale:U,width:C,height:k,highlightedId:ue??void 0})}),M("svg",{width:x,height:S-fe,style:{position:"absolute",top:0,left:0},children:G("g",{transform:`translate(${w.left}, ${w.top})`,children:[M(Y,{xScale:A,yScale:U,width:C,height:k,xLabel:T.xLabel,yLabel:T.yLabel,showGrid:b.showGrid,colors:R}),M("defs",{children:M("clipPath",{id:y,children:M("rect",{x:0,y:0,width:C,height:k})})}),G("g",{clipPath:`url(#${y})`,children:[o.length>0&&M(Me,{regions:o,xScale:A,height:k,colors:R}),r.length>0&&M(we,{peaks:r,xScale:A,yScale:U,colors:R,onPeakClick:a})]}),n.length>0&&M(Ee,{annotations:n,xScale:A,yScale:U,colors:R}),b.showCrosshair&&M(Ae,{position:zt,width:C,height:k,colors:R,snapPoint:Vt}),J&&M("rect",{x:A(J.xStart),y:0,width:Math.abs(A(J.xEnd)-A(J.xStart)),height:k,fill:R.regionFill,stroke:R.regionStroke,strokeWidth:1,pointerEvents:"none"}),M("rect",{ref:qe,x:0,y:0,width:C,height:k,fill:"transparent",style:{cursor:b.showCrosshair?"crosshair":"grab"},onMouseDown:$t,onMouseMove:$=>{ot($),Nt($)},onMouseUp:Ot,onMouseLeave:it})]})})]})}),b.showLegend&&b.legendPosition==="bottom"&&M(ne,{spectra:t,theme:v,position:"bottom",onToggleVisibility:l,onHighlight:rt,highlightedId:ue})]})}import{memo as Ur,useEffect as $r,useRef as Nr,useMemo as Be}from"react";import{scaleLinear as Rt}from"d3-scale";import{jsx as ie,jsxs as Mt}from"react/jsx-runtime";var Or=Ur(function({spectra:t,xExtent:r,yExtent:o,visibleXDomain:n,width:a=200,height:s=50,theme:i="light",isZoomed:l=!1}){let c=Nr(null),u=Be(()=>z(i),[i]),p=Be(()=>Rt().domain(r).range([0,a]),[r,a]),f=Be(()=>Rt().domain(o).range([s-2,2]),[o,s]);$r(()=>{let y=c.current?.getContext("2d");if(y){y.clearRect(0,0,a,s);for(let b=0;b<t.length;b++){let x=t[b];if(x.visible===!1)continue;let S=Math.min(x.x.length,x.y.length);if(S<2)continue;let w=x.color??P(b);y.beginPath(),y.strokeStyle=w,y.lineWidth=1,y.globalAlpha=.7;let h=Math.max(1,Math.floor(S/a)),v=!1;for(let C=0;C<S;C+=h){let k=p(x.x[C]),R=f(x.y[C]);v?y.lineTo(k,R):(y.moveTo(k,R),v=!0)}y.stroke()}}},[t,p,f,a,s]);let m=p(Math.min(n[0],n[1])),d=p(Math.max(n[0],n[1])),g=Math.max(d-m,2);return Mt("div",{className:"spectraview-minimap",style:{position:"relative",width:a,height:s,border:`1px solid ${u.gridColor}`,borderRadius:3,overflow:"hidden",background:u.background},children:[ie("canvas",{ref:c,width:a,height:s,style:{position:"absolute",top:0,left:0}}),l&&Mt("svg",{width:a,height:s,style:{position:"absolute",top:0,left:0},children:[ie("rect",{x:0,y:0,width:m,height:s,fill:u.background,opacity:.6}),ie("rect",{x:m+g,y:0,width:a-m-g,height:s,fill:u.background,opacity:.6}),ie("rect",{x:m,y:0,width:g,height:s,fill:"none",stroke:i==="dark"?"#60a5fa":"#3b82f6",strokeWidth:1.5,rx:1})]})]})});import{useMemo as _r}from"react";function He(e,t,r={}){let{prominence:o=.01,minDistance:n=5,maxPeaks:a}=r;if(e.length<3||t.length<3)return[];let s=1/0,i=-1/0;for(let m=0;m<t.length;m++)t[m]<s&&(s=t[m]),t[m]>i&&(i=t[m]);let l=i-s;if(l===0)return[];let c=o*l,u=[];for(let m=1;m<t.length-1;m++)if(t[m]>t[m-1]&&t[m]>t[m+1]){let d=zr(t,m),g=Vr(t,m),y=t[m]-Math.max(d,g);y>=c&&u.push({index:m,prom:y})}u.sort((m,d)=>d.prom-m.prom);let p=[];for(let m of u)p.some(g=>Math.abs(g.index-m.index)<n)||p.push(m);return(a?p.slice(0,a):p).map(m=>({x:e[m.index],y:t[m.index],label:Zr(e[m.index])})).sort((m,d)=>m.x-d.x)}function zr(e,t){let r=e[t];for(let o=t-1;o>=0&&!(e[o]>e[t]);o--)e[o]<r&&(r=e[o]);return r}function Vr(e,t){let r=e[t];for(let o=t+1;o<e.length&&!(e[o]>e[t]);o++)e[o]<r&&(r=e[o]);return r}function Zr(e){return Math.round(e).toString()}function Br(e,t={}){let{enabled:r=!0,spectrumIds:o,prominence:n,minDistance:a,maxPeaks:s}=t;return _r(()=>{if(!r)return[];let i=o?e.filter(c=>o.includes(c.id)):e,l=[];for(let c of i){if(c.visible===!1)continue;let u=He(c.x,c.y,{prominence:n,minDistance:a,maxPeaks:s});for(let p of u)l.push({...p,spectrumId:c.id})}return l},[e,r,o,n,a,s])}import{useCallback as B,useState as Ge}from"react";var Tt=0,Hr=[" ",",",";"," "];function At(e){let t=e.trim().split(/\r?\n/).slice(0,5),r=",",o=0;for(let n of Hr){let a=t.map(i=>i.split(n).length-1),s=Math.min(...a);s>0&&s>=o&&(a.every(l=>l===a[0])||s>o)&&(o=s,r=n)}return r}function Xe(e,t={}){let{xColumn:r=0,yColumn:o=1,hasHeader:n=!0,label:a="CSV Spectrum"}=t,s=t.delimiter??At(e),i=e.trim().split(/\r?\n/);if(i.length<2)throw new Error("CSV file must contain at least 2 lines");let l=a,c=0;if(n){let f=i[0].split(s).map(m=>m.trim());!t.label&&f[o]&&(l=f[o]),c=1}let u=[],p=[];for(let f=c;f<i.length;f++){let m=i[f].trim();if(m===""||m.startsWith("#"))continue;let d=m.split(s),g=parseFloat(d[r]),y=parseFloat(d[o]);!isNaN(g)&&!isNaN(y)&&(u.push(g),p.push(y))}if(u.length===0)throw new Error("No valid numeric data found in CSV");return{id:`csv-${++Tt}`,label:l,x:new Float64Array(u),y:new Float64Array(p)}}function Xr(e,t={}){let{hasHeader:r=!0,label:o}=t,n=t.delimiter??At(e),a=e.trim().split(/\r?\n/);if(a.length<2)throw new Error("CSV file must contain at least 2 lines");let i=a[r?1:0].split(n).length;if(i<2)throw new Error("CSV must have at least 2 columns (x + y)");let l,c=0;r&&(l=a[0].split(n).map(m=>m.trim()),c=1);let u=[],p=Array.from({length:i-1},()=>[]);for(let m=c;m<a.length;m++){let d=a[m].trim();if(d===""||d.startsWith("#"))continue;let g=d.split(n),y=parseFloat(g[0]);if(!isNaN(y)){u.push(y);for(let b=1;b<i;b++){let x=parseFloat(g[b]);p[b-1].push(isNaN(x)?0:x)}}}let f=new Float64Array(u);return p.map((m,d)=>({id:`csv-${++Tt}`,label:o??l?.[d+1]??`Spectrum ${d+1}`,x:f,y:new Float64Array(m)}))}var Wr=0;function Ye(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,o)=>We(r,o));if(typeof t=="object"&&t!==null){let r=t;return Array.isArray(r.spectra)?r.spectra.map((o,n)=>We(o,n)):[We(t,0)]}throw new Error("Invalid JSON structure: expected an object or array")}function We(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 o=e.y??e.intensities??e.absorbance;if(!o||!Array.isArray(o))throw new Error(`Spectrum ${t}: missing y-axis data (expected "y", "intensities", or "absorbance")`);if(r.length!==o.length)throw new Error(`Spectrum ${t}: x and y arrays must have the same length (got ${r.length} and ${o.length})`);let n=e.label??e.title??e.name??`Spectrum ${t+1}`;return{id:`json-${++Wr}`,label:n,x:new Float64Array(r),y:new Float64Array(o),xUnit:e.xUnit,yUnit:e.yUnit,type:e.type,meta:e.meta}}var Lt=0,ae=null,Et=!1;async function Yr(){if(Et)return ae;Et=!0;try{ae=await import("jcampconverter")}catch{ae=null}return ae}function Pt(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 je(e){let t=await Yr();return t?jr(e,t):[Gr(e)]}function jr(e,t){return t.convert(e,{keepRecordsRegExp:/.*/}).flatten.map((o,n)=>{let a=o.spectra?.[0]?.data?.[0];if(!a)throw new Error(`JCAMP block ${n}: no spectral data found`);return{id:`jcamp-${++Lt}`,label:o.info?.TITLE??`Spectrum ${n+1}`,x:new Float64Array(a.x),y:new Float64Array(a.y),xUnit:o.info?.XUNITS??"cm\u207B\xB9",yUnit:o.info?.YUNITS??"Absorbance",type:Pt(o.info),meta:o.info}})}function Gr(e){let t=e.split(/\r?\n/),r={},o=[],n=[],a=!1;for(let s of t){let i=s.trim();if(i.startsWith("##")){let l=i.match(/^##(.+?)=\s*(.*)$/);if(l){let c=l[1].trim().toUpperCase(),u=l[2].trim();if(c==="XYDATA"||c==="XYPOINTS"){a=!0;continue}if(c==="END"){a=!1;continue}r[c]=u}continue}if(a&&i!==""){let l=i.split(/[\s,]+/).map(Number);if(l.length>=2&&!l.some(isNaN)){let c=l[0],u=parseFloat(r.FIRSTX??"0"),p=parseFloat(r.LASTX??"0"),f=parseInt(r.NPOINTS??"0",10);if(f>0&&l.length===2)o.push(l[0]),n.push(l[1]);else if(l.length>1){let m=f>1?(p-u)/(f-1):0;for(let d=1;d<l.length;d++)o.push(c+(d-1)*m),n.push(l[d])}}}}if(o.length===0)throw new Error("Failed to parse JCAMP-DX: no data found. Install jcampconverter for full format support.");return{id:`jcamp-${++Lt}`,label:r.TITLE??"JCAMP Spectrum",x:new Float64Array(o),y:new Float64Array(n),xUnit:r.XUNITS??"cm\u207B\xB9",yUnit:r.YUNITS??"Absorbance",type:Pt(r),meta:r}}function Kr(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 Jr(e=[]){let[t,r]=Ge(e),[o,n]=Ge(!1),[a,s]=Ge(null),i=B(async(m,d)=>{n(!0),s(null);try{let g;switch(d){case"jcamp":g=await je(m);break;case"csv":g=[Xe(m)];break;case"json":g=Ye(m);break}r(y=>[...y,...g])}catch(g){let y=g instanceof Error?g.message:"Failed to parse file";s(y)}finally{n(!1)}},[]),l=B(async m=>{let d=Kr(m.name);if(!d){s(`Unsupported file format: ${m.name}`);return}let g=await m.text();await i(g,d)},[i]),c=B(m=>{r(d=>[...d,m])},[]),u=B(m=>{r(d=>d.filter(g=>g.id!==m))},[]),p=B(m=>{r(d=>d.map(g=>g.id===m?{...g,visible:g.visible===!1}:g))},[]),f=B(()=>{r([]),s(null)},[]);return{spectra:t,loading:o,error:a,loadFile:l,loadText:i,addSpectrum:c,removeSpectrum:u,toggleVisibility:p,clear:f}}import{useCallback as se}from"react";var Dt={solid:"",dashed:"8 4",dotted:"2 2","dash-dot":"8 4 2 4"};function Ke(e,t,r,o){let{width:n,height:a,background:s="#ffffff",title:i}=o,l=e.filter(c=>c.visible!==!1).map((c,u)=>{let p=c.color??P(u),f=c.lineStyle??"solid",m=c.lineWidth??1.5,d=Dt[f]??"",g=Math.min(c.x.length,c.y.length);if(g<2)return"";let y=[];for(let b=0;b<g;b++){let x=t(c.x[b]).toFixed(2),S=r(c.y[b]).toFixed(2);y.push(`${b===0?"M":"L"}${x},${S}`)}return`<path d="${y.join(" ")}" fill="none" stroke="${p}" stroke-width="${m}"${d?` stroke-dasharray="${d}"`:""}/>
|
|
2
|
+
import{useCallback as Mt,useId as Fr,useMemo as U,useRef as Tt,useState as Xe}from"react";import{scaleLinear as pt}from"d3-scale";import{extent as ft}from"d3-array";var Kt=.05;function xe(e){let t=1/0,r=-1/0;for(let o of e){if(o.visible===!1)continue;let[n,s]=ft(o.x);n<t&&(t=n),s>r&&(r=s)}return isFinite(t)?[t,r]:[0,1]}function H(e){let t=1/0,r=-1/0;for(let s of e){if(s.visible===!1)continue;let[a,i]=ft(s.y);a<t&&(t=a),i>r&&(r=i)}if(!isFinite(t))return[0,1];let n=(r-t)*Kt;return[t-n,r+n]}function Se(e,t,r,o){let n=t-r.left-r.right,s=o?[e[1],e[0]]:e;return pt().domain(s).range([0,n])}function X(e,t,r){let o=t-r.top-r.bottom;return pt().domain(e).range([o,0])}var ve=["#2563eb","#dc2626","#16a34a","#9333ea","#ea580c","#0891b2","#be185d","#854d0e","#4f46e5","#65a30d"],dt={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"},bt={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 A(e){return ve[e%ve.length]}function z(e){return e==="dark"?bt:dt}import{useCallback as we,useEffect as Jt,useMemo as gt,useRef as ne,useState as qt}from"react";import{zoom as Qt,zoomIdentity as Ce}from"d3-zoom";import{select as V}from"d3-selection";import"d3-transition";var ht=1.5;function ke(e){let{plotWidth:t,plotHeight:r,xScale:o,yScale:n,scaleExtent:s=[1,50],enabled:a=!0,onViewChange:i}=e,l=ne(null),c=ne(null),u=ne(i);u.current=i;let p=ne(s);p.current=s;let[f,m]=qt(Ce),d=gt(()=>f.rescaleX(o.copy()),[f,o]),g=gt(()=>f.rescaleY(n.copy()),[f,n]);Jt(()=>{let S=l.current;if(!S||!a)return;let v=Qt().scaleExtent(p.current).extent([[0,0],[t,r]]).translateExtent([[-1/0,-1/0],[1/0,1/0]]).on("zoom",x=>{let w=x.transform;if(m(w),u.current){let C=w.rescaleX(o.copy()),k=w.rescaleY(n.copy());u.current(C.domain(),k.domain())}});return c.current=v,V(S).call(v),V(S).on("dblclick.zoom",()=>{V(S).transition().duration(300).call(v.transform,Ce)}),()=>{V(S).on(".zoom",null)}},[t,r,a,o,n]);let h=we(()=>{!l.current||!c.current||V(l.current).transition().duration(300).call(c.current.transform,Ce)},[]),b=we(()=>{!l.current||!c.current||V(l.current).transition().duration(200).call(c.current.scaleBy,ht)},[]),y=we(()=>{!l.current||!c.current||V(l.current).transition().duration(200).call(c.current.scaleBy,1/ht)},[]);return{zoomRef:l,state:{transform:f,isZoomed:f.k!==1||f.x!==0||f.y!==0},zoomedXScale:d,zoomedYScale:g,resetZoom:h,zoomIn:b,zoomOut:y}}import{forwardRef as ir,useEffect as xt,useImperativeHandle as ar,useRef as St}from"react";function Re(e,t,r,o,n,s,a){let i=o-r;if(i<=a){let f=[];for(let m=r;m<o;m++)f.push({px:n(e[m]),py:s(t[m]),index:m});return f}let l=[];l.push({px:n(e[r]),py:s(t[r]),index:r});let c=a-2,u=(i-2)/c,p=r;for(let f=0;f<c;f++){let m=r+1+Math.floor(f*u),d=r+1+Math.min(Math.floor((f+1)*u),i-2),g=d,h=r+1+Math.min(Math.floor((f+2)*u),i-2),b,y;if(f===c-1)b=n(e[o-1]),y=s(t[o-1]);else{b=0,y=0;let C=h-g;for(let k=g;k<h;k++)b+=n(e[k]),y+=s(t[k]);C>0&&(b/=C,y/=C)}let S=n(e[p]),v=s(t[p]),x=-1,w=m;for(let C=m;C<d;C++){let k=n(e[C]),R=s(t[C]),T=Math.abs((S-b)*(R-v)-(S-k)*(y-v));T>x&&(x=T,w=C)}l.push({px:n(e[w]),py:s(t[w]),index:w}),p=w}return l.push({px:n(e[o-1]),py:s(t[o-1]),index:o-1}),l}var er=1.5,tr={solid:[],dashed:[8,4],dotted:[2,2],"dash-dot":[8,4,2,4]},rr=2e3;function nr(e,t,r){e.clearRect(0,0,t,r)}function or(e,t,r,o,n,s,a){let{highlighted:i=!1,opacity:l=1}=a??{},c=Math.min(t.x.length,t.y.length);if(c<2)return;let u=t.color??A(r),p=t.lineWidth??er,f=i?p+1:p,m=tr[t.lineStyle??"solid"]??[],[d,g]=o.domain(),h=Math.min(d,g),b=Math.max(d,g),y=0,S=c;for(let x=0;x<c;x++)if(t.x[x]>=h||x<c-1&&t.x[x+1]>=h){y=Math.max(0,x-1);break}for(let x=c-1;x>=0;x--)if(t.x[x]<=b||x>0&&t.x[x-1]<=b){S=Math.min(c,x+2);break}let v=S-y;if(e.save(),e.beginPath(),e.strokeStyle=u,e.lineWidth=f,e.globalAlpha=l,e.lineJoin="round",e.setLineDash(m),v>rr){let x=Math.max(Math.ceil(s*2),200),w=Re(t.x,t.y,y,S,o,n,x);if(w.length>0){e.moveTo(w[0].px,w[0].py);for(let C=1;C<w.length;C++)e.lineTo(w[C].px,w[C].py)}}else{let x=!1;for(let w=y;w<S;w++){let C=o(t.x[w]),k=n(t.y[w]);x?e.lineTo(C,k):(e.moveTo(C,k),x=!0)}}e.stroke(),e.restore()}function yt(e,t,r,o,n,s,a){nr(e,n,s),t.forEach((i,l)=>{i.visible!==!1&&or(e,i,l,r,o,n,{highlighted:i.id===a,opacity:a&&i.id!==a?.3:1})})}import{jsx as sr}from"react/jsx-runtime";var W=ir(function({spectra:t,xScale:r,yScale:o,width:n,height:s,highlightedId:a},i){let l=St(null),c=St(1);return ar(i,()=>l.current,[]),xt(()=>{let u=l.current;if(!u)return;let p=window.devicePixelRatio||1;c.current=p,u.width=n*p,u.height=s*p},[n,s]),xt(()=>{let u=l.current;if(!u)return;let p=u.getContext("2d");if(!p)return;let f=c.current;p.setTransform(f,0,0,f,0,0),yt(p,t,r,o,n,s,a)},[t,r,o,n,s,a]),sr("canvas",{ref:l,style:{width:n,height:s,position:"absolute",top:0,left:0,pointerEvents:"none"}})});import{jsx as F,jsxs as B}from"react/jsx-runtime";function vt(e,t){let[r,o]=e.domain(),n=Math.min(r,o),a=(Math.max(r,o)-n)/(t-1);return Array.from({length:t},(i,l)=>n+l*a)}function wt(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:r,height:o,xLabel:n,yLabel:s,showGrid:a=!0,colors:i}){let l=vt(e,8),c=vt(t,6);return B("g",{children:[a&&B("g",{children:[l.map(u=>F("line",{x1:e(u),x2:e(u),y1:0,y2:o,stroke:i.gridColor,strokeWidth:.5},`xgrid-${u}`)),c.map(u=>F("line",{x1:0,x2:r,y1:t(u),y2:t(u),stroke:i.gridColor,strokeWidth:.5},`ygrid-${u}`))]}),B("g",{transform:`translate(0, ${o})`,children:[F("line",{x1:0,x2:r,y1:0,y2:0,stroke:i.axisColor}),l.map(u=>B("g",{transform:`translate(${e(u)}, 0)`,children:[F("line",{y1:0,y2:6,stroke:i.axisColor}),F("text",{y:20,textAnchor:"middle",fill:i.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:wt(u)})]},`xtick-${u}`)),n&&F("text",{x:r/2,y:42,textAnchor:"middle",fill:i.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:n})]}),B("g",{children:[F("line",{x1:0,x2:0,y1:0,y2:o,stroke:i.axisColor}),c.map(u=>B("g",{transform:`translate(0, ${t(u)})`,children:[F("line",{x1:-6,x2:0,stroke:i.axisColor}),F("text",{x:-10,textAnchor:"end",dominantBaseline:"middle",fill:i.tickColor,fontSize:11,fontFamily:"system-ui, sans-serif",children:wt(u)})]},`ytick-${u}`)),s&&F("text",{transform:`translate(-50, ${o/2}) rotate(-90)`,textAnchor:"middle",fill:i.labelColor,fontSize:13,fontFamily:"system-ui, sans-serif",children:s})]})]})}import{jsx as Me,jsxs as lr}from"react/jsx-runtime";function Te({peaks:e,xScale:t,yScale:r,colors:o,onPeakClick:n}){let[s,a]=t.domain(),i=Math.min(s,a),l=Math.max(s,a),c=e.filter(u=>u.x>=i&&u.x<=l);return Me("g",{className:"spectraview-peaks",children:c.map((u,p)=>{let f=t(u.x),m=r(u.y);return lr("g",{transform:`translate(${f}, ${m})`,style:{cursor:n?"pointer":"default"},onClick:()=>n?.(u),children:[Me("polygon",{points:`0,-5 -5,${-5*2.5} 5,${-5*2.5}`,fill:o.labelColor,opacity:.8}),u.label&&Me("text",{y:-5*2.5-14,textAnchor:"middle",fill:o.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",fontWeight:500,children:u.label})]},`peak-${u.x}-${p}`)})})}import{jsx as Ae,jsxs as cr}from"react/jsx-runtime";function Ee({regions:e,xScale:t,height:r,colors:o}){return Ae("g",{className:"spectraview-regions",children:e.map((n,s)=>{let a=t(n.xStart),i=t(n.xEnd),l=Math.min(a,i),c=Math.abs(i-a);return cr("g",{children:[Ae("rect",{x:l,y:0,width:c,height:r,fill:n.color??o.regionFill,stroke:o.regionStroke,strokeWidth:1}),n.label&&Ae("text",{x:l+c/2,y:12,textAnchor:"middle",fill:o.labelColor,fontSize:10,fontFamily:"system-ui, sans-serif",children:n.label})]},`region-${s}`)})})}import{jsx as oe,jsxs as Pe}from"react/jsx-runtime";function Le({position:e,width:t,height:r,colors:o,snapPoint:n}){return e?Pe("g",{className:"spectraview-crosshair",pointerEvents:"none",children:[oe("line",{x1:e.px,x2:e.px,y1:0,y2:r,stroke:o.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),oe("line",{x1:0,x2:t,y1:e.py,y2:e.py,stroke:o.crosshairColor,strokeWidth:1,strokeDasharray:"4 4"}),n&&oe("circle",{cx:n.px,cy:n.py,r:4,fill:n.color??o.crosshairColor,stroke:o.background,strokeWidth:1.5}),Pe("g",{transform:`translate(${Math.min(e.px+10,t-100)}, ${Math.max(e.py-10,20)})`,children:[oe("rect",{x:0,y:-14,width:90,height:18,rx:3,fill:o.tooltipBg,stroke:o.tooltipBorder,strokeWidth:.5,opacity:.9}),Pe("text",{x:5,y:0,fill:o.tooltipText,fontSize:10,fontFamily:"monospace",children:[Ct(n?.dataX??e.dataX),","," ",Ct(n?.dataY??e.dataY)]})]})]}):null}function Ct(e){return Math.abs(e)>=100?Math.round(e).toString():Math.abs(e)>=1?e.toFixed(1):e.toFixed(4)}import{jsx as j,jsxs as mr}from"react/jsx-runtime";function Fe({annotations:e,xScale:t,yScale:r,colors:o}){return e.length===0?null:j("g",{className:"spectraview-annotations",pointerEvents:"none",children:e.map(n=>{let s=t(n.x),a=r(n.y),[i,l]=n.offset??[0,-20],c=s+i,u=a+l,p=n.fontSize??11,f=n.color??o.tickColor,m=n.showAnchorLine!==!1;return mr("g",{children:[m&&j("line",{x1:s,y1:a,x2:c,y2:u,stroke:f,strokeWidth:.75,strokeDasharray:"3 2",opacity:.6}),j("circle",{cx:s,cy:a,r:2.5,fill:f,opacity:.8}),j("text",{x:c,y:u,fill:o.background,fontSize:p,fontFamily:"system-ui, sans-serif",textAnchor:"middle",dominantBaseline:"auto",stroke:o.background,strokeWidth:3,strokeLinejoin:"round",children:n.text}),j("text",{x:c,y:u,fill:f,fontSize:p,fontFamily:"system-ui, sans-serif",textAnchor:"middle",dominantBaseline:"auto",children:n.text})]},n.id)})})}function ie(e,t,r){if(r===0)return-1;if(r===1)return 0;let o=e[r-1]>=e[0],n=0,s=r-1;for(;n<s-1;){let l=n+s>>>1,c=e[l];o?c<=t?n=l:s=l:c>=t?n=l:s=l}let a=Math.abs(e[n]-t),i=Math.abs(e[s]-t);return a<=i?n:s}function De(e,t,r,o,n){let s=null;for(let a of e){if(a.visible===!1)continue;let i=Math.min(a.x.length,a.y.length);if(i<2)continue;let l=ie(a.x,t,i);if(l<0)continue;let c=a.x[l],u=a.y[l],p=Math.abs(o(c)-o(t)),f=Math.abs(n(u)-r),m=Math.sqrt(p*p+f*f);(!s||m<s.distance)&&(s={spectrumId:a.id,index:l,x:c,y:u,distance:m})}return s}import{memo as ur}from"react";import{jsx as Ie,jsxs as fr}from"react/jsx-runtime";var Ue=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}),pr=e=>({display:"flex",gap:4,padding:"4px 0",borderBottom:`1px solid ${e==="dark"?"#374151":"#e5e7eb"}`}),$e=ur(function({onZoomIn:t,onZoomOut:r,onReset:o,isZoomed:n,theme:s}){return fr("div",{style:pr(s),className:"spectraview-toolbar",children:[Ie("button",{type:"button",style:Ue(s),onClick:t,title:"Zoom in","aria-label":"Zoom in",children:"+"}),Ie("button",{type:"button",style:Ue(s),onClick:r,title:"Zoom out","aria-label":"Zoom out",children:"\u2212"}),Ie("button",{type:"button",style:{...Ue(s),opacity:n?1:.4},onClick:o,disabled:!n,title:"Reset zoom","aria-label":"Reset zoom",children:"\u21BA"})]})});import{memo as dr}from"react";import{jsx as Ne,jsxs as yr}from"react/jsx-runtime";var br=(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}),gr=(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"}),hr=(e,t)=>({width:12,height:3,borderRadius:1,background:e,opacity:t?.4:1,flexShrink:0}),ae=dr(function({spectra:t,theme:r,position:o,onToggleVisibility:n,onHighlight:s,highlightedId:a}){return t.length<=1?null:Ne("div",{className:"spectraview-legend",style:br(r,o),role:"list","aria-label":"Spectrum legend",children:t.map((i,l)=>{let c=i.color??A(l),u=i.visible===!1,p=a===i.id;return yr("div",{role:"listitem",style:gr(r,u,p),onClick:()=>n?.(i.id),onMouseEnter:()=>s?.(i.id),onMouseLeave:()=>s?.(null),title:u?`Show ${i.label}`:`Hide ${i.label}`,children:[Ne("span",{style:hr(c,u)}),Ne("span",{style:{textDecoration:u?"line-through":"none",maxWidth:120,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:i.label})]},i.id)})})});import{useCallback as se,useState as xr}from"react";import{jsx as Sr,jsxs as vr}from"react/jsx-runtime";function Oe({enabled:e,theme:t,width:r,height:o,onDrop:n,children:s}){let[a,i]=xr(!1),l={current:0},c=se(m=>{e&&(m.preventDefault(),l.current++,i(!0))},[e]),u=se(m=>{e&&(m.preventDefault(),l.current--,l.current<=0&&(l.current=0,i(!1)))},[e]),p=se(m=>{e&&(m.preventDefault(),m.dataTransfer.dropEffect="copy")},[e]),f=se(m=>{if(!e)return;m.preventDefault(),l.current=0,i(!1);let d=Array.from(m.dataTransfer.files);d.length>0&&n?.(d)},[e,n]);return vr("div",{style:{position:"relative",width:r,height:o},onDragEnter:c,onDragLeave:u,onDragOver:p,onDrop:f,children:[s,a&&Sr("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"})]})}import{useMemo as wr}from"react";import{jsx as Z,jsxs as Cr}from"react/jsx-runtime";var kt=8;function ze({spectra:e,xScale:t,plotWidth:r,plotHeight:o,margin:n,theme:s,showGrid:a,xLabel:i,yLabel:l}){let c=e.filter(d=>d.visible!==!1),u=wr(()=>z(s),[s]),p=c.length,f=(p-1)*kt,m=Math.max(40,Math.floor((o-f)/Math.max(p,1)));return Z("g",{className:"spectraview-stacked",children:c.map((d,g)=>{let h=g*(m+kt),b=H([d]),y=X(b,m+n.top+n.bottom,{...n,top:0,bottom:0}),S=d.color??A(g),v={...d,color:S};return Cr("g",{transform:`translate(0, ${h})`,children:[Z("rect",{x:0,y:0,width:r,height:m,fill:"transparent",stroke:u.gridColor,strokeWidth:.5,rx:2}),Z(Y,{xScale:t,yScale:y,width:r,height:m,xLabel:g===p-1?i:"",yLabel:l,showGrid:a,colors:u}),Z("text",{x:4,y:14,fill:S,fontSize:11,fontFamily:"system-ui, sans-serif",fontWeight:500,children:d.label}),Z("foreignObject",{x:0,y:0,width:r,height:m,children:Z(W,{spectra:[v],xScale:t,yScale:y,width:r,height:m})})]},d.id)})})}import{useCallback as Ve,useRef as kr,useState as Rr}from"react";function Be(e){let{enabled:t,xScale:r,onRegionSelect:o}=e,[n,s]=Rr(null),a=kr(null),i=Ve(u=>{if(!t||!u.shiftKey)return;u.preventDefault();let p=u.currentTarget.getBoundingClientRect(),f=u.clientX-p.left,m=r.invert(f);a.current=m,s({xStart:m,xEnd:m})},[t,r]),l=Ve(u=>{if(a.current===null)return;let p=u.currentTarget.getBoundingClientRect(),f=u.clientX-p.left,m=r.invert(f),d=a.current;s({xStart:Math.min(d,m),xEnd:Math.max(d,m)})},[r]),c=Ve(()=>{if(a.current===null||!n)return;Math.abs(n.xEnd-n.xStart)>0&&o?.(n),a.current=null,s(null)},[n,o]);return{pendingRegion:n,handleMouseDown:i,handleMouseMove:l,handleMouseUp:c}}import{useCallback as Mr,useEffect as Tr,useRef as Rt,useState as Ar}from"react";function Ze(){let[e,t]=Ar(null),r=Rt(null),o=Rt(null),n=Mr(s=>{if(r.current&&(r.current.disconnect(),r.current=null),o.current=s,!s)return;let a=new ResizeObserver(c=>{let u=c[0];if(!u)return;let{width:p,height:f}=u.contentRect;t({width:Math.round(p),height:Math.round(f)})});a.observe(s),r.current=a;let{width:i,height:l}=s.getBoundingClientRect();t({width:Math.round(i),height:Math.round(l)})},[]);return Tr(()=>()=>{r.current?.disconnect()},[]),{ref:n,size:e}}import{useCallback as Er}from"react";function _e(e){let{onZoomIn:t,onZoomOut:r,onReset:o,enabled:n=!0}=e;return Er(a=>{if(n)switch(a.key){case"+":case"=":a.preventDefault(),t();break;case"-":a.preventDefault(),r();break;case"Escape":a.preventDefault(),o();break}},[n,t,r,o])}function Pr(){return typeof window>"u"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches}function He(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 Lr={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"};import{Fragment as zr,jsx as M,jsxs as G}from"react/jsx-runtime";var Dr={top:20,right:20,bottom:50,left:65},Ur=800,Ir=400;function $r(e){return{width:e.width??Ur,height:e.height??Ir,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:{...Dr,...e.margin},theme:e.theme??"light",responsive:e.responsive??!1,enableDragDrop:e.enableDragDrop??!1,enableRegionSelect:e.enableRegionSelect??!1}}function Nr(e,t,r){let o=e[0];return{xLabel:t??o?.xUnit??"x",yLabel:r??o?.yUnit??"y"}}function Or(e){let{spectra:t,peaks:r=[],regions:o=[],annotations:n=[],onPeakClick:s,onViewChange:a,onCrosshairMove:i,onToggleVisibility:l,onFileDrop:c,onRegionSelect:u,canvasRef:p,snapCrosshair:f=!0}=e,{ref:m,size:d}=Ze(),h=`spectraview-clip-${Fr().replace(/:/g,"")}`,b=U(()=>$r(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]),y=b.responsive&&d?d.width:b.width,{height:S,margin:v,reverseX:x,theme:w}=b,C=y-v.left-v.right,k=S-v.top-v.bottom,R=U(()=>z(w),[w]),T=U(()=>Nr(t,e.xLabel,e.yLabel),[t,e.xLabel,e.yLabel]),P=U(()=>xe(t),[t]),L=U(()=>H(t),[t]),fe=U(()=>Se(P,y,v,x),[P,y,v,x]),N=U(()=>X(L,S,v),[L,S,v]),O=Tt(a);O.current=a;let zt=U(()=>($,te)=>{O.current?.({xDomain:$,yDomain:te})},[]),{zoomRef:rt,state:Vt,zoomedXScale:E,zoomedYScale:I,resetZoom:nt,zoomIn:ot,zoomOut:it}=ke({plotWidth:C,plotHeight:k,xScale:fe,yScale:N,onViewChange:a?zt:void 0}),{pendingRegion:Q,handleMouseDown:Bt,handleMouseMove:Zt,handleMouseUp:_t}=Be({enabled:b.enableRegionSelect,xScale:E,onRegionSelect:u}),[de,at]=Xe(null),[Ht,st]=Xe(null),[Xt,be]=Xe(null),ee=Tt(i);ee.current=i;let lt=Mt($=>{if(!b.showCrosshair)return;let te=$.currentTarget.getBoundingClientRect(),mt=$.clientX-te.left,he=$.clientY-te.top,re=E.invert(mt),ye=I.invert(he);if(st({px:mt,py:he,dataX:re,dataY:ye}),f&&t.length>0){let D=De(t,re,he,E,I);if(D&&D.distance<50){let ut=t.findIndex(Gt=>Gt.id===D.spectrumId);be({px:E(D.x),py:I(D.y),dataX:D.x,dataY:D.y,color:t[ut]?.color??A(ut)}),ee.current?.(D.x,D.y)}else be(null),ee.current?.(re,ye)}else ee.current?.(re,ye)},[E,I,b.showCrosshair,f,t]),ct=Mt(()=>{st(null),be(null)},[]),Wt=_e({onZoomIn:ot,onZoomOut:it,onReset:nt}),Yt=U(()=>He(t.length,T.xLabel,T.yLabel),[t.length,T.xLabel,T.yLabel]),jt=b.displayMode==="stacked";if(t.length===0)return M("div",{ref:b.responsive?m:void 0,style:{width:b.responsive?"100%":y,height:S,display:"flex",alignItems:"center",justifyContent:"center",border:`1px dashed ${R.gridColor}`,borderRadius:8,color:R.tickColor,fontFamily:"system-ui, sans-serif",fontSize:14},className:e.className,children:"No spectra loaded"});let ge=b.showToolbar?37:0;return G("div",{ref:b.responsive?m:void 0,style:{width:b.responsive?"100%":y,background:R.background,borderRadius:4,overflow:"hidden"},className:e.className,role:"img","aria-label":Yt,tabIndex:0,onKeyDown:Wt,children:[b.showToolbar&&M($e,{onZoomIn:ot,onZoomOut:it,onReset:nt,isZoomed:Vt.isZoomed,theme:w}),b.showLegend&&b.legendPosition==="top"&&M(ae,{spectra:t,theme:w,position:"top",onToggleVisibility:l,onHighlight:at,highlightedId:de}),M(Oe,{enabled:b.enableDragDrop,theme:w,width:y,height:S-ge,onDrop:c,children:jt?M("svg",{width:y,height:S-ge,style:{position:"absolute",top:0,left:0},children:G("g",{transform:`translate(${v.left}, ${v.top})`,children:[M(ze,{spectra:t,xScale:E,plotWidth:C,plotHeight:k,margin:v,theme:w,showGrid:b.showGrid,xLabel:T.xLabel,yLabel:T.yLabel}),M("rect",{ref:rt,x:0,y:0,width:C,height:k,fill:"transparent",style:{cursor:"grab"},onMouseMove:lt,onMouseLeave:ct})]})}):G(zr,{children:[M("div",{style:{position:"absolute",top:v.top,left:v.left,width:C,height:k,overflow:"hidden"},children:M(W,{ref:p,spectra:t,xScale:E,yScale:I,width:C,height:k,highlightedId:de??void 0})}),M("svg",{width:y,height:S-ge,style:{position:"absolute",top:0,left:0},children:G("g",{transform:`translate(${v.left}, ${v.top})`,children:[M(Y,{xScale:E,yScale:I,width:C,height:k,xLabel:T.xLabel,yLabel:T.yLabel,showGrid:b.showGrid,colors:R}),M("defs",{children:M("clipPath",{id:h,children:M("rect",{x:0,y:0,width:C,height:k})})}),G("g",{clipPath:`url(#${h})`,children:[o.length>0&&M(Ee,{regions:o,xScale:E,height:k,colors:R}),r.length>0&&M(Te,{peaks:r,xScale:E,yScale:I,colors:R,onPeakClick:s})]}),n.length>0&&M(Fe,{annotations:n,xScale:E,yScale:I,colors:R}),b.showCrosshair&&M(Le,{position:Ht,width:C,height:k,colors:R,snapPoint:Xt}),Q&&M("rect",{x:E(Q.xStart),y:0,width:Math.abs(E(Q.xEnd)-E(Q.xStart)),height:k,fill:R.regionFill,stroke:R.regionStroke,strokeWidth:1,pointerEvents:"none"}),M("rect",{ref:rt,x:0,y:0,width:C,height:k,fill:"transparent",style:{cursor:b.showCrosshair?"crosshair":"grab"},onMouseDown:Bt,onMouseMove:$=>{lt($),Zt($)},onMouseUp:_t,onMouseLeave:ct})]})})]})}),b.showLegend&&b.legendPosition==="bottom"&&M(ae,{spectra:t,theme:w,position:"bottom",onToggleVisibility:l,onHighlight:at,highlightedId:de})]})}import{memo as Vr,useEffect as Br,useRef as Zr,useMemo as We}from"react";import{scaleLinear as At}from"d3-scale";import{jsx as le,jsxs as Et}from"react/jsx-runtime";var _r=Vr(function({spectra:t,xExtent:r,yExtent:o,visibleXDomain:n,width:s=200,height:a=50,theme:i="light",isZoomed:l=!1}){let c=Zr(null),u=We(()=>z(i),[i]),p=We(()=>At().domain(r).range([0,s]),[r,s]),f=We(()=>At().domain(o).range([a-2,2]),[o,a]);Br(()=>{let h=c.current?.getContext("2d");if(h){h.clearRect(0,0,s,a);for(let b=0;b<t.length;b++){let y=t[b];if(y.visible===!1)continue;let S=Math.min(y.x.length,y.y.length);if(S<2)continue;let v=y.color??A(b);h.beginPath(),h.strokeStyle=v,h.lineWidth=1,h.globalAlpha=.7;let x=Math.max(1,Math.floor(S/s)),w=!1;for(let C=0;C<S;C+=x){let k=p(y.x[C]),R=f(y.y[C]);w?h.lineTo(k,R):(h.moveTo(k,R),w=!0)}h.stroke()}}},[t,p,f,s,a]);let m=p(Math.min(n[0],n[1])),d=p(Math.max(n[0],n[1])),g=Math.max(d-m,2);return Et("div",{className:"spectraview-minimap",style:{position:"relative",width:s,height:a,border:`1px solid ${u.gridColor}`,borderRadius:3,overflow:"hidden",background:u.background},children:[le("canvas",{ref:c,width:s,height:a,style:{position:"absolute",top:0,left:0}}),l&&Et("svg",{width:s,height:a,style:{position:"absolute",top:0,left:0},children:[le("rect",{x:0,y:0,width:m,height:a,fill:u.background,opacity:.6}),le("rect",{x:m+g,y:0,width:s-m-g,height:a,fill:u.background,opacity:.6}),le("rect",{x:m,y:0,width:g,height:a,fill:"none",stroke:i==="dark"?"#60a5fa":"#3b82f6",strokeWidth:1.5,rx:1})]})]})});import{memo as Hr,useMemo as Pt}from"react";import{jsx as Lt,jsxs as K}from"react/jsx-runtime";function Ye(e,t){switch(t){case"fixed2":return e.toFixed(2);case"fixed4":return e.toFixed(4);case"scientific":return e.toExponential(2);default:return Math.abs(e)>=100?Math.round(e).toString():Math.abs(e)>=1?e.toFixed(2):Math.abs(e)>=.01?e.toFixed(4):e.toExponential(2)}}var Xr=Hr(function({data:t,spectra:r,peaks:o=[],plotWidth:n,plotHeight:s,colors:a,numberFormat:i="auto"}){if(!t)return null;let l=Pt(()=>t?r.filter(b=>b.visible!==!1).map((b,y)=>{let S=Math.min(b.x.length,b.y.length);if(S<1)return null;let v=ie(b.x,t.dataX,S);return v<0?null:{label:b.label,color:b.color??A(y),value:b.y[v],x:b.x[v]}}).filter(Boolean):[],[t?.dataX,r]),c=Pt(()=>{if(!t||o.length===0)return null;let b=null,y=1/0;for(let S of o){let v=Math.abs(S.x-t.dataX);v<y&&(y=v,b=S)}return b},[t?.dataX,o]),u=16,p=18,f=c?u:0,m=p+l.length*u+f+8,d=160,g=t.px+15,h=t.py-m/2;return g+d>n&&(g=t.px-d-15),h<0&&(h=4),h+m>s&&(h=s-m-4),K("g",{className:"spectraview-tooltip",transform:`translate(${g}, ${h})`,pointerEvents:"none",children:[Lt("rect",{x:0,y:0,width:d,height:m,rx:4,fill:a.tooltipBg,stroke:a.tooltipBorder,strokeWidth:.5,opacity:.95}),K("text",{x:8,y:14,fill:a.tooltipText,fontSize:10,fontFamily:"monospace",fontWeight:600,children:["x = ",Ye(t.dataX,i)]}),l.map((b,y)=>K("g",{transform:`translate(0, ${p+y*u})`,children:[Lt("circle",{cx:12,cy:8,r:3,fill:b.color}),K("text",{x:20,y:11,fill:a.tooltipText,fontSize:9,fontFamily:"monospace",children:[b.label.slice(0,10),": ",Ye(b.value,i)]})]},b.label)),c&&K("text",{x:8,y:p+l.length*u+12,fill:a.labelColor,fontSize:9,fontFamily:"monospace",fontStyle:"italic",children:["Peak: ",c.label??Ye(c.x,i)]})]})});import{useMemo as Gr}from"react";function je(e,t,r={}){let{prominence:o=.01,minDistance:n=5,maxPeaks:s}=r;if(e.length<3||t.length<3)return[];let a=1/0,i=-1/0;for(let m=0;m<t.length;m++)t[m]<a&&(a=t[m]),t[m]>i&&(i=t[m]);let l=i-a;if(l===0)return[];let c=o*l,u=[];for(let m=1;m<t.length-1;m++)if(t[m]>t[m-1]&&t[m]>t[m+1]){let d=Wr(t,m),g=Yr(t,m),h=t[m]-Math.max(d,g);h>=c&&u.push({index:m,prom:h})}u.sort((m,d)=>d.prom-m.prom);let p=[];for(let m of u)p.some(g=>Math.abs(g.index-m.index)<n)||p.push(m);return(s?p.slice(0,s):p).map(m=>({x:e[m.index],y:t[m.index],label:jr(e[m.index])})).sort((m,d)=>m.x-d.x)}function Wr(e,t){let r=e[t];for(let o=t-1;o>=0&&!(e[o]>e[t]);o--)e[o]<r&&(r=e[o]);return r}function Yr(e,t){let r=e[t];for(let o=t+1;o<e.length&&!(e[o]>e[t]);o++)e[o]<r&&(r=e[o]);return r}function jr(e){return Math.round(e).toString()}function Kr(e,t={}){let{enabled:r=!0,spectrumIds:o,prominence:n,minDistance:s,maxPeaks:a}=t;return Gr(()=>{if(!r)return[];let i=o?e.filter(c=>o.includes(c.id)):e,l=[];for(let c of i){if(c.visible===!1)continue;let u=je(c.x,c.y,{prominence:n,minDistance:s,maxPeaks:a});for(let p of u)l.push({...p,spectrumId:c.id})}return l},[e,r,o,n,s,a])}import{useCallback as _,useState as Qe}from"react";var Ft=0,Jr=[" ",",",";"," "];function Dt(e){let t=e.trim().split(/\r?\n/).slice(0,5),r=",",o=0;for(let n of Jr){let s=t.map(i=>i.split(n).length-1),a=Math.min(...s);a>0&&a>=o&&(s.every(l=>l===s[0])||a>o)&&(o=a,r=n)}return r}function Ge(e,t={}){let{xColumn:r=0,yColumn:o=1,hasHeader:n=!0,label:s="CSV Spectrum"}=t,a=t.delimiter??Dt(e),i=e.trim().split(/\r?\n/);if(i.length<2)throw new Error("CSV file must contain at least 2 lines");let l=s,c=0;if(n){let f=i[0].split(a).map(m=>m.trim());!t.label&&f[o]&&(l=f[o]),c=1}let u=[],p=[];for(let f=c;f<i.length;f++){let m=i[f].trim();if(m===""||m.startsWith("#"))continue;let d=m.split(a),g=parseFloat(d[r]),h=parseFloat(d[o]);!isNaN(g)&&!isNaN(h)&&(u.push(g),p.push(h))}if(u.length===0)throw new Error("No valid numeric data found in CSV");return{id:`csv-${++Ft}`,label:l,x:new Float64Array(u),y:new Float64Array(p)}}function qr(e,t={}){let{hasHeader:r=!0,label:o}=t,n=t.delimiter??Dt(e),s=e.trim().split(/\r?\n/);if(s.length<2)throw new Error("CSV file must contain at least 2 lines");let i=s[r?1:0].split(n).length;if(i<2)throw new Error("CSV must have at least 2 columns (x + y)");let l,c=0;r&&(l=s[0].split(n).map(m=>m.trim()),c=1);let u=[],p=Array.from({length:i-1},()=>[]);for(let m=c;m<s.length;m++){let d=s[m].trim();if(d===""||d.startsWith("#"))continue;let g=d.split(n),h=parseFloat(g[0]);if(!isNaN(h)){u.push(h);for(let b=1;b<i;b++){let y=parseFloat(g[b]);p[b-1].push(isNaN(y)?0:y)}}}let f=new Float64Array(u);return p.map((m,d)=>({id:`csv-${++Ft}`,label:o??l?.[d+1]??`Spectrum ${d+1}`,x:f,y:new Float64Array(m)}))}var Qr=0;function Je(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,o)=>Ke(r,o));if(typeof t=="object"&&t!==null){let r=t;return Array.isArray(r.spectra)?r.spectra.map((o,n)=>Ke(o,n)):[Ke(t,0)]}throw new Error("Invalid JSON structure: expected an object or array")}function Ke(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 o=e.y??e.intensities??e.absorbance;if(!o||!Array.isArray(o))throw new Error(`Spectrum ${t}: missing y-axis data (expected "y", "intensities", or "absorbance")`);if(r.length!==o.length)throw new Error(`Spectrum ${t}: x and y arrays must have the same length (got ${r.length} and ${o.length})`);let n=e.label??e.title??e.name??`Spectrum ${t+1}`;return{id:`json-${++Qr}`,label:n,x:new Float64Array(r),y:new Float64Array(o),xUnit:e.xUnit,yUnit:e.yUnit,type:e.type,meta:e.meta}}var It=0,ce=null,Ut=!1;async function en(){if(Ut)return ce;Ut=!0;try{ce=await import("jcampconverter")}catch{ce=null}return ce}function $t(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 qe(e){let t=await en();return t?tn(e,t):[rn(e)]}function tn(e,t){return t.convert(e,{keepRecordsRegExp:/.*/}).flatten.map((o,n)=>{let s=o.spectra?.[0]?.data?.[0];if(!s)throw new Error(`JCAMP block ${n}: no spectral data found`);return{id:`jcamp-${++It}`,label:o.info?.TITLE??`Spectrum ${n+1}`,x:new Float64Array(s.x),y:new Float64Array(s.y),xUnit:o.info?.XUNITS??"cm\u207B\xB9",yUnit:o.info?.YUNITS??"Absorbance",type:$t(o.info),meta:o.info}})}function rn(e){let t=e.split(/\r?\n/),r={},o=[],n=[],s=!1;for(let a of t){let i=a.trim();if(i.startsWith("##")){let l=i.match(/^##(.+?)=\s*(.*)$/);if(l){let c=l[1].trim().toUpperCase(),u=l[2].trim();if(c==="XYDATA"||c==="XYPOINTS"){s=!0;continue}if(c==="END"){s=!1;continue}r[c]=u}continue}if(s&&i!==""){let l=i.split(/[\s,]+/).map(Number);if(l.length>=2&&!l.some(isNaN)){let c=l[0],u=parseFloat(r.FIRSTX??"0"),p=parseFloat(r.LASTX??"0"),f=parseInt(r.NPOINTS??"0",10);if(f>0&&l.length===2)o.push(l[0]),n.push(l[1]);else if(l.length>1){let m=f>1?(p-u)/(f-1):0;for(let d=1;d<l.length;d++)o.push(c+(d-1)*m),n.push(l[d])}}}}if(o.length===0)throw new Error("Failed to parse JCAMP-DX: no data found. Install jcampconverter for full format support.");return{id:`jcamp-${++It}`,label:r.TITLE??"JCAMP Spectrum",x:new Float64Array(o),y:new Float64Array(n),xUnit:r.XUNITS??"cm\u207B\xB9",yUnit:r.YUNITS??"Absorbance",type:$t(r),meta:r}}function nn(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 on(e=[]){let[t,r]=Qe(e),[o,n]=Qe(!1),[s,a]=Qe(null),i=_(async(m,d)=>{n(!0),a(null);try{let g;switch(d){case"jcamp":g=await qe(m);break;case"csv":g=[Ge(m)];break;case"json":g=Je(m);break}r(h=>[...h,...g])}catch(g){let h=g instanceof Error?g.message:"Failed to parse file";a(h)}finally{n(!1)}},[]),l=_(async m=>{let d=nn(m.name);if(!d){a(`Unsupported file format: ${m.name}`);return}let g=await m.text();await i(g,d)},[i]),c=_(m=>{r(d=>[...d,m])},[]),u=_(m=>{r(d=>d.filter(g=>g.id!==m))},[]),p=_(m=>{r(d=>d.map(g=>g.id===m?{...g,visible:g.visible===!1}:g))},[]),f=_(()=>{r([]),a(null)},[]);return{spectra:t,loading:o,error:s,loadFile:l,loadText:i,addSpectrum:c,removeSpectrum:u,toggleVisibility:p,clear:f}}import{useCallback as me}from"react";var Nt={solid:"",dashed:"8 4",dotted:"2 2","dash-dot":"8 4 2 4"};function et(e,t,r,o){let{width:n,height:s,background:a="#ffffff",title:i}=o,l=e.filter(c=>c.visible!==!1).map((c,u)=>{let p=c.color??A(u),f=c.lineStyle??"solid",m=c.lineWidth??1.5,d=Nt[f]??"",g=Math.min(c.x.length,c.y.length);if(g<2)return"";let h=[];for(let b=0;b<g;b++){let y=t(c.x[b]).toFixed(2),S=r(c.y[b]).toFixed(2);h.push(`${b===0?"M":"L"}${y},${S}`)}return`<path d="${h.join(" ")}" fill="none" stroke="${p}" stroke-width="${m}"${d?` stroke-dasharray="${d}"`:""}/>
|
|
3
3
|
<!-- ${c.label} -->`}).filter(Boolean).join(`
|
|
4
4
|
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
5
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="${n}" height="${
|
|
6
|
-
<rect width="${n}" height="${
|
|
5
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${n}" height="${s}" viewBox="0 0 ${n} ${s}">
|
|
6
|
+
<rect width="${n}" height="${s}" fill="${a}"/>
|
|
7
7
|
${i?`<text x="${n/2}" y="20" text-anchor="middle" font-family="system-ui" font-size="14">${i}</text>`:""}
|
|
8
8
|
<g>
|
|
9
9
|
${l}
|
|
10
10
|
</g>
|
|
11
|
-
</svg>`}function
|
|
11
|
+
</svg>`}function tt(e,t="spectrum.svg"){let r=new Blob([e],{type:"image/svg+xml"}),o=URL.createObjectURL(r),n=document.createElement("a");n.href=o,n.download=t,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(o)}function ue(e,t){let r=URL.createObjectURL(e),o=document.createElement("a");o.href=r,o.download=t,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(r)}function an(){let e=me((n,s="spectrum.png")=>{n.toBlob(a=>{a&&ue(a,s)},"image/png")},[]),t=me((n,s="spectra.csv")=>{let a=n.filter(i=>i.visible!==!1);if(a.length!==0)if(a.length===1){let i=a[0],l=`${i.xUnit??"x"},${i.yUnit??"y"}
|
|
12
12
|
`,c=Array.from(i.x).map((p,f)=>`${p},${i.y[f]}`),u=l+c.join(`
|
|
13
|
-
`);
|
|
13
|
+
`);ue(new Blob([u],{type:"text/csv"}),s)}else{let i=Math.max(...a.map(p=>p.x.length)),l=a.map(p=>`${p.label}_x,${p.label}_y`).join(","),c=[];for(let p=0;p<i;p++){let f=a.map(m=>p<m.x.length?`${m.x[p]},${m.y[p]}`:",");c.push(f.join(","))}let u=l+`
|
|
14
14
|
`+c.join(`
|
|
15
|
-
`);
|
|
15
|
+
`);ue(new Blob([u],{type:"text/csv"}),s)}},[]),r=me((n,s="spectra.json")=>{let i=n.filter(c=>c.visible!==!1).map(c=>({label:c.label,x:Array.from(c.x),y:Array.from(c.y),xUnit:c.xUnit,yUnit:c.yUnit,type:c.type})),l=JSON.stringify(i,null,2);ue(new Blob([l],{type:"application/json"}),s)},[]),o=me((n,s,a,i,l,c="spectrum.svg")=>{let u=et(n,s,a,{width:i,height:l});tt(u,c)},[]);return{exportPng:e,exportSvg:o,exportCsv:t,exportJson:r}}import{useCallback as sn,useState as ln}from"react";import{jsx as J,jsxs as Ot}from"react/jsx-runtime";var cn=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"}),mn=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"}),pe=e=>({display:"block",width:"100%",padding:"6px 12px",border:"none",background:"transparent",color:e==="dark"?"#d1d5db":"#374151",fontSize:12,textAlign:"left",cursor:"pointer"});function un({theme:e,onExportPng:t,onExportSvg:r,onExportCsv:o,onExportJson:n}){let[s,a]=ln(!1),i=sn(l=>{a(!1),l?.()},[]);return Ot("div",{style:{position:"relative",display:"inline-block"},children:[J("button",{type:"button",style:cn(e),onClick:()=>a(!s),"aria-label":"Export","aria-expanded":s,"aria-haspopup":"true",children:"Export"}),s&&Ot("div",{style:mn(e),role:"menu",children:[t&&J("button",{type:"button",role:"menuitem",style:pe(e),onClick:()=>i(t),children:"PNG Image"}),r&&J("button",{type:"button",role:"menuitem",style:pe(e),onClick:()=>i(r),children:"SVG Vector"}),o&&J("button",{type:"button",role:"menuitem",style:pe(e),onClick:()=>i(o),children:"CSV Data"}),n&&J("button",{type:"button",role:"menuitem",style:pe(e),onClick:()=>i(n),children:"JSON Data"})]})]})}function pn(e){let t=e.length;if(t<3)return new Float64Array(e);let r=[0];for(let a=1;a<t;a++){for(;r.length>=2;){let i=r.length-1,l=r[i-1],c=r[i];if((a-l)*(e[c]-e[l])-(c-l)*(e[a]-e[l])>=0)r.pop();else break}r.push(a)}let o=new Float64Array(t),n=0;for(let a=0;a<t;a++){for(;n<r.length-1&&r[n+1]<=a;)n++;if(n>=r.length-1)o[a]=e[r[r.length-1]];else{let i=r[n],l=r[n+1],c=(a-i)/(l-i);o[a]=e[i]*(1-c)+e[l]*c}}let s=new Float64Array(t);for(let a=0;a<t;a++)s[a]=e[a]-o[a];return s}function fn(e){let t=e.length,r=new Float64Array(t),o=1/0,n=-1/0;for(let a=0;a<t;a++){let i=e[a];i<o&&(o=i),i>n&&(n=i)}let s=n-o;if(s===0)return r;for(let a=0;a<t;a++)r[a]=(e[a]-o)/s;return r}function dn(e,t){let r=Math.min(e.length,t.length);if(r<2)return new Float64Array(t);let o=0;for(let s=1;s<r;s++)o+=Math.abs(e[s]-e[s-1])*(Math.abs(t[s])+Math.abs(t[s-1]))*.5;if(o===0)return new Float64Array(t);let n=new Float64Array(r);for(let s=0;s<r;s++)n[s]=t[s]/o;return n}function bn(e){let t=e.length;if(t===0)return new Float64Array(0);let r=0;for(let i=0;i<t;i++)r+=e[i];let o=r/t,n=0;for(let i=0;i<t;i++){let l=e[i]-o;n+=l*l}let s=Math.sqrt(n/t);if(s===0)return new Float64Array(t);let a=new Float64Array(t);for(let i=0;i<t;i++)a[i]=(e[i]-o)/s;return a}function gn(e,t=5){let r=e.length;if(r<t||t<3)return new Float64Array(e);let o=t%2===0?t+1:t,n=(o-1)/2,s=hn(o),a=new Float64Array(r);for(let i=0;i<n;i++)a[i]=e[i],a[r-1-i]=e[r-1-i];for(let i=n;i<r-n;i++){let l=0;for(let c=-n;c<=n;c++)l+=s[c+n]*e[i+c];a[i]=l}return a}function hn(e){let t={5:[-3,12,17,12,-3].map(r=>r/35),7:[-2,3,6,7,6,3,-2].map(r=>r/21),9:[-21,14,39,54,59,54,39,14,-21].map(r=>r/231),11:[-36,9,44,69,84,89,84,69,44,9,-36].map(r=>r/429)};return t[e]?t[e]:Array(e).fill(1/e)}function yn(e,t){let r=Math.min(e.length,t.length);if(r<2)return new Float64Array(r);let o=new Float64Array(r);o[0]=(t[1]-t[0])/(e[1]-e[0]);for(let n=1;n<r-1;n++)o[n]=(t[n+1]-t[n-1])/(e[n+1]-e[n-1]);return o[r-1]=(t[r-1]-t[r-2])/(e[r-1]-e[r-2]),o}function xn(e,t){let r=Math.min(e.length,t.length);if(r<3)return new Float64Array(r);let o=new Float64Array(r);for(let n=1;n<r-1;n++){let s=e[n]-e[n-1],a=e[n+1]-e[n],i=(s+a)/2;o[n]=(t[n+1]-2*t[n]+t[n-1])/(i*i)}return o[0]=o[1],o[r-1]=o[r-2],o}var q=0;function Sn(e,t){let r=Math.min(e.y.length,t.y.length),o=new Float64Array(r);for(let n=0;n<r;n++)o[n]=e.y[n]-t.y[n];return{id:`diff-${++q}`,label:`${e.label} \u2212 ${t.label}`,x:e.x.length<=t.x.length?new Float64Array(e.x):new Float64Array(t.x),y:o,xUnit:e.xUnit,yUnit:e.yUnit,color:"#ef4444",type:e.type}}function vn(e,t){let r=Math.min(e.y.length,t.y.length),o=new Float64Array(r);for(let n=0;n<r;n++)o[n]=e.y[n]+t.y[n];return{id:`add-${++q}`,label:`${e.label} + ${t.label}`,x:e.x.length<=t.x.length?new Float64Array(e.x):new Float64Array(t.x),y:o,xUnit:e.xUnit,yUnit:e.yUnit,type:e.type}}function wn(e,t){let r=e.y.length,o=new Float64Array(r);for(let n=0;n<r;n++)o[n]=e.y[n]*t;return{id:`scaled-${++q}`,label:`${e.label} \xD7 ${t}`,x:new Float64Array(e.x),y:o,xUnit:e.xUnit,yUnit:e.yUnit,color:e.color,type:e.type}}function Cn(e,t){let r=Math.min(e.y.length,t.y.length);if(r===0)return 0;let o=0,n=0;for(let p=0;p<r;p++)o+=e.y[p],n+=t.y[p];let s=o/r,a=n/r,i=0,l=0,c=0;for(let p=0;p<r;p++){let f=e.y[p]-s,m=t.y[p]-a;i+=f*m,l+=f*f,c+=m*m}let u=Math.sqrt(l*c);return u===0?0:i/u}function kn(e,t){let r=Math.min(e.y.length,t.y.length),o=new Float64Array(r);for(let n=0;n<r;n++)o[n]=Math.abs(e.y[n]-t.y[n]);return{id:`residual-${++q}`,label:`|${e.label} \u2212 ${t.label}|`,x:e.x.length<=t.x.length?new Float64Array(e.x):new Float64Array(t.x),y:o,xUnit:e.xUnit,yUnit:e.yUnit,color:"#f97316",lineStyle:"dashed",type:e.type}}function Rn(e,t){let r=Math.min(e.x.length,e.y.length),o=t.length,n=new Float64Array(o);if(r<2)return{...e,x:new Float64Array(t),y:n};let s=e.x[r-1]>e.x[0];for(let a=0;a<o;a++){let i=t[a],l=0,c=r-1;for(;l<c-1;){let d=l+c>>>1;s?e.x[d]<=i?l=d:c=d:e.x[d]>=i?l=d:c=d}let u=e.x[l],p=e.x[c],f=e.y[l],m=e.y[c];if(u===p)n[a]=f;else{let d=(i-u)/(p-u);n[a]=f+d*(m-f)}}return{...e,id:`interp-${++q}`,x:new Float64Array(t),y:n}}var Mn=0,Tn=1,An=4,En=128,Pn={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"},Ln={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 Fn(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 Dn(e){let t=new DataView(e);if(e.byteLength<512)throw new Error("Invalid SPC file: too small for SPC header");let o=t.getUint8(0),n=t.getUint8(1);if(n!==75&&n!==77)throw new Error(`Unsupported SPC version: 0x${n.toString(16)}. Expected 0x4B or 0x4D.`);let s=t.getUint8(2),a=t.getUint8(3),i=t.getUint32(4,!0),l=t.getFloat64(8,!0),c=t.getFloat64(16,!0),u=t.getUint32(24,!0),p=Pn[s]??"Arbitrary",f=Ln[a]??"Arbitrary",m=new Uint8Array(e,30,130),d=Un(m),g=(o&An)!==0,h=(o&En)!==0,b=(o&Tn)!==0,y=Fn(s,a),S=null;if(!h&&i>0){S=new Float64Array(i);let k=i>1?(c-l)/(i-1):0;for(let R=0;R<i;R++)S[R]=l+R*k}let v=[],x=512,w=null;if(h&&!g){w=new Float64Array(i);for(let k=0;k<i;k++)w[k]=t.getFloat32(x,!0),x+=4}let C=g?u:1;for(let k=0;k<C;k++){let R,T,P=i;if(g){if(x+32>e.byteLength)break;let L=t.getFloat32(x+4,!0),fe=t.getFloat32(x+8,!0);if(P=t.getUint32(x+12,!0)||i,x+=32,h){R=new Float64Array(P);for(let N=0;N<P&&!(x+4>e.byteLength);N++)R[N]=t.getFloat32(x,!0),x+=4}else if(S)R=S;else{R=new Float64Array(P);let N=P>1?(fe-L)/(P-1):0;for(let O=0;O<P;O++)R[O]=L+O*N}}else R=w??S??new Float64Array(0);if(T=new Float64Array(P),b)for(let L=0;L<P&&!(x+2>e.byteLength);L++)T[L]=t.getInt16(x,!0),x+=2;else for(let L=0;L<P&&!(x+4>e.byteLength);L++)T[L]=t.getFloat32(x,!0),x+=4;v.push({id:`spc-${++Mn}`,label:d||`SPC Spectrum ${k+1}`,x:R,y:T,xUnit:p,yUnit:f,type:y,meta:{format:"SPC",version:n===75?"new":"old",xType:s.toString(),yType:a.toString()}})}if(v.length===0)throw new Error("Invalid SPC file: no spectra found");return v}function Un(e){let t=e.indexOf(0),r=t>=0?e.slice(0,t):e;return new TextDecoder("ascii").decode(r).trim()}export{Fe as AnnotationLayer,Y as AxisLayer,Le as Crosshair,bt as DARK_THEME,Oe as DropZone,un as ExportMenu,Lr as KEYBOARD_SHORTCUTS,dt as LIGHT_THEME,Nt as LINE_DASH_PATTERNS,ae as Legend,_r as Minimap,Te as PeakMarkers,Ee as RegionSelector,ve as SPECTRUM_COLORS,Or as SpectraView,W as SpectrumCanvas,ze as StackedView,$e as Toolbar,Xr as Tooltip,vn as addSpectra,pn as baselineRubberBand,ie as binarySearchClosest,xe as computeXExtent,H as computeYExtent,Cn as correlationCoefficient,Se as createXScale,X as createYScale,yn as derivative1st,xn as derivative2nd,je as detectPeaks,Sn as differenceSpectrum,tt as downloadSvg,He as generateChartDescription,et as generateSvg,A as getSpectrumColor,z as getThemeColors,Rn as interpolateToGrid,Re as lttbDownsample,dn as normalizeArea,fn as normalizeMinMax,bn as normalizeSNV,Ge as parseCsv,qr as parseCsvMulti,qe as parseJcamp,Je as parseJson,Dn as parseSpc,Pr as prefersReducedMotion,kn as residualSpectrum,wn as scaleSpectrum,gn as smoothSavitzkyGolay,De as snapToNearestSpectrum,an as useExport,_e as useKeyboardNavigation,Kr as usePeakPicking,Be as useRegionSelect,Ze as useResizeObserver,on as useSpectrumData,ke as useZoomPan};
|
|
16
16
|
//# sourceMappingURL=index.js.map
|