spectraview 1.8.0 → 1.8.2

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/CHANGELOG.md CHANGED
@@ -5,6 +5,211 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [1.8.2] — 2026-02-26
9
+
10
+ ### Added
11
+
12
+ - ESLint 9 flat config with TypeScript, React, and React Hooks rules
13
+ - Test coverage reporting in CI with PR comments and dynamic badge
14
+ - Tests for StackedView (8), useExport (9), useResizeObserver (8) — 25 new tests
15
+ - Coverage badge in README
16
+
17
+ ### Fixed
18
+
19
+ - `Tooltip.tsx`: React rules-of-hooks violation (early return before useMemo)
20
+ - `spc.ts`: `let` → `const` for single-assignment variable
21
+ - README: `pip install spectrakit` → `pip install pyspectrakit`
22
+
23
+ ## [1.8.1] — 2026-02-26
24
+
25
+ ### Added
26
+
27
+ - 9 new Storybook stories: Legend, AnnotationLayer, DataTable, DropZone, ExportMenu, Minimap, Tooltip, StackedView, SpectrumCanvas (15 total, 100% component coverage)
28
+ - `.prettierrc` and `.editorconfig` for consistent formatting
29
+ - `SECURITY.md` with vulnerability reporting policy
30
+ - `.github/FUNDING.yml` for GitHub Sponsors
31
+ - GitHub issue templates (bug report, feature request)
32
+ - Pull request template with checklist
33
+
34
+ ### Changed
35
+
36
+ - CHANGELOG.md now documents all versions 0.1.0 through 1.8.0
37
+ - CONTRIBUTING.md updated: pnpm → npm, added Storybook development section
38
+ - CITATION.cff version updated to 1.8.0 with new keywords
39
+ - Removed empty `demo/` and `paper/` directories
40
+
41
+ ## [1.8.0] — 2026-02-26
42
+
43
+ ### Changed
44
+
45
+ - Added npm keywords for better discoverability (SPC, canvas, d3, peak-detection, baseline-correction, savitzky-golay, normalization)
46
+ - Updated README with comprehensive API documentation covering all v1.x features
47
+
48
+ ## [1.7.0] — 2026-02-26
49
+
50
+ ### Added
51
+
52
+ - `DataTable` component — sortable tabular view of spectrum data with region highlighting
53
+ - `spectrumToCsv()` — single spectrum CSV export with range filtering, delimiter, and precision options
54
+ - `multiSpectraToCsv()` — multi-spectrum CSV with shared X column
55
+ - `spectrumToJson()` — JSON export with range filtering
56
+ - `downloadString()` — browser file download trigger utility
57
+
58
+ ## [1.6.0] — 2026-02-26
59
+
60
+ ### Added
61
+
62
+ - `useHistory` hook — generic undo/redo with configurable max depth
63
+ - Command pattern: `push()`, `undo()`, `redo()`, `reset()`, `canUndo`, `canRedo`
64
+
65
+ ## [1.5.0] — 2026-02-26
66
+
67
+ ### Added
68
+
69
+ - `useNormalization` hook — reactive spectral transformations for in-viewer processing
70
+ - Supports 7 modes: none, min-max, area, SNV, baseline, smooth, derivative
71
+ - Memoized transformation pipeline
72
+
73
+ ## [1.4.0] — 2026-02-26
74
+
75
+ ### Added
76
+
77
+ - `Tooltip` component — multi-spectrum hover tooltip with nearest peak indicator
78
+ - Binary search for O(log n) value lookup at cursor position
79
+ - Configurable number format (auto, fixed2, fixed4, scientific)
80
+ - Smart positioning to avoid clipping at plot edges
81
+
82
+ ## [1.3.0] — 2026-02-26
83
+
84
+ ### Added
85
+
86
+ - `differenceSpectrum()` — element-wise subtraction (A − B)
87
+ - `addSpectra()` — element-wise addition (A + B)
88
+ - `scaleSpectrum()` — scalar multiplication
89
+ - `correlationCoefficient()` — Pearson correlation coefficient
90
+ - `residualSpectrum()` — absolute difference |A − B|
91
+ - `interpolateToGrid()` — linear interpolation to align spectra on a common X grid
92
+
93
+ ## [1.2.0] — 2026-02-26
94
+
95
+ ### Added
96
+
97
+ - `Minimap` component — canvas-rendered overview navigator with SVG viewport overlay
98
+ - Dims areas outside the visible viewport when zoomed
99
+ - Decimated rendering for performance at any data size
100
+
101
+ ## [1.1.0] — 2026-02-26
102
+
103
+ ### Added
104
+
105
+ - `baselineRubberBand()` — rubber-band baseline correction via lower convex hull
106
+ - `normalizeMinMax()` — scale Y values to [0, 1]
107
+ - `normalizeArea()` — area normalization using trapezoidal integration
108
+ - `normalizeSNV()` — Standard Normal Variate normalization
109
+ - `smoothSavitzkyGolay()` — Savitzky-Golay smoothing with pre-computed coefficients (windows 5–11)
110
+ - `derivative1st()` — first derivative via central differences
111
+ - `derivative2nd()` — second derivative via central differences
112
+
113
+ ## [1.0.0] — 2026-02-26
114
+
115
+ ### Added
116
+
117
+ - `parseSpc()` — SPC binary format parser (Thermo/Galactic .spc files)
118
+ - Supports single and multi-spectrum SPC files
119
+ - Handles 16-bit integer and 32-bit float Y data
120
+ - Automatic X/Y unit label detection from SPC type codes
121
+
122
+ ### Changed
123
+
124
+ - Stable public API — first major release
125
+
126
+ ## [0.9.0] — 2026-02-26
127
+
128
+ ### Added
129
+
130
+ - `lttbDownsample()` — Largest-Triangle-Three-Buckets downsampling algorithm
131
+
132
+ ### Changed
133
+
134
+ - Replaced min-max decimation with LTTB in canvas rendering for better visual fidelity
135
+ - Wrapped `Toolbar` and `Legend` in `React.memo` for performance
136
+
137
+ ### Removed
138
+
139
+ - `decimateMinMax()` internal function (replaced by LTTB)
140
+
141
+ ## [0.8.0] — 2026-02-26
142
+
143
+ ### Added
144
+
145
+ - `AnnotationLayer` component — text annotations with anchor dots and optional anchor lines
146
+ - `binarySearchClosest()` — O(log n) binary search for sorted arrays (ascending and descending)
147
+ - `snapToNearestSpectrum()` — finds nearest data point across all spectra in pixel space
148
+ - `snapCrosshair` prop on `<SpectraView />` — crosshair snaps to nearest data point (default: true)
149
+ - `annotations` prop on `<SpectraView />` — render text annotations on the chart
150
+ - `Annotation` type in core types
151
+
152
+ ### Changed
153
+
154
+ - `Crosshair` component now renders a snap dot with spectrum color when snapped
155
+
156
+ ## [0.7.0] — 2026-02-26
157
+
158
+ ### Added
159
+
160
+ - `useKeyboardNavigation` hook — keyboard shortcuts: +/= zoom in, − zoom out, Esc reset
161
+ - `generateChartDescription()` — generates ARIA-friendly chart descriptions
162
+ - `prefersReducedMotion()` — detect reduced motion preference
163
+ - `KEYBOARD_SHORTCUTS` constant for discoverability
164
+ - ARIA attributes on `<SpectraView />`: `role="img"`, `aria-label`, `tabIndex`
165
+
166
+ ## [0.6.0] — 2026-02-25
167
+
168
+ ### Added
169
+
170
+ - `ExportMenu` component — dropdown menu with PNG, SVG, CSV, JSON export options
171
+ - `generateSvg()` — programmatic SVG export of chart
172
+ - `downloadSvg()` — trigger SVG file download
173
+ - `LINE_DASH_PATTERNS` — dash patterns for solid, dashed, dotted, dash-dot line styles
174
+ - `LineStyle` type and `lineStyle` / `lineWidth` properties on `Spectrum`
175
+
176
+ ## [0.5.0] — 2026-02-25
177
+
178
+ ### Added
179
+
180
+ - `StackedView` component — vertically separated panels for multi-spectrum display
181
+ - `displayMode` prop: `"overlay"` (default) or `"stacked"`
182
+ - `responsive` prop — auto-sizes component to fill container width
183
+ - `useResizeObserver` hook — container size observation
184
+
185
+ ## [0.4.0] — 2026-02-25
186
+
187
+ ### Added
188
+
189
+ - `DropZone` component — drag-and-drop file loading overlay
190
+ - `RegionSelector` component — interactive Shift+drag region selection
191
+ - `useRegionSelect` hook — handles region selection state and callbacks
192
+ - `enableDragDrop`, `onFileDrop`, `enableRegionSelect`, `onRegionSelect` props
193
+
194
+ ## [0.3.0] — 2026-02-25
195
+
196
+ ### Added
197
+
198
+ - `Legend` component — spectrum list with color swatches, visibility toggles, hover highlight
199
+ - `showLegend` and `legendPosition` props on `<SpectraView />`
200
+ - `onToggleVisibility` callback for spectrum hide/show
201
+ - Auto-shown when more than one spectrum is displayed
202
+
203
+ ## [0.1.1] — 2026-02-25
204
+
205
+ ### Fixed
206
+
207
+ - Counter-based clipPath IDs replaced with `useId()` to avoid collisions with multiple instances
208
+ - Canvas decimation for large datasets
209
+ - Export `ref` forwarding to internal canvas element
210
+ - CJS type resolution with split `.d.cts` declarations
211
+ - CI switched from pnpm to npm
212
+
8
213
  ## [0.1.0] — 2026-02-25
