diff --git a/README.md b/README.md index 11fac440..baf3fc2a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ $ yarn start [demo & step-by-step manual](https://github.com/ComPlat/react-spectra-editor/blob/master/DEMO_MANUAL.md) +### Documentation + +- [Developer documentation](docs/README.md): architecture and diagrams + ### Testing #### Unit test ``` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..1bb4f1ec --- /dev/null +++ b/docs/README.md @@ -0,0 +1,23 @@ +# Documentation + +Developer documentation for `@complat/react-spectra-editor` (`react-spectra-editor`). + +## Architecture + +- [High-Level Overview](architecture/high-level-overview.md): package purpose, system boundaries, layers, and rendering branches +- [Frontend Architecture](architecture/frontend-architecture.md): Redux, sagas, data pipeline, multi-curve/CV, forecast, and runtime synchronization + +## Diagrams + +- [Diagram Generation](diagram.md): maintain and regenerate Mermaid diagrams + +Regenerate all diagrams from the repository root: + +```bash +yarn docs:diagrams +``` + +## User-Facing Guides + +- [Demo & step-by-step manual](../DEMO_MANUAL.md): interactive demo usage +- [README](../README.md): installation and quick start diff --git a/docs/architecture/frontend-architecture.md b/docs/architecture/frontend-architecture.md new file mode 100644 index 00000000..e1340faa --- /dev/null +++ b/docs/architecture/frontend-architecture.md @@ -0,0 +1,241 @@ +# Frontend Architecture + +This document describes the internal runtime architecture of `@complat/react-spectra-editor`. It covers state, sagas, rendering, data transformation, multi-curve workflows, forecast workflows, and host integration contracts. + +Host props and callbacks are listed in [High-Level Overview](high-level-overview.md#data-entry-and-exit-points). This document maps those contracts to runtime code. + +## Redux Architecture + +The Redux store is created in [`src/app.js`](../../src/app.js) and uses the combined reducer from [`src/reducers/index.js`](../../src/reducers/index.js). The store is shared by the editor tree through `react-redux` and is the central coordination point for rendering state, editing state, multi-curve state, forecast state, and host-facing workflow state. + +
+ Redux Architecture +
+ +
+ +The root reducer combines these state domains: + +| Domain | Reducers | Runtime responsibility | +|---|---|---| +| Rendering and navigation | `layout`, `ui`, `scan`, `threshold`, `wavelength`, `axesUnits`, `detector` | Determines active layout, viewer mode, sweep mode, zoom extent, scan target, thresholds, axes, wavelength, and detector display state. | +| Spectrum editing | `editPeak`, `shift`, `integration`, `multiplicity`, `simulation` | Tracks user edits and NMR-specific derived editing state. | +| Multi-curve and CV | `curve`, `cyclicvolta` | Tracks active curve, curve list, CV peak pairs, reference state, current-density mode, and per-curve CV edits. | +| Host integration | `submit`, `forecast`, `jcamp` | Tracks selected submit operation, forecast state, and comparison spectra/callbacks. | +| Metadata and orchestration | `meta`, `status`, `manager` | Stores derived metadata and provides action namespaces used by sagas and reducers. | + +`editPeak`, `integration`, and `multiplicity` are undoable reducers. They are wrapped with `redux-undo` and share the action filter in [`src/reducers/undo_redo_config.js`](../../src/reducers/undo_redo_config.js). The undo configuration includes edit actions, integration actions, multiplicity actions, and manager reset actions. Reset-oriented actions clear history so that user-facing undo state stays aligned with the active spectrum and feature. + +Action namespaces are defined in [`src/constants/action_type.js`](../../src/constants/action_type.js). They are organized around runtime domains rather than React components: `MANAGER`, `UI`, `EDITPEAK`, `INTEGRATION`, `MULTIPLICITY`, `CURVE`, `CYCLIC_VOLTA_METRY`, `FORECAST`, `JCAMP`, `SUBMIT`, and related display domains. This allows sagas to translate UI-level actions into domain-level mutations without coupling every component directly to every reducer. + +Reducers interact operationally through shared action streams. A single action can be handled by multiple reducers and sagas. For example: + +- `MANAGER.RESETALL` resets layout/UI-facing state, clears comparison state, resets edit peak state, and triggers saga-managed shift propagation. +- `CURVE.SET_ALL_CURVES` rebuilds `curve.listCurves`, expands threshold state when needed, and triggers multi-entity sagas that initialize CV, integration, multiplicity, and simulation state. +- `UI.SWEEP.SELECT_INTEGRATION` is emitted from UI sweep interactions and consumed by the undoable integration reducer. +- Multiplicity workflows flow through saga actions first, then land in reducer actions with `_RDC` suffixes. + +The resulting model is action-driven synchronization: reducers own state mutations, sagas coordinate multi-slice effects, and connected components derive rendering inputs from the current store snapshot. + +## Saga Orchestration Architecture + +The root saga in [`src/sagas/index.js`](../../src/sagas/index.js) composes all orchestration modules with `yield all([...])`. Sagas exist where a single action must coordinate multiple state domains, derive additional payload data, or translate UI gestures into domain-specific actions. + +
+ Saga Orchestration +
+ +
+ +The saga modules have distinct runtime roles: + +| Saga module | Runtime role | +|---|---| +| [`saga_manager.js`](../../src/sagas/saga_manager.js) | Propagates manager resets into shift resets and initializes integration/simulation state for NMR and integration-enabled layouts. | +| [`saga_ui.js`](../../src/sagas/saga_ui.js) | Translates click, brush, and wheel interactions into edit peak, shift, integration, multiplicity, zoom, and CV actions according to `ui.sweepType`. | +| [`saga_meta.js`](../../src/sagas/saga_meta.js) | Computes peak metadata and DSC metadata reducer payloads. | +| [`saga_multiplicity.js`](../../src/sagas/saga_multiplicity.js) | Coordinates multiplicity-specific mutations and reducer payloads. | +| [`saga_edit_peak.js`](../../src/sagas/saga_edit_peak.js) | Synchronizes edited peaks with shift updates. | +| [`saga_multi_entities.js`](../../src/sagas/saga_multi_entities.js) | Initializes per-curve integrations, multiplicities, simulations, and cyclic voltammetry state from `curve.listCurves`. | + +Manager flows start with initialization or viewer resets. `LayerInit` dispatches layout-specific manager actions during `execReset()`. Viewers dispatch `resetAll(feature)` during mount and when their active feature changes. `saga_manager.js` listens to `MANAGER.RESETALL`, reads the current layout and curve state, and emits `MANAGER.RESETSHIFT` with curve metadata. That enriches downstream reducers with layout and multi-curve context. + +UI orchestration is driven by `ui.sweepType`. D3 interactions call `clickUiTarget`, `selectUiSweep`, or `scrollUiWheel`. `saga_ui.js` reads the current sweep mode and active `curveIdx`, then emits the domain action. The same click can mean "add peak", "delete peak", "anchor shift", "remove integration", "select multiplicity", or "edit cyclic voltammetry peak" depending on `ui.sweepType`. + +Multi-entity orchestration is triggered by `CURVE.SET_ALL_CURVES`. `saga_multi_entities.js` reads `curve.listCurves`, resets CV state, initializes CV area settings from the first curve, creates pair peaks per curve, initializes reference peaks, and populates integration/multiplicity arrays by curve index. This keeps multi-curve rendering, editing overlays, and submit payloads aligned. + +## Rendering Pipeline Architecture + +Rendering is split between React viewer wrappers and D3 focus classes. React owns component composition, lifecycle entry points, and Redux-connected props. D3 owns the SVG scene graph, scales, axes, paths, overlays, brushing, and interaction hit targets. + +
+ Rendering Pipeline +
+ +
+ +The viewer wrappers are: + +- [`ViewerLine`](../../src/components/d3_line/index.js), which wraps `LineFocus`. +- [`ViewerRect`](../../src/components/d3_rect/index.js), which wraps `RectFocus`. +- [`ViewerMulti`](../../src/components/d3_multi/index.js), which wraps `MultiFocus`. + +Each wrapper follows the same runtime pattern: + +1. Read derived data from Redux selectors and component props. +2. Destroy the previous root SVG under the viewer root when mounting or unmounting. +3. Dispatch `resetAll(feature)` to synchronize Redux with the active feature. +4. Call `drawMain()` to create the SVG shell. +5. Call the focus class `create()` method to mount axes, clips, paths, overlays, brush behavior, and tooltips. +6. On React updates, call the focus class `update()` method with new derived inputs. +7. Update labels and visibility using `drawLabel()` and `drawDisplay()`. + +`LineFocus` handles line spectra. It creates the main focus frame, clipping paths, scales, axes, grid, line path, threshold lines, peak overlays, reference overlays, integration overlays, multiplicity overlays, comparison paths, and brush behavior. Its `update()` method recalculates scales and redraws only the affected D3 layers based on stored `shouldUpdate` state. + +`RectFocus` follows the same wrapper pattern for MS-oriented rectangular rendering. + +`MultiFocus` handles multi-curve and cyclic voltammetry rendering. It selects the active curve, builds `otherLineData`, filters SEC/GC curves by unit compatibility, transforms CV values for current-density display, renders other curves with comparison paths, and manages CV peak/pecker interactions. + +D3 helper responsibilities are concentrated in: + +- [`src/components/common/draw.js`](../../src/components/common/draw.js): SVG creation, destruction, labels, visibility, and curve arrows. +- [`src/helpers/mount.js`](../../src/helpers/mount.js): SVG groups, axes, clips, paths, threshold lines, markers, compare paths, and overlay containers. +- [`src/helpers/brush.js`](../../src/helpers/brush.js): brush wiring. +- [`src/helpers/compass.js`](../../src/helpers/compass.js): pointer-to-data translation and rescaling helpers. +- [`src/helpers/init.js`](../../src/helpers/init.js): D3 scale, axis, and tooltip initialization. + +React does not render the SVG chart declaratively. React provides state and lifecycle signals; D3 mutates the SVG scene. Debug rendering issues by checking Redux-derived viewer props and focus-class D3 state together. + +## Data Transformation Pipeline + +The editor receives host-provided `entity` objects and transforms them into renderable D3 inputs through a layered pipeline. Hosts convert JCAMP with `FN.ExtractJcamp` before mount; `SpectraEditor` expects shaped `entity` and `multiEntities` props at runtime. + +
+ Data Transformation Pipeline +
+ +
+ +The pipeline starts in [`src/helpers/chem.js`](../../src/helpers/chem.js): + +- `ExtractJcamp(source)` uses `jcampconverter` and preserved JCAMP records to build `{ spectra, features, layout }`. +- Layout-specific feature builders derive peak features, integration data, multiplicity data, simulation data, CV metadata, DSC metadata, and XRD temperature metadata. +- Conversion helpers such as `Convert2Scan`, `Convert2Thres`, `Convert2Peak`, and `convertTopic` produce data used by submit payloads and render selectors. + +`LayerInit` consumes `entity.layout` and `entity.features` to initialize reducer state. It does not render the data directly. Rendering input is derived later through `extractParams()` and selectors. + +[`extractParams`](../../src/helpers/extractParams.js) resolves: + +- `topic`: the spectral `x`/`y` source for the active scan or first spectrum. +- `feature`: either edit peak or auto peak feature depending on threshold edit mode. +- `hasEdit`: whether edit peak data is available. +- `integration` and `multiplicity`: feature-level data used by panels and initialization. + +Rendering selectors in [`src/helpers/chem.js`](../../src/helpers/chem.js) convert the selected `topic` and `feature` into D3 inputs: + +- `Topic2Seed` applies shift offset and creates renderable points. Integration-enabled layouts receive cumulative `k` values for integration curves. +- `Feature2Peak` derives visible peak markers from feature data, thresholds, and shift offset. +- `ToThresEndPts` derives threshold line endpoints. +- `ToShiftPeaks` derives reference markers. +- `ToFrequency` extracts NMR observe frequency when applicable. +- `GetComparisons` rescales comparison spectra for IR, HPLC UV/VIS, and XRD comparison rendering. +- `Feature2MaxMinPeak` derives cyclic voltammetry max/min/pecker points for multi-curve CV state. + +The output of this pipeline is not a single normalized object. It is a set of render-specific inputs consumed by viewer wrappers and focus classes: `seed`, `peak`, `comparisons`, threshold endpoints, shift peaks, integration state, multiplicity state, sweep extent, layout state, axes state, and cyclic voltammetry state. + +## Multi-Curve and CV Architecture + +Multi-curve behavior is coordinated by the `curve` reducer, `cyclicvolta` reducer, `saga_multi_entities.js`, `MultiJcampsViewer`, `ViewerMulti`, and `MultiFocus`. + +
+ Multi-Curve Architecture +
+ +
+ +`LayerInit.updateMultiEntities()` dispatches `setAllCurves(multiEntities)` when multiple entities are present. For cyclic voltammetry, it dispatches a single-item curve list when `multiEntities` is empty. The `curve` reducer transforms each entity through `extractParams()`, computes max/min peak data with `Convert2MaxMinPeak`, assigns a color, and stores the result in `curve.listCurves`. + +The active curve is tracked by `curve.curveIdx`. Multiple state slices use that index: + +- `threshold.list[curveIdx]` stores the active curve threshold state. +- `integration.present.integrations[curveIdx]` stores per-curve integration state. +- `multiplicity.present.multiplicities[curveIdx]` stores per-curve multiplicity state. +- `cyclicvolta.spectraList[curveIdx]` stores CV-specific pair peaks, selected pair index, reference state, and shift history. + +`saga_multi_entities.js` is the key synchronization layer. On `CURVE.SET_ALL_CURVES`, it: + +- Resets CV state. +- Reads CV area value, area unit, and current mode from the first curve feature. +- Initializes CV pair peaks for each curve. +- Restores max/min/pecker points from parsed feature data. +- Selects the CV reference pair when present. +- Initializes integration, multiplicity, and simulation arrays by curve index. + +`ViewerMulti` connects the active curve state to rendering. It derives seed, peaks, threshold endpoints, shift markers, edit state, integration state, multiplicity state, axes state, and cyclic voltammetry state from Redux. For CV layouts it also uses `ResizeObserver` and rebuilds the chart when the container size changes. + +`MultiFocus` owns the multi-curve D3 scene. It selects the active curve data, builds `otherLineData` for non-active curves, filters SEC/GC curves by compatible units, transforms y-axis values for current density, renders the active path, draws other curves, and manages CV-specific peak and pecker overlays. + +State is indexed by `curve.curveIdx` across `threshold`, `integration`, `multiplicity`, and `cyclicvolta`. Rendering and submit payloads require a consistent active curve and aligned per-curve arrays. Changes to curve selection, CV reference state, density mode, or the imported curve list update overlays, labels, integration and multiplicity state, and submit payload construction together. + +## Forecast and Analysis Architecture + +Forecast workflows are callback-driven and render through the same spectrum infrastructure used by standard line spectra. + +`BtnPredict` in [`src/components/cmd_bar/r06_predict_btn.js`](../../src/components/cmd_bar/r06_predict_btn.js) prepares prediction payloads from current peaks, layout, scan, shift, threshold, forecast analysis state, integration state, multiplicity state, and curve state. When prediction is ready, it switches the UI viewer to `analysis` and calls `forecast.btnCb(payload)`. When NMR simulation data is missing, it calls `forecast.refreshCb(payload)`. + +`ForecastViewer` in [`src/components/forecast_viewer.js`](../../src/components/forecast_viewer.js) initializes the `forecast` reducer from the incoming `forecast` prop. If forecast predictions are running or refreshed, it switches the UI viewer to analysis mode. It always mounts a `ViewerLine`, passing comparison spectra from `jcamp` state for the active curve. The line chart is hidden when the active viewer mode is analysis, but it remains part of the rendered structure. + +Analysis rendering is layout-specific: + +- NMR analysis uses `NmrViewer` when `ui.viewer` is `analysis` and the layout is NMR. +- IR analysis uses `IrViewer` when `ui.viewer` is `analysis` and the layout is IR. +- Forecast input callbacks are passed into the analysis viewers through `inputCb`. + +Forecast state integrates in two directions: command-bar actions send prediction requests through host callbacks; incoming `forecast` props update the local forecast reducer and set spectrum versus analysis viewer mode. + +## Host Integration Contracts + +The following table maps each host contract to runtime code inside the editor. + +| Contract | Runtime entry | Runtime exit | +|---|---|---| +| `entity` | `LayerInit` reads `layout` and `features`; `extractParams` and selectors derive render inputs | Submit payload via `BtnSubmit` | +| `multiEntities` | `LayerInit.updateMultiEntities()` → `setAllCurves` → `curve.listCurves` | `spectra_list` with one entry per curve and `curveSt` | +| `operations` | `LayerInit.initReducer()` selects `operations[0]` | `operation.value(payload)` on submit | +| `forecast` | `ForecastViewer` initializes forecast reducer from props | `forecast.btnCb`, `forecast.refreshCb`, `forecast.inputCb` | +| `others` | `LayerInit.updateOthers()` → `jcamp` reducer | `addOthersCb({ jcamps })` from Compare panel | +| `onDescriptionChanged` | `descriptions` prop seeds the info panel | Quill Delta forwarded from `PanelViewer` | + +`entity` must include `layout`, `spectra`, and `features` in the shape produced by `FN.ExtractJcamp` (`{ spectra, features, layout }`, plus layout-specific fields). `GetComparisons` transforms comparison entities in `jcamp` state for IR, HPLC UV/VIS, and XRD overlays. + +## Runtime Synchronization Patterns + +Runtime synchronization is distributed across `LayerInit`, reducers, sagas, and D3 viewers. The editor relies on action propagation rather than a single central controller. + +
+ Runtime Synchronization Flow +
+ +
+ +The initialization path begins in `LayerInit.componentDidMount()`: + +1. `execReset()` dispatches common reset actions and layout-specific initialization. +2. `initReducer()` selects the first submit operation. +3. `updateOthers()` initializes comparison state. +4. `updateMultiEntities()` initializes or clears curve state. + +On `componentDidUpdate()`, `LayerInit` compares `entity` identity and reruns `execReset()` when the entity changes. It also refreshes comparison state and multi-entity state. + +Viewers participate in synchronization after React selects the rendering branch. `ViewerLine`, `ViewerRect`, and `ViewerMulti` dispatch `resetAll(feature)` on mount and when their active feature changes. This action resets and propagates feature-specific state. `saga_manager.js` enriches the reset with layout and curve metadata and dispatches `MANAGER.RESETSHIFT`. + +Rendering synchronization follows the state snapshot: + +- Redux selectors derive render inputs from the current store and viewer props. +- Viewer wrappers pass those inputs to focus classes. +- Focus classes update D3 layers. +- D3 interactions dispatch UI actions back into Redux. +- Sagas translate those UI actions into domain mutations. +- New state causes connected viewers to update again. + +Undoable reducers participate in this loop by including domain edit actions in history while clearing history for reset actions. This keeps undo state attached to the current editing context instead of crossing spectrum or feature boundaries. diff --git a/docs/architecture/high-level-overview.md b/docs/architecture/high-level-overview.md new file mode 100644 index 00000000..fd0e23cc --- /dev/null +++ b/docs/architecture/high-level-overview.md @@ -0,0 +1,163 @@ +# High-Level Overview + +This page describes the high-level architecture of `@complat/react-spectra-editor`. The public entry point is `SpectraEditor` in [`src/app.js`](../../src/app.js). + +The package is a stateful React editor for chemical spectra. A host application owns data loading, backend communication, persistence, and product workflows. The editor owns client-side spectrum rendering, editing interactions, Redux state orchestration, and callback-based integration. + +## Project Purpose + +`react-spectra-editor` provides an embedded editor for viewing and editing chemical spectra. The package supports NMR, IR, MS, UV, CV, XRD, and additional layout types listed in [`src/constants/list_layout.js`](../../src/constants/list_layout.js). See [`README.md`](../../README.md) for the published feature summary. + +Supported layout identifiers include: + +- `PLAIN` +- `IR` +- `RAMAN` +- `UV/VIS` +- `1H`, `13C`, `19F`, `31P`, `15N`, `29Si` +- `MS` +- `THERMOGRAVIMETRIC ANALYSIS` +- `X-RAY DIFFRACTION` +- `HPLC UV/VIS` +- `CYCLIC VOLTAMMETRY` +- `CIRCULAR DICHROISM SPECTROSCOPY` +- `SIZE EXCLUSION CHROMATOGRAPHY` +- `AIF` +- `Emissions` +- `DLS ACF` +- `DLS intensity` +- `DIFFERENTIAL SCANNING CALORIMETRY` +- `GAS CHROMATOGRAPHY` + +The editor supports these main user-facing responsibilities: + +- Display spectrum data as line charts, MS bar charts, or multi-spectrum overlays. +- Navigate spectra through zooming, brushing, scan selection, and curve selection. +- Edit peak data, thresholds, shifts, integrations, multiplicities, and cyclic voltammetry peaks. +- Compare compatible spectra through the comparison panel. +- Submit edited spectrum payloads through host-provided operations. +- Trigger prediction workflows and display forecast results when the host supplies `forecast` props and callbacks. + +The package provides these main technical responsibilities: + +- Convert JCAMP sources into the editor data model through `ExtractJcamp` in [`src/helpers/chem.js`](../../src/helpers/chem.js). +- Initialize Redux state from host-provided props in [`src/layer_init.js`](../../src/layer_init.js). +- Derive renderable spectrum inputs through selectors and helpers in [`src/helpers/chem.js`](../../src/helpers/chem.js) and [`src/helpers/extractParams.js`](../../src/helpers/extractParams.js). +- Select the appropriate rendering branch in [`src/layer_content.js`](../../src/layer_content.js). +- Render spectra with D3/SVG through [`src/components/d3_line`](../../src/components/d3_line), [`src/components/d3_rect`](../../src/components/d3_rect), and [`src/components/d3_multi`](../../src/components/d3_multi). +- Coordinate editing workflows through Redux reducers, undoable state, and Redux Saga. +- Expose integration helpers through `FN` in [`src/fn.js`](../../src/fn.js). + +## Technology Stack + +| Technology | Source files | Architectural role | +|---|---|---| +| React 17 | [`package.json`](../../package.json), [`src/app.js`](../../src/app.js) | Provides the component model for the embedded editor. The core codebase primarily uses class components connected to Redux through `connect`. | +| React Redux | [`src/app.js`](../../src/app.js), [`src/reducers/index.js`](../../src/reducers/index.js) | Holds the editor-wide state model for layouts, UI mode, thresholds, peaks, integrations, multiplicities, curves, forecast state, comparison state, and cyclic voltammetry data. | +| Redux Saga | [`src/app.js`](../../src/app.js), [`src/sagas/index.js`](../../src/sagas/index.js) | Coordinates action-driven workflows that span multiple reducers, especially UI interactions, resets, metadata calculation, multiplicity updates, and multi-entity initialization. | +| Redux Undo | [`src/reducers/undo_redo_config.js`](../../src/reducers/undo_redo_config.js), [`src/reducers/reducer_edit_peak.js`](../../src/reducers/reducer_edit_peak.js), [`src/reducers/reducer_integration.js`](../../src/reducers/reducer_integration.js), [`src/reducers/reducer_multiplicity.js`](../../src/reducers/reducer_multiplicity.js) | Provides bounded undo/redo history for editing-sensitive state: peaks, integrations, and multiplicities. | +| D3 | [`src/components/d3_line`](../../src/components/d3_line), [`src/components/d3_rect`](../../src/components/d3_rect), [`src/components/d3_multi`](../../src/components/d3_multi), [`src/helpers/mount.js`](../../src/helpers/mount.js) | Owns SVG rendering, axes, grids, paths, brushes, tooltips, markers, overlays, comparison lines, integrations, and multiplicity visualization. | +| Material UI | [`src/app.js`](../../src/app.js), [`src/components/cmd_bar/index.js`](../../src/components/cmd_bar/index.js), [`src/components/panel/index.js`](../../src/components/panel/index.js) | Provides UI structure for grids, buttons, tooltips, accordions, panel styling, and theme integration. | +| `jcampconverter` | [`src/helpers/chem.js`](../../src/helpers/chem.js), [`package.json`](../../package.json) | Converts JCAMP input into the internal `entity` shape consumed by initialization, selectors, and viewers. | +| `reselect` | [`src/helpers/chem.js`](../../src/helpers/chem.js) | Memoizes derived rendering data such as seed points, peaks, thresholds, comparisons, and frequency values. | +| Create React App / `react-scripts` | [`package.json`](../../package.json) | Supports local development and the demo application workflow through `start`, `build`, and `test`. | +| Babel compile | [`package.json`](../../package.json) | Builds distributable package output into `dist/` through the `compile` script. | +| Storybook / Cypress | [`package.json`](../../package.json), [`cypress.config.ts`](../../cypress.config.ts) | Supports component exploration and end-to-end testing workflows around the editor. | + +## System Boundaries + +The package is an embedded editor. It owns the in-browser editing experience and delegates persistence, prediction services, and product workflows to the host application. + +![Frontend Context Map](../diagrams/generated/frontend-context-map.svg) + +### Frontend Package Responsibilities + +The package owns: + +- The public editor component, `SpectraEditor`, exported from [`src/app.js`](../../src/app.js). +- The internal Redux store, reducers, and sagas. +- JCAMP conversion helpers exposed through `FN.ExtractJcamp`. +- Spectrum rendering through D3/SVG viewers. +- Editor controls for viewer mode, zoom, peak editing, integration, multiplicity, submit, thresholds, layout, wavelength, axes, detector, and cyclic voltammetry density. +- Panel UI for spectrum information, peaks, multiplicity, comparisons, graph selection, and cyclic voltammetry data. +- Construction of host-facing submit payloads in [`src/components/cmd_bar/r05_submit_btn.js`](../../src/components/cmd_bar/r05_submit_btn.js). +- Construction of host-facing prediction payloads in [`src/components/cmd_bar/r06_predict_btn.js`](../../src/components/cmd_bar/r06_predict_btn.js). + +### Host Application Responsibilities + +The host application owns the application context around the editor: + +- Loading spectrum data and passing it as `entity` or `multiEntities`. +- Calling backend services and persisting edited results. +- Providing submit operations through the `operations` prop. +- Providing prediction callbacks and forecast state through the `forecast` prop. +- Providing comparison data and file import behavior through the `others` prop and `addOthersCb`. +- Handling rich description changes through `onDescriptionChanged`. +- Owning authentication, authorization, routing, ELN synchronization, and product-level navigation. + +### Data Entry and Exit Points + +Data enters the editor through props: + +- `entity` initializes the main spectrum. +- `multiEntities` initializes multi-spectrum and cyclic voltammetry workflows. +- `others` initializes comparison spectra and the comparison import callback. +- `forecast` initializes prediction state and prediction callbacks. +- `descriptions`, `molSvg`, `multiMolSvgs`, `exactMass`, `entityFileNames`, and labels provide display context. + +Edited data exits the editor through callbacks: + +- `operation.value(payload)` receives the submit payload built by [`src/components/cmd_bar/r05_submit_btn.js`](../../src/components/cmd_bar/r05_submit_btn.js). +- `forecast.btnCb(payload)` receives prediction requests. +- `forecast.refreshCb(payload)` receives simulation refresh requests. +- `forecast.inputCb(...)` is passed into forecast viewers for analysis interactions. +- `others.addOthersCb({ jcamps })` receives dropped comparison files from [`src/components/panel/compare.js`](../../src/components/panel/compare.js). +- `onDescriptionChanged(delta)` receives user-authored rich description changes from the info panel flow. + +The integration model is callback-driven. The editor manages local rendering and editing state, then returns structured payloads to the host for persistence, prediction, and synchronization. + +## Global Architecture + +The application composition starts in [`src/app.js`](../../src/app.js). `SpectraEditor` creates the Redux store, starts the root saga, wraps the editor in the Redux `Provider`, applies Material UI style injection, and renders `LayerInit`. + +![Application Composition](../diagrams/generated/application-composition.svg) + +### Main Layers + +| Layer | File | Responsibility | +|---|---|---| +| `SpectraEditor` | [`src/app.js`](../../src/app.js) | Public component boundary, Redux provider setup, saga startup, helper export through `FN`. | +| `LayerInit` | [`src/layer_init.js`](../../src/layer_init.js) | Converts host props into Redux initialization actions and chooses between the single-spectrum and multi-spectrum branches. | +| `LayerPrism` | [`src/layer_prism.js`](../../src/layer_prism.js) | Main single-entity workspace. It composes the command bar, content viewer, and side panels. | +| `LayerContent` | [`src/layer_content.js`](../../src/layer_content.js) | Selects `ForecastViewer` when `forecast` is non-empty and the layout supports prediction (NMR, IR, UV/VIS, XRD); otherwise `ViewerRect` for MS or `ViewerLine` for other layouts. | +| `MultiJcampsViewer` | [`src/components/multi_jcamps_viewer.js`](../../src/components/multi_jcamps_viewer.js) | Multi-entity and cyclic voltammetry workspace. It composes the command bar, `ViewerMulti`, cyclic voltammetry panel, and side panels. | +| `CmdBar` | [`src/components/cmd_bar/index.js`](../../src/components/cmd_bar/index.js) | Central command surface for editing and display actions. | +| `PanelViewer` | [`src/components/panel/index.js`](../../src/components/panel/index.js) | Side-panel system for metadata, peaks, multiplicity, comparisons, graph selection, and CV data. | +| D3 viewers | [`src/components/d3_line`](../../src/components/d3_line), [`src/components/d3_rect`](../../src/components/d3_rect), [`src/components/d3_multi`](../../src/components/d3_multi) | Rendering branches for line spectra, MS spectra, and multi-spectrum/CV views. | + +Redux crosses the full tree. Components derive render data from Redux and props, dispatch actions from UI interactions, and rely on sagas to coordinate cross-slice workflows. + +## Rendering Architecture + +Rendering is split between React composition and D3-managed SVG drawing. [`src/layer_content.js`](../../src/layer_content.js) selects the viewer branch from the active layout and forecast state. + +![Rendering Branches](../diagrams/generated/rendering-branches.svg) + +| Branch | Viewer | Used for | +|---|---|---| +| Line | [`ViewerLine`](../../src/components/d3_line/index.js) | Line-chart layouts (NMR, IR, UV/VIS, XRD, Raman, and related types) | +| MS | [`ViewerRect`](../../src/components/d3_rect/index.js) | Mass spectrometry bar charts | +| Multi | [`ViewerMulti`](../../src/components/d3_multi/index.js) | `multiEntities` overlays and cyclic voltammetry | + +React mounts containers and passes Redux-derived inputs; D3 focus classes own the SVG lifecycle (`create` / `update`, brush, overlays). See [Frontend Architecture: Rendering Pipeline](frontend-architecture.md#rendering-pipeline-architecture) for the full pipeline, helpers, and debugging notes. + +## State Management + +Redux and Redux Saga coordinate editor-wide state from [`src/reducers/index.js`](../../src/reducers/index.js) and [`src/sagas/index.js`](../../src/sagas/index.js). Undoable editing applies to peaks, integrations, and multiplicities. `LayerInit` initializes state from host props; D3 viewers dispatch `resetAll(feature)` to stay aligned with the active feature. + +See [Frontend Architecture](frontend-architecture.md) for reducer domains, saga modules, the data transformation pipeline, multi-curve/CV behavior, and runtime synchronization flows. + +## Related Documentation + +- [Frontend Architecture](frontend-architecture.md): Redux, sagas, pipelines, host contracts, and synchronization +- [Diagram Generation](../diagram.md): maintain architecture diagrams diff --git a/docs/diagram.md b/docs/diagram.md new file mode 100644 index 00000000..88d89a66 --- /dev/null +++ b/docs/diagram.md @@ -0,0 +1,129 @@ +# Diagram Generation + +Developer reference for maintaining documentation diagrams in `docs/diagrams/`. + +## Purpose + +Documentation diagrams are generated automatically from Mermaid source files. The source files are versioned so diagrams can be reviewed and edited as code, while generated image files are used by Markdown documentation. + +## Folder Structure + +```text +docs/diagrams/ + config/ + mermaid-config.json + generate.sh + generated/ + application-composition.svg + data-transformation-pipeline.svg + frontend-context-map.svg + multi-curve-architecture.svg + redux-architecture.svg + rendering-branches.svg + rendering-pipeline.svg + runtime-synchronization-flow.svg + saga-orchestration.svg + src/ + application-composition.mmd + data-transformation-pipeline.mmd + frontend-context-map.mmd + multi-curve-architecture.mmd + redux-architecture.mmd + rendering-branches.mmd + rendering-pipeline.mmd + runtime-synchronization-flow.mmd + saga-orchestration.mmd +``` + +`docs/diagrams/src/` contains Mermaid source files (`.mmd`). Edit these files when a diagram needs to change. + +`docs/diagrams/generated/` contains generated SVG diagrams. Every Mermaid source is generated as SVG. + +`docs/diagrams/config/mermaid-config.json` defines shared Mermaid theme settings (fonts, colors, flowchart spacing) applied to every generated diagram. + +## How to Generate Diagrams + +Run this command from the repository root: + +```bash +bash docs/diagrams/generate.sh +``` + +Or: + +```bash +yarn docs:diagrams +``` + +This regenerates all SVG diagrams from every `.mmd` file in `docs/diagrams/src/`. + +## How It Works + +- The generation script uses Mermaid CLI through `npx`. +- Mermaid CLI is downloaded and run on demand; it is not a project dependency. +- The script keeps npm and Puppeteer caches outside the repository under `~/.cache/react-spectra-editor/` (override with `REACT_SPECTRA_EDITOR_CACHE`). +- Shared styling is applied through `docs/diagrams/config/mermaid-config.json`. + +Equivalent manual command pattern: + +```bash +for file in docs/diagrams/src/*.mmd; do + npx --yes --package @mermaid-js/mermaid-cli mmdc \ + -i "$file" \ + -o "docs/diagrams/generated/$(basename "${file%.mmd}.svg")" \ + -c docs/diagrams/config/mermaid-config.json +done +``` + +## How to Add or Modify a Diagram + +1. Create or edit a `.mmd` file in `docs/diagrams/src/`. +2. Run the generation script: + + ```bash + bash docs/diagrams/generate.sh + ``` + +3. Reference the generated image in Markdown under `docs/architecture/`. + +Use centered HTML image tags for large architecture diagrams. Keep diagrams fluid, readable on narrow screens, and capped in height: + +```html +
+ Redux Architecture +
+``` + +For simpler overview diagrams, standard Markdown image syntax is also used: + +```markdown +![Application Composition](../diagrams/generated/application-composition.svg) +``` + +Use PNG only when targeting tools that do not reliably render SVG. This repository currently references SVG diagrams only; do not add PNG files unless Markdown documentation needs them. + +## Rules + +- Do not manually edit generated images. +- Always modify `.mmd` source files. +- Add new documentation diagrams as `.mmd` files in `docs/diagrams/src/`. +- Regenerate SVG files after changing any `.mmd` source. +- Center large diagrams in architecture pages and include `width="100%"` as the GitHub fallback. Keep `width: 100%; min-width: 1000px; max-height: 840px; height: auto; object-fit: contain;` for local previews that preserve inline styles. +- Do not keep generated PNG files unless Markdown documentation references them. +- Do not add `@mermaid-js/mermaid-cli` as a project dependency; generation stays on-demand through `npx`. +- Keep diagrams simple and readable. +- Mark unconfirmed behavior in the Mermaid source as `TODO: Confirm `. + +## Diagram Usage in Documentation + +| Diagram | Referenced in | +|---|---| +| `frontend-context-map` | [high-level-overview.md](architecture/high-level-overview.md) | +| `application-composition` | [high-level-overview.md](architecture/high-level-overview.md) | +| `rendering-branches` | [high-level-overview.md](architecture/high-level-overview.md) | +| `redux-architecture` | [frontend-architecture.md](architecture/frontend-architecture.md) | +| `saga-orchestration` | [frontend-architecture.md](architecture/frontend-architecture.md) | +| `rendering-pipeline` | [frontend-architecture.md](architecture/frontend-architecture.md) | +| `data-transformation-pipeline` | [frontend-architecture.md](architecture/frontend-architecture.md) | +| `multi-curve-architecture` | [frontend-architecture.md](architecture/frontend-architecture.md) | +| `runtime-synchronization-flow` | [frontend-architecture.md](architecture/frontend-architecture.md) | diff --git a/docs/diagrams/config/mermaid-config.json b/docs/diagrams/config/mermaid-config.json new file mode 100644 index 00000000..3d366235 --- /dev/null +++ b/docs/diagrams/config/mermaid-config.json @@ -0,0 +1,23 @@ +{ + "theme": "base", + "fontFamily": "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif", + "themeVariables": { + "background": "#ffffff", + "primaryColor": "#f8fafc", + "primaryTextColor": "#0f172a", + "primaryBorderColor": "#64748b", + "lineColor": "#475569", + "secondaryColor": "#eef2ff", + "tertiaryColor": "#f1f5f9", + "clusterBkg": "#f8fafc", + "clusterBorder": "#cbd5e1", + "fontSize": "16px" + }, + "flowchart": { + "htmlLabels": true, + "nodeSpacing": 44, + "rankSpacing": 64, + "padding": 16, + "curve": "linear" + } +} diff --git a/docs/diagrams/generate.sh b/docs/diagrams/generate.sh new file mode 100755 index 00000000..8a64c414 --- /dev/null +++ b/docs/diagrams/generate.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +SOURCE_DIR="$SCRIPT_DIR/src" +OUTPUT_DIR="$SCRIPT_DIR/generated" +CONFIG_FILE="$SCRIPT_DIR/config/mermaid-config.json" +CACHE_ROOT="${REACT_SPECTRA_EDITOR_CACHE:-$HOME/.cache/react-spectra-editor}" + +export NPM_CONFIG_CACHE="$CACHE_ROOT/npm" +export PUPPETEER_CACHE_DIR="$CACHE_ROOT/puppeteer" + +mkdir -p "$NPM_CONFIG_CACHE" "$PUPPETEER_CACHE_DIR" "$OUTPUT_DIR" + +NPX=(npx --yes --package @mermaid-js/mermaid-cli mmdc) + +cd "$REPO_ROOT" + +shopt -s nullglob +sources=("$SOURCE_DIR"/*.mmd) + +if [ ${#sources[@]} -eq 0 ]; then + echo "No Mermaid diagram sources found in $SOURCE_DIR" + exit 0 +fi + +for input in "${sources[@]}"; do + base="$(basename "$input" .mmd)" + output="$OUTPUT_DIR/$base.svg" + echo "Generating ${output#$REPO_ROOT/} from ${input#$REPO_ROOT/}" + "${NPX[@]}" -i "$input" -o "$output" -c "$CONFIG_FILE" +done + +echo "Generated ${#sources[@]} diagram(s)." diff --git a/docs/diagrams/generated/application-composition.svg b/docs/diagrams/generated/application-composition.svg new file mode 100644 index 00000000..0e38a263 --- /dev/null +++ b/docs/diagrams/generated/application-composition.svg @@ -0,0 +1 @@ +

Shared Editor Shell

Workspace Selection

Initialization

Public Entry

SpectraEditor

LayerInit

LayerPrism
or MultiJcampsViewer

CmdBar + LayerContent + PanelViewer

\ No newline at end of file diff --git a/docs/diagrams/generated/data-transformation-pipeline.svg b/docs/diagrams/generated/data-transformation-pipeline.svg new file mode 100644 index 00000000..b7926079 --- /dev/null +++ b/docs/diagrams/generated/data-transformation-pipeline.svg @@ -0,0 +1 @@ +

Viewer Inputs

Selector Derivation

Internal Spectrum Model

Ingestion

Host Input

entity

multiEntities

ExtractJcamp

LayerInit

extractParams

topic + feature

curve.listCurves

seed + peaks

thresholds, shifts,
frequency, comparisons

Viewer-ready D3 inputs

\ No newline at end of file diff --git a/docs/diagrams/generated/frontend-context-map.svg b/docs/diagrams/generated/frontend-context-map.svg new file mode 100644 index 00000000..56753231 --- /dev/null +++ b/docs/diagrams/generated/frontend-context-map.svg @@ -0,0 +1 @@ +

Host Output Boundary

react-spectra-editor Package

Host Input Boundary

props

Host application
data and workflow context

SpectraEditor

Redux and Saga
runtime orchestration

Rendering Layer

Submit, forecast,
and comparison callbacks

Host services
and persistence

\ No newline at end of file diff --git a/docs/diagrams/generated/multi-curve-architecture.svg b/docs/diagrams/generated/multi-curve-architecture.svg new file mode 100644 index 00000000..0820956a --- /dev/null +++ b/docs/diagrams/generated/multi-curve-architecture.svg @@ -0,0 +1 @@ +

Multi-Curve Rendering

Indexed Curve State

Curve Initialization

Curve Input

multiEntities

setAllCurves

extractParams
max/min peak extraction

saga_multi_entities

curve.listCurves
curveIdx

thresholds, integrations,
multiplicities, CV peaks

ViewerMulti

MultiFocus
active curve + overlays

\ No newline at end of file diff --git a/docs/diagrams/generated/redux-architecture.svg b/docs/diagrams/generated/redux-architecture.svg new file mode 100644 index 00000000..a0b602d5 --- /dev/null +++ b/docs/diagrams/generated/redux-architecture.svg @@ -0,0 +1 @@ +

Undoable History

Redux Store

Saga Orchestration

Action Namespaces

UI and Manager

Edit, Integration,
Multiplicity, Curve

Forecast, Submit,
JCAMP

Root saga

Manager, UI,
metadata, multi-entity

Display and navigation

Undoable editing

Multi-curve and CV

Host integration state

editPeak, integration,
multiplicity history

\ No newline at end of file diff --git a/docs/diagrams/generated/rendering-branches.svg b/docs/diagrams/generated/rendering-branches.svg new file mode 100644 index 00000000..3a1b9521 --- /dev/null +++ b/docs/diagrams/generated/rendering-branches.svg @@ -0,0 +1 @@ +

D3 Rendering Layer

Rendering Branches

Rendering Selection

LayerContent

Layout and forecast decision

Line spectra

MS bars

Forecast analysis

Multi-spectrum and CV

Viewer wrappers
and focus classes

\ No newline at end of file diff --git a/docs/diagrams/generated/rendering-pipeline.svg b/docs/diagrams/generated/rendering-pipeline.svg new file mode 100644 index 00000000..25e1c270 --- /dev/null +++ b/docs/diagrams/generated/rendering-pipeline.svg @@ -0,0 +1 @@ +

D3 Helper Systems

D3 Ownership

React Ownership

Redux-Derived Inputs

Topic2Seed
Feature2Peak
comparison selectors

seed, peaks,
thresholds, overlays

LayerContent

Viewer wrappers
Line / Rect / Multi

Focus classes
LineFocus / RectFocus / MultiFocus

SVG layers
paths, axes, clips, overlays

draw, mount,
brush, compass, init

\ No newline at end of file diff --git a/docs/diagrams/generated/runtime-synchronization-flow.svg b/docs/diagrams/generated/runtime-synchronization-flow.svg new file mode 100644 index 00000000..85e30310 --- /dev/null +++ b/docs/diagrams/generated/runtime-synchronization-flow.svg @@ -0,0 +1 @@ +RenderingState OrchestrationHost and InitializationD3 Focus ClassesViewer WrappersRedux and SagasLayerInitHost PropsD3 Focus ClassesViewer WrappersRedux and SagasLayerInitHost Propsentity, multiEntities, operations, forecast, others1reset common, layout, operation, comparison state2initialize curves, CV, integration, multiplicity3selector outputs, active feature, overlay state4resetAll(feature)5reset shift and synchronize edit state6create SVG scene and D3 layers7user interactions as UI actions8updated peaks, integrations, CV, zoom state9update paths, axes, clips, overlays10 \ No newline at end of file diff --git a/docs/diagrams/generated/saga-orchestration.svg b/docs/diagrams/generated/saga-orchestration.svg new file mode 100644 index 00000000..b7ca1084 --- /dev/null +++ b/docs/diagrams/generated/saga-orchestration.svg @@ -0,0 +1 @@ +

Reducer Synchronization

Saga Modules

Action Flow

Runtime Action Sources

LayerInit

D3 viewers
command bar
panels

Manager and curve actions

UI interaction actions

manager + multi-entity sagas

UI + edit + multiplicity sagas

metadata saga

Reset and initialization state

Editing and overlay state

Metadata state

\ No newline at end of file diff --git a/docs/diagrams/src/application-composition.mmd b/docs/diagrams/src/application-composition.mmd new file mode 100644 index 00000000..79b93795 --- /dev/null +++ b/docs/diagrams/src/application-composition.mmd @@ -0,0 +1,30 @@ +flowchart LR + subgraph entry [Public Entry] + SpectraEditor["SpectraEditor"] + end + + subgraph init [Initialization] + LayerInit["LayerInit"] + end + + subgraph workspace [Workspace Selection] + WorkspaceBranch["LayerPrism
or MultiJcampsViewer"] + end + + subgraph shell [Shared Editor Shell] + EditorShell["CmdBar + LayerContent + PanelViewer"] + end + + SpectraEditor --> LayerInit + LayerInit --> WorkspaceBranch + WorkspaceBranch --> EditorShell + + classDef entryStyle fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + classDef initStyle fill:#f3e8ff,stroke:#9333ea,color:#581c87,stroke-width:1.5px + classDef workspaceStyle fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:1.5px + classDef shellStyle fill:#fef3c7,stroke:#d97706,color:#78350f,stroke-width:1.5px + + class SpectraEditor entryStyle + class LayerInit initStyle + class WorkspaceBranch workspaceStyle + class EditorShell shellStyle diff --git a/docs/diagrams/src/data-transformation-pipeline.mmd b/docs/diagrams/src/data-transformation-pipeline.mmd new file mode 100644 index 00000000..58531154 --- /dev/null +++ b/docs/diagrams/src/data-transformation-pipeline.mmd @@ -0,0 +1,51 @@ +flowchart LR + subgraph host [Host Input] + direction TB + EntityInput["entity"] + MultiInput["multiEntities"] + end + + subgraph ingestion [Ingestion] + direction TB + JcampParsing["ExtractJcamp"] + RuntimeInit["LayerInit"] + ParamSelection["extractParams"] + end + + subgraph model [Internal Spectrum Model] + direction TB + TopicFeature["topic + feature"] + CurveModel["curve.listCurves"] + end + + subgraph selectors [Selector Derivation] + direction TB + RenderSelectors["seed + peaks"] + OverlaySelectors["thresholds, shifts,
frequency, comparisons"] + end + + subgraph rendering [Viewer Inputs] + ViewerInputs["Viewer-ready D3 inputs"] + end + + EntityInput --> RuntimeInit + EntityInput --> ParamSelection + MultiInput --> RuntimeInit + JcampParsing --> EntityInput + RuntimeInit --> CurveModel + ParamSelection --> TopicFeature + CurveModel --> TopicFeature + TopicFeature --> RenderSelectors + TopicFeature --> OverlaySelectors + RenderSelectors --> ViewerInputs + OverlaySelectors --> ViewerInputs + + classDef hostStyle fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:1.5px + classDef helperStyle fill:#f3f4f6,stroke:#6b7280,color:#374151,stroke-width:1.5px + classDef stateStyle fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + classDef renderStyle fill:#dcfce7,stroke:#16a34a,color:#14532d,stroke-width:1.5px + + class EntityInput,MultiInput hostStyle + class JcampParsing,RuntimeInit,ParamSelection helperStyle + class TopicFeature,CurveModel,RenderSelectors,OverlaySelectors stateStyle + class ViewerInputs renderStyle diff --git a/docs/diagrams/src/frontend-context-map.mmd b/docs/diagrams/src/frontend-context-map.mmd new file mode 100644 index 00000000..db731d67 --- /dev/null +++ b/docs/diagrams/src/frontend-context-map.mmd @@ -0,0 +1,31 @@ +flowchart LR + subgraph hostInput [Host Input Boundary] + HostData["Host application
data and workflow context"] + end + + subgraph editorBoundary [react-spectra-editor Package] + SpectraEditor["SpectraEditor"] + EditorRuntime["Redux and Saga
runtime orchestration"] + RenderingLayer["Rendering Layer"] + end + + subgraph hostOutput [Host Output Boundary] + CallbackOutputs["Submit, forecast,
and comparison callbacks"] + HostServices["Host services
and persistence"] + end + + HostData -->|"props"| SpectraEditor + SpectraEditor --> EditorRuntime + EditorRuntime --> RenderingLayer + EditorRuntime --> CallbackOutputs + CallbackOutputs --> HostServices + + classDef host fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:1.5px + classDef frontend fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + classDef rendering fill:#dcfce7,stroke:#16a34a,color:#14532d,stroke-width:1.5px + classDef output fill:#ffedd5,stroke:#f97316,color:#7c2d12,stroke-width:1.5px + + class HostData,HostServices host + class SpectraEditor,EditorRuntime frontend + class RenderingLayer rendering + class CallbackOutputs output diff --git a/docs/diagrams/src/multi-curve-architecture.mmd b/docs/diagrams/src/multi-curve-architecture.mmd new file mode 100644 index 00000000..c2237e02 --- /dev/null +++ b/docs/diagrams/src/multi-curve-architecture.mmd @@ -0,0 +1,41 @@ +flowchart LR + subgraph input [Curve Input] + MultiEntities["multiEntities"] + end + + subgraph orchestration [Curve Initialization] + direction TB + SetAllCurves["setAllCurves"] + CurveExtraction["extractParams
max/min peak extraction"] + MultiSaga["saga_multi_entities"] + end + + subgraph state [Indexed Curve State] + direction TB + CurveState["curve.listCurves
curveIdx"] + PerCurveState["thresholds, integrations,
multiplicities, CV peaks"] + end + + subgraph rendering [Multi-Curve Rendering] + direction TB + ViewerMulti["ViewerMulti"] + MultiFocus["MultiFocus
active curve + overlays"] + end + + MultiEntities --> SetAllCurves + SetAllCurves --> CurveExtraction + CurveExtraction --> CurveState + CurveState --> MultiSaga + MultiSaga --> PerCurveState + PerCurveState --> ViewerMulti + ViewerMulti --> MultiFocus + + classDef hostStyle fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:1.5px + classDef stateStyle fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + classDef workflowStyle fill:#ffedd5,stroke:#f97316,color:#7c2d12,stroke-width:1.5px + classDef renderStyle fill:#dcfce7,stroke:#16a34a,color:#14532d,stroke-width:1.5px + + class MultiEntities hostStyle + class CurveState,PerCurveState stateStyle + class SetAllCurves,CurveExtraction,MultiSaga workflowStyle + class ViewerMulti,MultiFocus renderStyle diff --git a/docs/diagrams/src/redux-architecture.mmd b/docs/diagrams/src/redux-architecture.mmd new file mode 100644 index 00000000..a032d692 --- /dev/null +++ b/docs/diagrams/src/redux-architecture.mmd @@ -0,0 +1,41 @@ +flowchart LR + subgraph actions [Action Namespaces] + InteractionActions["UI and Manager"] + DomainActions["Edit, Integration,
Multiplicity, Curve"] + IntegrationActions["Forecast, Submit,
JCAMP"] + end + + subgraph orchestration [Saga Orchestration] + RootSaga["Root saga"] + SagaModules["Manager, UI,
metadata, multi-entity"] + end + + subgraph store [Redux Store] + DisplayState["Display and navigation"] + EditingState["Undoable editing"] + CurveState["Multi-curve and CV"] + HostState["Host integration state"] + end + + subgraph history [Undoable History] + Undoable["editPeak, integration,
multiplicity history"] + end + + InteractionActions --> RootSaga + DomainActions --> RootSaga + IntegrationActions --> HostState + RootSaga --> SagaModules + SagaModules --> DisplayState + SagaModules --> EditingState + SagaModules --> CurveState + EditingState --> Undoable + + classDef actionStyle fill:#ffedd5,stroke:#f97316,color:#7c2d12,stroke-width:1.5px + classDef sagaStyle fill:#fef3c7,stroke:#d97706,color:#78350f,stroke-width:1.5px + classDef stateStyle fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + classDef undoStyle fill:#f3e8ff,stroke:#9333ea,color:#581c87,stroke-width:1.5px + + class InteractionActions,DomainActions,IntegrationActions actionStyle + class RootSaga,SagaModules sagaStyle + class DisplayState,EditingState,CurveState,HostState stateStyle + class Undoable undoStyle diff --git a/docs/diagrams/src/rendering-branches.mmd b/docs/diagrams/src/rendering-branches.mmd new file mode 100644 index 00000000..c9238604 --- /dev/null +++ b/docs/diagrams/src/rendering-branches.mmd @@ -0,0 +1,31 @@ +flowchart LR + subgraph selection [Rendering Selection] + LayerContent["LayerContent"] + Decision["Layout and forecast decision"] + end + + subgraph branches [Rendering Branches] + StandardBranch["Line spectra"] + MsBranch["MS bars"] + ForecastBranch["Forecast analysis"] + MultiBranch["Multi-spectrum and CV"] + end + + subgraph d3Rendering [D3 Rendering Layer] + D3Layer["Viewer wrappers
and focus classes"] + end + + LayerContent --> Decision + Decision --> StandardBranch + Decision --> MsBranch + Decision --> ForecastBranch + Decision --> MultiBranch + branches --> D3Layer + + classDef selectionStyle fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + classDef branchStyle fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:1.5px + classDef renderingStyle fill:#dcfce7,stroke:#16a34a,color:#14532d,stroke-width:1.5px + + class LayerContent,Decision selectionStyle + class StandardBranch,MsBranch,ForecastBranch,MultiBranch branchStyle + class D3Layer renderingStyle diff --git a/docs/diagrams/src/rendering-pipeline.mmd b/docs/diagrams/src/rendering-pipeline.mmd new file mode 100644 index 00000000..4530ad3b --- /dev/null +++ b/docs/diagrams/src/rendering-pipeline.mmd @@ -0,0 +1,39 @@ +flowchart LR + subgraph state [Redux-Derived Inputs] + direction TB + Selectors["Topic2Seed
Feature2Peak
comparison selectors"] + RenderInputs["seed, peaks,
thresholds, overlays"] + end + + subgraph react [React Ownership] + direction TB + LayerContent["LayerContent"] + ViewerWrappers["Viewer wrappers
Line / Rect / Multi"] + end + + subgraph d3 [D3 Ownership] + direction TB + FocusLayer["Focus classes
LineFocus / RectFocus / MultiFocus"] + SvgLayers["SVG layers
paths, axes, clips, overlays"] + end + + subgraph helpers [D3 Helper Systems] + Helpers["draw, mount,
brush, compass, init"] + end + + Selectors --> RenderInputs + RenderInputs --> LayerContent + LayerContent --> ViewerWrappers + ViewerWrappers --> FocusLayer + FocusLayer --> Helpers + Helpers --> SvgLayers + + classDef reactStyle fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:1.5px + classDef stateStyle fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + classDef renderStyle fill:#dcfce7,stroke:#16a34a,color:#14532d,stroke-width:1.5px + classDef helperStyle fill:#f3f4f6,stroke:#6b7280,color:#374151,stroke-width:1.5px + + class LayerContent,ViewerWrappers reactStyle + class Selectors,RenderInputs stateStyle + class FocusLayer,SvgLayers renderStyle + class Helpers helperStyle diff --git a/docs/diagrams/src/runtime-synchronization-flow.mmd b/docs/diagrams/src/runtime-synchronization-flow.mmd new file mode 100644 index 00000000..532fbff5 --- /dev/null +++ b/docs/diagrams/src/runtime-synchronization-flow.mmd @@ -0,0 +1,27 @@ +sequenceDiagram + autonumber + + box rgb(219, 234, 254) Host and Initialization + participant Host as Host Props + participant LayerInit as LayerInit + end + + box rgb(237, 233, 254) State Orchestration + participant Runtime as Redux and Sagas + end + + box rgb(220, 252, 231) Rendering + participant Viewers as Viewer Wrappers + participant Focus as D3 Focus Classes + end + + Host->>LayerInit: entity, multiEntities, operations, forecast, others + LayerInit->>Runtime: reset common, layout, operation, comparison state + LayerInit->>Runtime: initialize curves, CV, integration, multiplicity + Runtime->>Viewers: selector outputs, active feature, overlay state + Viewers->>Runtime: resetAll(feature) + Runtime->>Viewers: reset shift and synchronize edit state + Viewers->>Focus: create SVG scene and D3 layers + Focus->>Runtime: user interactions as UI actions + Runtime->>Viewers: updated peaks, integrations, CV, zoom state + Viewers->>Focus: update paths, axes, clips, overlays diff --git a/docs/diagrams/src/saga-orchestration.mmd b/docs/diagrams/src/saga-orchestration.mmd new file mode 100644 index 00000000..4ff16064 --- /dev/null +++ b/docs/diagrams/src/saga-orchestration.mmd @@ -0,0 +1,41 @@ +flowchart LR + subgraph sources [Runtime Action Sources] + InitSource["LayerInit"] + InteractionSource["D3 viewers
command bar
panels"] + end + + subgraph actions [Action Flow] + ManagerCurveActions["Manager and curve actions"] + UiActions["UI interaction actions"] + end + + subgraph sagas [Saga Modules] + ManagerSaga["manager + multi-entity sagas"] + UiSaga["UI + edit + multiplicity sagas"] + MetaSaga["metadata saga"] + end + + subgraph reducers [Reducer Synchronization] + ResetState["Reset and initialization state"] + EditingState["Editing and overlay state"] + MetaState["Metadata state"] + end + + InitSource --> ManagerCurveActions + InteractionSource --> UiActions + ManagerCurveActions --> ManagerSaga + ManagerCurveActions --> MetaSaga + UiActions --> UiSaga + ManagerSaga --> ResetState + UiSaga --> EditingState + MetaSaga --> MetaState + + classDef sourceStyle fill:#dbeafe,stroke:#2563eb,color:#1e3a8a,stroke-width:1.5px + classDef actionStyle fill:#ffedd5,stroke:#f97316,color:#7c2d12,stroke-width:1.5px + classDef sagaStyle fill:#fef3c7,stroke:#d97706,color:#78350f,stroke-width:1.5px + classDef stateStyle fill:#ede9fe,stroke:#7c3aed,color:#3b0764,stroke-width:1.5px + + class InitSource,InteractionSource sourceStyle + class ManagerCurveActions,UiActions actionStyle + class ManagerSaga,UiSaga,MetaSaga sagaStyle + class ResetState,EditingState,MetaState stateStyle diff --git a/package.json b/package.json index a23b25b0..e9f6578f 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "eject": "react-scripts eject", "storybook": "start-storybook -p 3001 -c .storybook", "buildbook": "build-storybook -c .storybook -o .out", - "e2e": "cypress open" + "e2e": "cypress open", + "docs:diagrams": "bash docs/diagrams/generate.sh" }, "peerDependencies": { "react": "^17.0.2",