spectraview 1.7.0 → 1.8.1

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