9
214
 
10
215
  ### Added
package/README.md CHANGED
@@ -7,22 +7,34 @@ Interactive React component for vibrational spectroscopy (IR, Raman, NIR).
7
7
  [![bundle size](https://img.shields.io/bundlephobia/minzip/spectraview)](https://bundlephobia.com/package/spectraview)
8
8
  [![license](https://img.shields.io/npm/l/spectraview)](https://github.com/ktubhyam/spectraview/blob/main/LICENSE)
9
9
  [![CI](https://img.shields.io/github/actions/workflow/status/ktubhyam/spectraview/ci.yml?branch=main)](https://github.com/ktubhyam/spectraview/actions)
10
+ [![coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/ktubhyam/spectraview/coverage-badges/coverage.json)](https://github.com/ktubhyam/spectraview/actions/workflows/ci.yml)
10
11
  [![Storybook](https://img.shields.io/badge/storybook-deployed-ff4785)](https://ktubhyam.github.io/spectraview/)
11
12
 
12
13
  ## Features
13
14
 
14
- - **High-performance rendering** — Canvas 2D for spectral data (10K+ points at 60fps), SVG for axes and annotations
15
- - **Zoom and pan** — Mouse wheel zoom, click-drag pan, double-click reset
15
+ - **High-performance rendering** — Canvas 2D with LTTB downsampling (10K+ points at 60fps), SVG for axes and annotations
16
+ - **Zoom and pan** — Mouse wheel zoom, click-drag pan, double-click reset, keyboard shortcuts (+/−/Esc)
16
17
  - **Reversed x-axis** — Standard IR wavenumber convention (high → low)
17
18
  - **Peak detection** — Automatic peak picking with prominence filtering
18
- - **Region selection** — Click-drag to highlight wavenumber regions
19
- - **Multi-format loading** — JCAMP-DX, CSV/TSV, JSON
20
- - **Multi-spectrum overlay** — Compare spectra with automatic color assignment
21
- - **Crosshair** — Hover readout with coordinate display
22
- - **Export** — PNG image, CSV data, JSON
19
+ - **Snap crosshair** — Hover readout that snaps to nearest data point with spectrum color indicator
20
+ - **Region selection** — Shift+drag to select wavenumber regions interactively
21
+ - **Annotations** — Positioned text labels with anchor lines on the chart
22
+ - **Multi-format parsing** — JCAMP-DX, CSV/TSV, JSON, and SPC (Thermo/Galactic binary)
23
+ - **Multi-spectrum overlay** — Compare spectra with automatic color assignment and legend
24
+ - **Stacked display** — View multiple spectra in vertically separated panels
25
+ - **Spectral processing** — Baseline correction (rubber-band), normalization (min-max, area, SNV), Savitzky-Golay smoothing, 1st/2nd derivatives
26
+ - **Spectrum comparison** — Difference, addition, scaling, Pearson correlation, residuals, grid interpolation
27
+ - **Export** — PNG, SVG, CSV, JSON with range filtering and precision control
28
+ - **Data table** — Sortable tabular view of spectrum values with region highlighting
29
+ - **Minimap** — Overview navigator showing viewport position within full spectrum
30
+ - **Tooltip** — Multi-spectrum hover tooltip with nearest peak indicator
31
+ - **Undo/redo** — Generic history hook for state management
32
+ - **Drag-and-drop** — Built-in drop zone for loading spectrum files
33
+ - **Responsive** — Optional auto-sizing to fill container width
34
+ - **Accessible** — ARIA labels, keyboard navigation, screen reader support
23
35
  - **Themes** — Light and dark mode
24
36
  - **TypeScript** — Full type definitions included
25
- - **Tiny bundle** — ~50-70KB min+gzip (no Plotly dependency)
37
+ - **Tiny bundle** — ~48KB ESM / ~51KB CJS (no Plotly dependency)
26
38
 
27
39
  ## Installation
28
40
 
@@ -51,26 +63,36 @@ function App() {
51
63
 
52
64
  ## Loading Files
53
65
 
66
+ SpectraView supports drag-and-drop file loading out of the box:
67
+
54
68
  ```tsx
55
- import { useSpectrumData, SpectraView } from "spectraview";
69
+ import { SpectraView, useSpectrumData } from "spectraview";
56
70
 
57
71
  function App() {
58
72
  const { spectra, loadFile } = useSpectrumData();
59
73
 
60
- const handleDrop = (e: React.DragEvent) => {
61
- e.preventDefault();
62
- const file = e.dataTransfer.files[0];
63
- if (file) loadFile(file);
64
- };
65
-
66
74
  return (
67
- <div onDrop={handleDrop} onDragOver={(e) => e.preventDefault()}>
68
- <SpectraView spectra={spectra} reverseX showCrosshair />
69
- </div>
75
+ <SpectraView
76
+ spectra={spectra}
77
+ reverseX
78
+ enableDragDrop
79
+ onFileDrop={(files) => files.forEach(loadFile)}
80
+ />
70
81
  );
71
82
  }
72
83
  ```
73
84
 
85
+ Or parse files manually:
86
+
87
+ ```ts
88
+ import { parseJcamp, parseCsv, parseJson, parseSpc } from "spectraview";
89
+
90
+ const spectra = await parseJcamp(jcampText); // JCAMP-DX (.dx, .jdx)
91
+ const spectrum = parseCsv(csvText); // CSV/TSV
92
+ const spectra = parseJson(jsonText); // JSON
93
+ const spectra = parseSpc(arrayBuffer); // SPC binary (.spc)
94
+ ```
95
+
74
96
  ## Peak Detection
75
97
 
76
98
  ```tsx
@@ -95,6 +117,98 @@ function App() {
95
117
  }
96
118
  ```
97
119
 
120
+ ## Spectral Processing
121
+
122
+ Apply common preprocessing transformations:
123
+
124
+ ```ts
125
+ import {
126
+ baselineRubberBand,
127
+ normalizeMinMax,
128
+ normalizeArea,
129
+ normalizeSNV,
130
+ smoothSavitzkyGolay,
131
+ derivative1st,
132
+ derivative2nd,
133
+ } from "spectraview";
134
+
135
+ const corrected = baselineRubberBand(spectrum.y); // Rubber-band baseline correction
136
+ const normed = normalizeMinMax(spectrum.y); // Scale to [0, 1]
137
+ const areaNormed = normalizeArea(spectrum.x, spectrum.y); // Unit area normalization
138
+ const snv = normalizeSNV(spectrum.y); // Standard Normal Variate
139
+ const smoothed = smoothSavitzkyGolay(spectrum.y, 7); // SG smoothing (window=7)
140
+ const dy = derivative1st(spectrum.x, spectrum.y); // 1st derivative
141
+ const d2y = derivative2nd(spectrum.x, spectrum.y); // 2nd derivative
142
+ ```
143
+
144
+ Or use the `useNormalization` hook for reactive transformations:
145
+
146
+ ```tsx
147
+ import { useNormalization, SpectraView } from "spectraview";
148
+
149
+ function App() {
150
+ const [mode, setMode] = useState("none");
151
+ const { spectra: processed, modeLabel } = useNormalization({
152
+ spectra: rawSpectra,
153
+ mode, // "none" | "min-max" | "area" | "snv" | "baseline" | "smooth" | "derivative"
154
+ });
155
+
156
+ return <SpectraView spectra={processed} reverseX />;
157
+ }
158
+ ```
159
+
160
+ ## Spectrum Comparison
161
+
162
+ ```ts
163
+ import {
164
+ differenceSpectrum,
165
+ addSpectra,
166
+ scaleSpectrum,
167
+ correlationCoefficient,
168
+ residualSpectrum,
169
+ interpolateToGrid,
170
+ } from "spectraview";
171
+
172
+ const diff = differenceSpectrum(spectrumA, spectrumB); // A - B
173
+ const sum = addSpectra(spectrumA, spectrumB); // A + B
174
+ const scaled = scaleSpectrum(spectrum, 2.5); // Scale Y by factor
175
+ const r = correlationCoefficient(spectrumA, spectrumB); // Pearson r ∈ [-1, 1]
176
+ const resid = residualSpectrum(spectrumA, spectrumB); // |A - B|
177
+
178
+ // Align spectra to a common X grid before comparison
179
+ const aligned = interpolateToGrid(spectrumB, spectrumA.x);
180
+ ```
181
+
182
+ ## Data Export
183
+
184
+ ```ts
185
+ import {
186
+ spectrumToCsv,
187
+ multiSpectraToCsv,
188
+ spectrumToJson,
189
+ downloadString,
190
+ generateSvg,
191
+ downloadSvg,
192
+ } from "spectraview";
193
+
194
+ // CSV export with options
195
+ const csv = spectrumToCsv(spectrum, {
196
+ delimiter: ",",
197
+ precision: 4,
198
+ xRange: [1000, 2000],
199
+ includeHeader: true,
200
+ });
201
+
202
+ // Multi-spectrum CSV (shared X column)
203
+ const multiCsv = multiSpectraToCsv([spectrumA, spectrumB]);
204
+
205
+ // JSON export
206
+ const json = spectrumToJson(spectrum, { xRange: [1000, 2000] });
207
+
208
+ // Trigger browser download
209
+ downloadString(csv, "spectrum.csv", "text/csv");
210
+ ```
211
+
98
212
  ## API Reference
99
213
 
100
214
  ### `<SpectraView />`
@@ -108,31 +222,73 @@ function App() {
108
222
  | `showGrid` | `boolean` | `true` | Show grid lines |
109
223
  | `showCrosshair` | `boolean` | `true` | Show hover crosshair |
110
224
  | `showToolbar` | `boolean` | `true` | Show zoom controls |
225
+ | `showLegend` | `boolean` | `true` | Show spectrum legend |
226
+ | `legendPosition` | `"top" \| "bottom"` | `"bottom"` | Legend placement |
227
+ | `displayMode` | `"overlay" \| "stacked"` | `"overlay"` | Multi-spectrum display mode |
228
+ | `responsive` | `boolean` | `false` | Auto-size to container width |
229
+ | `theme` | `"light" \| "dark"` | `"light"` | Color theme |
111
230
  | `peaks` | `Peak[]` | `[]` | Peak markers to display |
112
231
  | `regions` | `Region[]` | `[]` | Highlighted regions |
113
- | `xLabel` | `string` | auto | X-axis label |
114
- | `yLabel` | `string` | auto | Y-axis label |
115
- | `theme` | `"light" \| "dark"` | `"light"` | Color theme |
232
+ | `annotations` | `Annotation[]` | `[]` | Text annotations on the chart |
233
+ | `snapCrosshair` | `boolean` | `true` | Snap crosshair to nearest data point |
234
+ | `xLabel` | `string` | auto | X-axis label override |
235
+ | `yLabel` | `string` | auto | Y-axis label override |
236
+ | `margin` | `Partial<Margin>` | — | Custom chart margins |
237
+ | `enableDragDrop` | `boolean` | `false` | Enable drag-and-drop file loading |
238
+ | `enableRegionSelect` | `boolean` | `false` | Enable Shift+drag region selection |
239
+ | `className` | `string` | — | Custom CSS class |
240
+ | `canvasRef` | `RefObject<HTMLCanvasElement>` | — | Ref to canvas element (for export) |
116
241
  | `onPeakClick` | `(peak: Peak) => void` | — | Peak click callback |
117
- | `onViewChange` | `(view: ViewState) => void` | — | Zoom/pan callback |
118
- | `onCrosshairMove` | `(x, y) => void` | — | Crosshair move callback |
242
+ | `onViewChange` | `(view: ViewState) => void` | — | Zoom/pan change callback |
243
+ | `onCrosshairMove` | `(x: number, y: number) => void` | — | Crosshair move callback |
244
+ | `onToggleVisibility` | `(id: string) => void` | — | Legend visibility toggle callback |
245
+ | `onFileDrop` | `(files: File[]) => void` | — | File drop callback |
246
+ | `onRegionSelect` | `(region: Region) => void` | — | Region selection callback |
119
247
 
120
- ### Parsers
248
+ ### Sub-Components
121
249
 
122
- ```ts
123
- import { parseJcamp, parseCsv, parseJson } from "spectraview";
250
+ For advanced composition, individual layers are exported:
124
251
 
125
- const spectra = await parseJcamp(jcampText); // JCAMP-DX (.dx, .jdx)
126
- const spectrum = parseCsv(csvText); // CSV/TSV
127
- const spectra = parseJson(jsonText); // JSON
252
+ ```tsx
253
+ import {
254
+ SpectrumCanvas,
255
+ AxisLayer,
256
+ PeakMarkers,
257
+ RegionSelector,
258
+ Crosshair,
259
+ Toolbar,
260
+ Legend,
261
+ DropZone,
262
+ AnnotationLayer,
263
+ Minimap,
264
+ Tooltip,
265
+ DataTable,
266
+ StackedView,
267
+ ExportMenu,
268
+ } from "spectraview";
128
269
  ```
129
270
 
130
271
  ### Hooks
131
272
 
132
- - **`useSpectrumData()`** File loading and spectrum state management
133
- - **`useZoomPan(options)`** — Zoom/pan behavior backed by d3-zoom
134
- - **`usePeakPicking(spectra, options)`** Automatic peak detection
135
- - **`useExport()`** PNG, CSV, JSON export functions
273
+ | Hook | Description |
274
+ |------|-------------|
275
+ | `useSpectrumData()` | File loading and spectrum state management |
276
+ | `useZoomPan(options)` | Zoom/pan behavior backed by d3-zoom |
277
+ | `usePeakPicking(spectra, options)` | Automatic peak detection |
278
+ | `useExport()` | PNG, CSV, JSON export functions |
279
+ | `useRegionSelect(options)` | Interactive Shift+drag region selection |
280
+ | `useResizeObserver()` | Container resize observation for responsive sizing |
281
+ | `useKeyboardNavigation(options)` | Keyboard shortcuts (+/−/Esc for zoom/reset) |
282
+ | `useNormalization(options)` | Reactive spectral normalization/processing |
283
+ | `useHistory(options)` | Generic undo/redo with configurable depth |
284
+
285
+ ### Keyboard Shortcuts
286
+
287
+ | Key | Action |
288
+ |-----|--------|
289
+ | `+` or `=` | Zoom in |
290
+ | `-` | Zoom out |
291
+ | `Escape` | Reset zoom |
136
292
 
137
293
  ## Companion: SpectraKit (Python)
138
294
 
@@ -142,7 +298,7 @@ SpectraView pairs with [SpectraKit](https://github.com/ktubhyam/spectrakit), a P
142
298
  - **SpectraView** — View spectra: interactive visualization in the browser
143
299
 
144
300
  ```
145
- pip install spectrakit # Process in Python
301
+ pip install pyspectrakit # Process in Python
146
302
  npm install spectraview # View in the browser
147
303
  ```
148
304