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/dist/index.d.cts CHANGED
@@ -1,4 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
2
4
  import * as d3_scale from 'd3-scale';
3
5
  import { ScaleLinear } from 'd3-scale';
4
6
  import { ZoomTransform } from 'd3-zoom';
@@ -10,6 +12,8 @@ import { ZoomTransform } from 'd3-zoom';
10
12
  */
11
13
  /** Supported spectral technique types. */
12
14
  type SpectrumType = "IR" | "Raman" | "NIR" | "UV-Vis" | "fluorescence" | "other";
15
+ /** Supported line styles for spectrum rendering. */
16
+ type LineStyle = "solid" | "dashed" | "dotted" | "dash-dot";
13
17
  /** A single spectrum dataset. */
14
18
  interface Spectrum {
15
19
  /** Unique identifier. */
@@ -28,6 +32,10 @@ interface Spectrum {
28
32
  type?: SpectrumType;
29
33
  /** Rendering color (CSS color string). */
30
34
  color?: string;
35
+ /** Line style for rendering. */
36
+ lineStyle?: LineStyle;
37
+ /** Line width in pixels. */
38
+ lineWidth?: number;
31
39
  /** Whether this spectrum is visible. */
32
40
  visible?: boolean;
33
41
  /** Arbitrary metadata from file headers. */
@@ -55,6 +63,25 @@ interface Region {
55
63
  /** Optional fill color. */
56
64
  color?: string;
57
65
  }
66
+ /** A text annotation placed on the chart. */
67
+ interface Annotation {
68
+ /** Unique identifier. */
69
+ id: string;
70
+ /** Data-space x position (anchor point). */
71
+ x: number;
72
+ /** Data-space y position (anchor point). */
73
+ y: number;
74
+ /** Annotation text. */
75
+ text: string;
76
+ /** Pixel offset from anchor point [dx, dy]. Defaults to [0, -20]. */
77
+ offset?: [number, number];
78
+ /** Font size in pixels. Defaults to 11. */
79
+ fontSize?: number;
80
+ /** Text color (CSS). Defaults to theme text color. */
81
+ color?: string;
82
+ /** Show anchor line from text to data point. Defaults to true. */
83
+ showAnchorLine?: boolean;
84
+ }
58
85
  /** Current zoom/pan view state. */
59
86
  interface ViewState {
60
87
  /** Visible x-axis domain [min, max]. */
@@ -65,7 +92,9 @@ interface ViewState {
65
92
  /** Theme configuration. */
66
93
  type Theme = "light" | "dark";
67
94
  /** Display mode for multiple spectra. */
68
- type DisplayMode = "overlay";
95
+ type DisplayMode = "overlay" | "stacked";
96
+ /** Legend position relative to the chart. */
97
+ type LegendPosition$1 = "top" | "bottom" | "left" | "right";
69
98
  /** Margin configuration for the chart area. */
70
99
  interface Margin {
71
100
  top: number;
@@ -93,6 +122,10 @@ interface SpectraViewProps {
93
122
  peaks?: Peak[];
94
123
  /** Highlighted regions. */
95
124
  regions?: Region[];
125
+ /** Text annotations to display on the chart. */
126
+ annotations?: Annotation[];
127
+ /** Snap crosshair to nearest spectrum data point. Defaults to true. */
128
+ snapCrosshair?: boolean;
96
129
  /** X-axis label override. */
97
130
  xLabel?: string;
98
131
  /** Y-axis label override. */
@@ -103,8 +136,26 @@ interface SpectraViewProps {
103
136
  margin?: Partial<Margin>;
104
137
  /** Theme. */
105
138
  theme?: Theme;
139
+ /** Show legend (auto-shown when >1 spectrum). */
140
+ showLegend?: boolean;
141
+ /** Legend position. */
142
+ legendPosition?: LegendPosition$1;
143
+ /** Callback when a spectrum's visibility is toggled via legend. */
144
+ onToggleVisibility?: (id: string) => void;
145
+ /** Enable drag-and-drop file loading. */
146
+ enableDragDrop?: boolean;
147
+ /** Callback when files are dropped. */
148
+ onFileDrop?: (files: File[]) => void;
149
+ /** Enable interactive region selection (Shift+drag). */
150
+ enableRegionSelect?: boolean;
151
+ /** Callback when a region is created. */
152
+ onRegionSelect?: (region: Region) => void;
153
+ /** Responsive sizing (fills container width). */
154
+ responsive?: boolean;
106
155
  /** Custom CSS class name. */
107
156
  className?: string;
157
+ /** Ref to access the underlying Canvas element (for export). */
158
+ canvasRef?: React.RefObject<HTMLCanvasElement | null>;
108
159
  /** Callback when user clicks a peak marker. */
109
160
  onPeakClick?: (peak: Peak) => void;
110
161
  /** Callback when zoom/pan state changes. */
@@ -120,9 +171,14 @@ interface ResolvedConfig {
120
171
  showGrid: boolean;
121
172
  showCrosshair: boolean;
122
173
  showToolbar: boolean;
174
+ showLegend: boolean;
175
+ legendPosition: LegendPosition$1;
123
176
  displayMode: DisplayMode;
124
177
  margin: Margin;
125
178
  theme: Theme;
179
+ responsive: boolean;
180
+ enableDragDrop: boolean;
181
+ enableRegionSelect: boolean;
126
182
  }
127
183
 
128
184
  declare function SpectraView(props: SpectraViewProps): react_jsx_runtime.JSX.Element;
@@ -141,7 +197,7 @@ interface SpectrumCanvasProps {
141
197
  /** ID of the currently highlighted spectrum. */
142
198
  highlightedId?: string;
143
199
  }
144
- declare function SpectrumCanvas({ spectra, xScale, yScale, width, height, highlightedId, }: SpectrumCanvasProps): react_jsx_runtime.JSX.Element;
200
+ declare const SpectrumCanvas: react.ForwardRefExoticComponent<SpectrumCanvasProps & react.RefAttributes<HTMLCanvasElement>>;
145
201
 
146
202
  /**
147
203
  * Default color palette for rendering multiple spectra.
@@ -237,6 +293,19 @@ interface CrosshairPosition {
237
293
  /** Data-space y value. */
238
294
  dataY: number;
239
295
  }
296
+ /** Snap point data for rendering a snap dot on the nearest spectrum. */
297
+ interface SnapPoint {
298
+ /** Pixel x of the snapped data point. */
299
+ px: number;
300
+ /** Pixel y of the snapped data point. */
301
+ py: number;
302
+ /** Data-space x value. */
303
+ dataX: number;
304
+ /** Data-space y value. */
305
+ dataY: number;
306
+ /** Color of the spectrum (for dot fill). */
307
+ color?: string;
308
+ }
240
309
  interface CrosshairProps {
241
310
  /** Current crosshair position, or null when not hovering. */
242
311
  position: CrosshairPosition | null;
@@ -246,8 +315,10 @@ interface CrosshairProps {
246
315
  height: number;
247
316
  /** Theme colors. */
248
317
  colors: ThemeColors;
318
+ /** Optional snap point on nearest spectrum. */
319
+ snapPoint?: SnapPoint | null;
249
320
  }
250
- declare function Crosshair({ position, width, height, colors, }: CrosshairProps): react_jsx_runtime.JSX.Element | null;
321
+ declare function Crosshair({ position, width, height, colors, snapPoint, }: CrosshairProps): react_jsx_runtime.JSX.Element | null;
251
322
 
252
323
  interface ToolbarProps {
253
324
  /** Zoom in handler. */
@@ -261,7 +332,53 @@ interface ToolbarProps {
261
332
  /** Theme. */
262
333
  theme: Theme;
263
334
  }
264
- declare function Toolbar({ onZoomIn, onZoomOut, onReset, isZoomed, theme, }: ToolbarProps): react_jsx_runtime.JSX.Element;
335
+ declare const Toolbar: react.NamedExoticComponent<ToolbarProps>;
336
+
337
+ /** Position for the legend relative to the chart. */
338
+ type LegendPosition = "top" | "bottom" | "left" | "right";
339
+ interface LegendProps {
340
+ /** Spectra to list in the legend. */
341
+ spectra: Spectrum[];
342
+ /** Theme for styling. */
343
+ theme: Theme;
344
+ /** Legend position. */
345
+ position: LegendPosition;
346
+ /** Callback when a spectrum's visibility is toggled. */
347
+ onToggleVisibility?: (id: string) => void;
348
+ /** Callback when hovering a spectrum in the legend. */
349
+ onHighlight?: (id: string | null) => void;
350
+ /** Currently highlighted spectrum ID. */
351
+ highlightedId?: string | null;
352
+ }
353
+ declare const Legend: react.NamedExoticComponent<LegendProps>;
354
+
355
+ interface DropZoneProps {
356
+ /** Whether drag-drop is enabled. */
357
+ enabled: boolean;
358
+ /** Theme for styling the overlay. */
359
+ theme: Theme;
360
+ /** Width of the drop zone. */
361
+ width: number;
362
+ /** Height of the drop zone. */
363
+ height: number;
364
+ /** Callback when files are dropped. */
365
+ onDrop?: (files: File[]) => void;
366
+ /** Children to render inside the drop zone. */
367
+ children: ReactNode;
368
+ }
369
+ declare function DropZone({ enabled, theme, width, height, onDrop, children, }: DropZoneProps): react_jsx_runtime.JSX.Element;
370
+
371
+ interface AnnotationLayerProps {
372
+ /** Annotations to render. */
373
+ annotations: Annotation[];
374
+ /** X scale (zoomed). */
375
+ xScale: ScaleLinear<number, number>;
376
+ /** Y scale (zoomed). */
377
+ yScale: ScaleLinear<number, number>;
378
+ /** Theme colors. */
379
+ colors: ThemeColors;
380
+ }
381
+ declare function AnnotationLayer({ annotations, xScale, yScale, colors, }: AnnotationLayerProps): react_jsx_runtime.JSX.Element | null;
265
382
 
266
383
  /**
267
384
  * Hook for zoom and pan behavior backed by d3-zoom.
@@ -396,6 +513,8 @@ declare function useSpectrumData(initialSpectra?: Spectrum[]): UseSpectrumDataRe
396
513
  interface UseExportReturn {
397
514
  /** Export the canvas as a PNG data URL. */
398
515
  exportPng: (canvas: HTMLCanvasElement, filename?: string) => void;
516
+ /** Export visible spectra as SVG vector. */
517
+ exportSvg: (spectra: Spectrum[], xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>, width: number, height: number, filename?: string) => void;
399
518
  /** Export visible spectra as CSV text. */
400
519
  exportCsv: (spectra: Spectrum[], filename?: string) => void;
401
520
  /** Export visible spectra as JSON. */
@@ -406,6 +525,222 @@ interface UseExportReturn {
406
525
  */
407
526
  declare function useExport(): UseExportReturn;
408
527
 
528
+ /**
529
+ * Hook for interactive region selection via Shift+drag.
530
+ *
531
+ * Tracks mouse drag state when Shift is held, converting pixel
532
+ * coordinates to data-space values using the provided x-scale.
533
+ */
534
+
535
+ interface UseRegionSelectOptions {
536
+ /** Whether region selection is enabled. */
537
+ enabled: boolean;
538
+ /** X-axis scale for pixel-to-data conversion. */
539
+ xScale: ScaleLinear<number, number>;
540
+ /** Callback when a region is created. */
541
+ onRegionSelect?: (region: Region) => void;
542
+ }
543
+ interface UseRegionSelectReturn {
544
+ /** Pending region being dragged (null when not dragging). */
545
+ pendingRegion: Region | null;
546
+ /** Mouse down handler — call on the interaction rect. */
547
+ handleMouseDown: (event: React.MouseEvent<SVGRectElement>) => void;
548
+ /** Mouse move handler — call on the interaction rect. */
549
+ handleMouseMove: (event: React.MouseEvent<SVGRectElement>) => void;
550
+ /** Mouse up handler — call on the interaction rect. */
551
+ handleMouseUp: () => void;
552
+ }
553
+ declare function useRegionSelect(options: UseRegionSelectOptions): UseRegionSelectReturn;
554
+
555
+ /**
556
+ * Hook for responsive sizing via ResizeObserver.
557
+ *
558
+ * Tracks the width (and optionally height) of a container element,
559
+ * enabling the chart to auto-size to its parent.
560
+ */
561
+ interface ResizeObserverSize {
562
+ width: number;
563
+ height: number;
564
+ }
565
+ declare function useResizeObserver(): {
566
+ ref: React.RefCallback<HTMLElement>;
567
+ size: ResizeObserverSize | null;
568
+ };
569
+
570
+ /**
571
+ * Hook for keyboard navigation within the spectrum viewer.
572
+ *
573
+ * Handles arrow keys for panning, +/- for zoom, Escape for reset.
574
+ */
575
+ interface UseKeyboardNavigationOptions {
576
+ /** Zoom in handler. */
577
+ onZoomIn: () => void;
578
+ /** Zoom out handler. */
579
+ onZoomOut: () => void;
580
+ /** Reset zoom handler. */
581
+ onReset: () => void;
582
+ /** Whether keyboard navigation is enabled. */
583
+ enabled?: boolean;
584
+ }
585
+ declare function useKeyboardNavigation(options: UseKeyboardNavigationOptions): (event: React.KeyboardEvent) => void;
586
+
587
+ interface StackedViewProps {
588
+ /** Spectra to display. */
589
+ spectra: Spectrum[];
590
+ /** Zoomed x-scale (shared across panels). */
591
+ xScale: ScaleLinear<number, number>;
592
+ /** Full plot width. */
593
+ plotWidth: number;
594
+ /** Full plot height (will be divided among panels). */
595
+ plotHeight: number;
596
+ /** Chart margins. */
597
+ margin: Margin;
598
+ /** Theme. */
599
+ theme: Theme;
600
+ /** Show grid lines. */
601
+ showGrid: boolean;
602
+ /** X-axis label. */
603
+ xLabel: string;
604
+ /** Y-axis label. */
605
+ yLabel: string;
606
+ }
607
+ declare function StackedView({ spectra, xScale, plotWidth, plotHeight, margin, theme, showGrid, xLabel, yLabel, }: StackedViewProps): react_jsx_runtime.JSX.Element;
608
+
609
+ interface ExportMenuProps {
610
+ /** Theme for styling. */
611
+ theme: Theme;
612
+ /** Export as PNG. */
613
+ onExportPng?: () => void;
614
+ /** Export as SVG. */
615
+ onExportSvg?: () => void;
616
+ /** Export as CSV. */
617
+ onExportCsv?: () => void;
618
+ /** Export as JSON. */
619
+ onExportJson?: () => void;
620
+ }
621
+ declare function ExportMenu({ theme, onExportPng, onExportSvg, onExportCsv, onExportJson, }: ExportMenuProps): react_jsx_runtime.JSX.Element;
622
+
623
+ /**
624
+ * SVG export utility for generating publication-quality vector figures.
625
+ *
626
+ * Serializes a chart's SVG element to a standalone SVG string with
627
+ * embedded spectral data paths.
628
+ */
629
+
630
+ /** Line dash patterns for different line styles. */
631
+ declare const LINE_DASH_PATTERNS: Record<string, string>;
632
+ interface SvgExportOptions {
633
+ /** Width of the SVG. */
634
+ width: number;
635
+ /** Height of the SVG. */
636
+ height: number;
637
+ /** Background color. */
638
+ background?: string;
639
+ /** Title text for the SVG. */
640
+ title?: string;
641
+ }
642
+ /**
643
+ * Generate an SVG string for the given spectra.
644
+ */
645
+ declare function generateSvg(spectra: Spectrum[], xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>, options: SvgExportOptions): string;
646
+ /**
647
+ * Download an SVG string as a file.
648
+ */
649
+ declare function downloadSvg(svg: string, filename?: string): void;
650
+
651
+ /**
652
+ * Accessibility utilities for SpectraView.
653
+ *
654
+ * Provides helpers for reduced motion detection, ARIA label generation,
655
+ * and keyboard navigation constants.
656
+ */
657
+ /** Check if the user prefers reduced motion. */
658
+ declare function prefersReducedMotion(): boolean;
659
+ /** Generate an accessible description for a spectrum chart. */
660
+ declare function generateChartDescription(spectrumCount: number, xLabel: string, yLabel: string): string;
661
+ /** Keyboard shortcut definitions. */
662
+ declare const KEYBOARD_SHORTCUTS: {
663
+ readonly PAN_LEFT: "ArrowLeft";
664
+ readonly PAN_RIGHT: "ArrowRight";
665
+ readonly PAN_UP: "ArrowUp";
666
+ readonly PAN_DOWN: "ArrowDown";
667
+ readonly ZOOM_IN: "+";
668
+ readonly ZOOM_IN_ALT: "=";
669
+ readonly ZOOM_OUT: "-";
670
+ readonly RESET: "Escape";
671
+ readonly NEXT_PEAK: "Tab";
672
+ readonly PREV_PEAK: "Shift+Tab";
673
+ };
674
+
675
+ /**
676
+ * Binary search utilities for snapping crosshair to nearest spectrum data.
677
+ *
678
+ * Given a cursor x-position, finds the closest data point on visible spectra
679
+ * using binary search for O(log n) performance.
680
+ */
681
+
682
+ /** Result of a snap-to-spectrum search. */
683
+ interface SnapResult {
684
+ /** Spectrum ID of the closest match. */
685
+ spectrumId: string;
686
+ /** Index within the spectrum's data arrays. */
687
+ index: number;
688
+ /** Data-space x value of the snapped point. */
689
+ x: number;
690
+ /** Data-space y value of the snapped point. */
691
+ y: number;
692
+ /** Pixel distance from cursor to the snapped point. */
693
+ distance: number;
694
+ }
695
+ /**
696
+ * Binary search for the index of the closest x-value in a sorted array.
697
+ *
698
+ * Works with both ascending and descending arrays.
699
+ * Returns the index of the element closest to `target`.
700
+ */
701
+ declare function binarySearchClosest(arr: Float64Array | number[], target: number, length: number): number;
702
+ /**
703
+ * Find the nearest data point across all visible spectra to a given
704
+ * data-space x position. Uses pixel-space distance for ranking.
705
+ */
706
+ declare function snapToNearestSpectrum(spectra: Spectrum[], dataX: number, cursorPy: number, xScale: (v: number) => number, yScale: (v: number) => number): SnapResult | null;
707
+
708
+ /**
709
+ * Largest-Triangle-Three-Buckets (LTTB) downsampling algorithm.
710
+ *
711
+ * LTTB produces visually superior downsampled representations compared to
712
+ * simple min-max binning. It works by dividing data into buckets and
713
+ * selecting the point in each bucket that forms the largest triangle with
714
+ * the selected points in adjacent buckets.
715
+ *
716
+ * Reference: Sveinn Steinarsson, "Downsampling Time Series for Visual
717
+ * Representation" (2013).
718
+ *
719
+ * @module lttb
720
+ */
721
+ /** A downsampled point with its original index. */
722
+ interface LTTBPoint {
723
+ /** Pixel x coordinate. */
724
+ px: number;
725
+ /** Pixel y coordinate. */
726
+ py: number;
727
+ /** Original index in the source arrays. */
728
+ index: number;
729
+ }
730
+ /**
731
+ * Downsample data using the LTTB algorithm.
732
+ *
733
+ * @param x - Source x-values
734
+ * @param y - Source y-values
735
+ * @param startIdx - Start index (inclusive) in the source arrays
736
+ * @param endIdx - End index (exclusive) in the source arrays
737
+ * @param xScale - Function mapping data x to pixel x
738
+ * @param yScale - Function mapping data y to pixel y
739
+ * @param targetCount - Desired number of output points
740
+ * @returns Array of downsampled points
741
+ */
742
+ declare function lttbDownsample(x: Float64Array | number[], y: Float64Array | number[], startIdx: number, endIdx: number, xScale: (v: number) => number, yScale: (v: number) => number, targetCount: number): LTTBPoint[];
743
+
409
744
  /**
410
745
  * JCAMP-DX parser for spectral data.
411
746
  *
@@ -481,6 +816,32 @@ declare function parseCsvMulti(text: string, options?: Omit<CsvParseOptions, "xC
481
816
  */
482
817
  declare function parseJson(text: string): Spectrum[];
483
818
 
819
+ /**
820
+ * SPC file parser for Thermo/Galactic spectral data format.
821
+ *
822
+ * Parses the binary SPC format used by GRAMS, Thermo Scientific,
823
+ * PerkinElmer, and other spectroscopy software.
824
+ *
825
+ * Supports:
826
+ * - Single and multi-spectrum files
827
+ * - Even and uneven X spacing
828
+ * - 32-bit float and 16-bit integer Y data
829
+ * - File header metadata (resolution, instrument, etc.)
830
+ *
831
+ * Reference: "The New Galactic SPC File Format" specification
832
+ *
833
+ * @module spc
834
+ */
835
+
836
+ /**
837
+ * Parse an SPC binary file into Spectrum objects.
838
+ *
839
+ * @param buffer - ArrayBuffer containing the SPC file data
840
+ * @returns Array of parsed Spectrum objects
841
+ * @throws Error if the file is not a valid SPC file
842
+ */
843
+ declare function parseSpc(buffer: ArrayBuffer): Spectrum[];
844
+
484
845
  /**
485
846
  * Compute the x-axis extent across all visible spectra.
486
847
  */
@@ -501,4 +862,4 @@ declare function createXScale(domain: [number, number], width: number, margin: M
501
862
  */
502
863
  declare function createYScale(domain: [number, number], height: number, margin: Margin): d3_scale.ScaleLinear<number, number, never>;
503
864
 
504
- export { AxisLayer, Crosshair, type CrosshairPosition, type CrosshairProps, type CsvParseOptions, DARK_THEME, type DisplayMode, LIGHT_THEME, type Margin, type Peak, type PeakDetectionOptions, PeakMarkers, type Region, RegionSelector, type ResolvedConfig, SPECTRUM_COLORS, SpectraView, type SpectraViewProps, type Spectrum, SpectrumCanvas, type SpectrumType, type Theme, Toolbar, type UseExportReturn, type UsePeakPickingOptions, type UseSpectrumDataReturn, type UseZoomPanOptions, type UseZoomPanReturn, type ViewState, type ZoomPanState, computeXExtent, computeYExtent, createXScale, createYScale, detectPeaks, getSpectrumColor, getThemeColors, parseCsv, parseCsvMulti, parseJcamp, parseJson, useExport, usePeakPicking, useSpectrumData, useZoomPan };
865
+ 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, 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, 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 };