ml-time-graph 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,32 @@
1
+ MLTimeGraph License (MIT with Attribution)
2
+ Copyright (c) 2026 Michael Lechner
3
+
4
+ Permission is hereby granted, free of charge, to any person or organization
5
+ obtaining a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including without
7
+ limitation the rights to use, copy, modify, merge, publish, distribute,
8
+ sublicense, and/or sell copies of the Software, and to permit persons to whom
9
+ the Software is furnished to do so, subject to the following conditions:
10
+
11
+ 1. The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ 2. Attribution. Any product or service that uses the Software, including
15
+ commercial and closed-source products, must give clearly visible credit to
16
+ the author "Michael Lechner" in a location reasonably accessible to end
17
+ users (for example: product documentation, an "About" / credits screen, a
18
+ legal-notices page, or a comparable place).
19
+
20
+ 3. Commercial license (attribution waiver). Organizations that do not wish to
21
+ display the attribution required by Section 2 may obtain a separate
22
+ commercial license that waives this requirement. Such a license is available
23
+ from the author on request.
24
+ Contact: mlcgo.eu@michael-lechner.de
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ SOFTWARE.
@@ -0,0 +1,154 @@
1
+ # Dokumentation: Kinetische Temperaturaggregation (`aggregateBySlot`)
2
+
3
+ Dieses Modul bietet eine mathematische Kernfunktion zur zeitlichen Aggregation von Temperatur-Loggerdaten. Neben klassischen statistischen Werten (`min`, `max`, `avg`, `stdDev`) berechnet die Funktion die **Mean Kinetic Temperature (MKT)** sowie die präzise Dauer von Grenzwertverletzungen in Minuten.
4
+
5
+ Die Funktion ist vollständig **entkoppelt**, besitzt kein inhärentes Wissen über spezifische Produkte (Blut, Pharma, Lebensmittel) und wird rein über mathematische Parameter gesteuert.
6
+
7
+ ---
8
+
9
+ ## 1. Was ist die Mean Kinetic Temperature (MKT)?
10
+
11
+ Die MKT ist eine Methode zur Bewertung von Temperaturschwankungen bei der Lagerung und dem Transport von temperaturempfindlichen Gütern. Im Gegensatz zum arithmetischen Mittelwert (`avg`) gewichtet die MKT **höhere Temperaturen exponentiell stärker**. Dies bildet das reale biologische und chemische Degradationsverhalten (Verderb) von Produkten ab, da chemische Reaktionen bei Wärme beschleunigt ablaufen (Arrhenius-Gleichung).
12
+
13
+ ### Mathematische Formel (nach USP <1079.2>)
14
+
15
+ $$T_{K} = \frac{\frac{\Delta H}{R}}{-\ln\left(\frac{e^{-\frac{\Delta H}{R \cdot T_1}} + e^{-\frac{\Delta H}{R \cdot T_2}} + \dots + e^{-\frac{\Delta H}{R \cdot T_n}}}{n}\right)}$$
16
+
17
+ * **$T_K$**: Mittlere kinetische Temperatur in Kelvin.
18
+ * **$\Delta H$**: Aktivierungsenergie. Der globale Standard für Pharma- und GDP-Audits beträgt **$83.144\text{ kJ/mol}$** ($83144\text{ J/mol}$).
19
+ * **$R$**: Universelle Gaskonstante ($8.31446\text{ J/(mol}\cdot\text{K)}$).
20
+ * **$T_n$**: Gemessene Temperatur zum Zeitpunkt $n$ in Kelvin.
21
+ * **$n$**: Anzahl der gültigen Messwerte im Intervall.
22
+
23
+ ---
24
+
25
+ ## 2. Datenstrukturen & Interfaces
26
+
27
+ ### Eingangsdaten (`DataPoint`)
28
+ Die Rohdaten erlauben explizit `null`-Werte (z. B. bei temporärem Sensorausfall).
29
+
30
+ ```typescript
31
+ export interface DataPoint {
32
+ time: number; // Unix-Zeitstempel in Millisekunden
33
+ value: number | null; // Temperaturwert in °C oder null
34
+ }
35
+ ```
36
+
37
+ ### Konfiguration (`AggregationThresholds`)
38
+ Ermöglicht die Übergabe dynamischer Grenzwerte zur Bestimmung der Abweichungsminuten.
39
+
40
+ ```typescript
41
+ export interface AggregationThresholds {
42
+ limitLow: number; // Unterer Grenzwert in °C
43
+ limitHigh: number; // Oberer Grenzwert in °C
44
+ activationEnergy?: number; // Optional: Aktivierungsenergie in J/mol (Standard: 83144.0)
45
+ }
46
+ ```
47
+
48
+ ### Ausgangsdaten (`StatsAggregatedPoint`)
49
+
50
+ `aggregateBySlot()` mit `thresholds`-Parameter liefert
51
+ `StatsAggregatedPoint[]` — eine Intersection aus `MktPoint`,
52
+ `StdDevPoint` und `LimitStatsPoint`. Die Basis-`AggregatedPoint` trägt
53
+ nur die statistischen Pflichtfelder; alles MKT/σ/Limit-spezifische lebt
54
+ auf den spezialisierten Untertypen, sodass User unfallfrei zwischen
55
+ "reine min/max/avg"-Aggregation und "voll dekoriertem" Slot
56
+ unterscheiden können.
57
+
58
+ ```typescript
59
+ export interface AggregatedPoint {
60
+ time: number; // Startzeitpunkt des Slots in ms
61
+ min: number | null; // Niedrigste Temperatur im Slot
62
+ max: number | null; // Höchste Temperatur im Slot
63
+ avg: number | null; // Arithmetischer Mittelwert
64
+ count: number; // Anzahl valider Messwerte
65
+ }
66
+
67
+ export interface MktPoint extends AggregatedPoint {
68
+ mkt: number | null; // Mean Kinetic Temperature in °C
69
+ deltaMkt?: number | null; // Delta zur Vor-Slot-MKT (optional)
70
+ }
71
+
72
+ export interface StdDevPoint extends AggregatedPoint {
73
+ stdDev: number | null; // Standardabweichung
74
+ }
75
+
76
+ export interface LimitStatsPoint extends AggregatedPoint {
77
+ minutesAboveHigh?: number | null;
78
+ minutesBelowLow?: number | null;
79
+ }
80
+
81
+ export type StatsAggregatedPoint = MktPoint & StdDevPoint & LimitStatsPoint;
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 3. Kern-Features des Algorithmus
87
+
88
+ 1. **Umgang mit Lücken & Ausfällen (`null`-Werte):**
89
+ * `null`-Werte in den Rohdaten werden vor der mathematischen Berechnung strikt herausgefiltert. Sie verfälschen weder Minimum, Maximum noch die MKT-Exponentialsumme.
90
+ * Ein Slot, der *ausschließlich* aus `null`-Daten besteht, bleibt im Ausgabe-Array als Platzhalter erhalten (`count: 0`, alle Werte sind `null`), um Zeitleisten-Lücken in Diagrammen zu verhindern.
91
+ 2. **Präzise Zeitsynchronität (Feste Slots):**
92
+ * Die Funktion verwendet ein festes Intervallgitter. Unabhängig davon, wann Datenpunkte eintreffen, springt das Raster präzise um die exakte Intervall-Länge (z. B. stündlich).
93
+ 3. **MKT-Stabilitätsschutz (`minCountForMkt`):**
94
+ * Die MKT benötigt eine statistisch relevante Anzahl an Messwerten. Über einen Parameter kann definiert werden, ab wie vielen validen Punkten im Slot die MKT berechnet wird. Wird die Schwelle unterschritten, wird `mkt` sicher auf `null` gesetzt, anstatt mathematische Artefakte zu erzeugen.
95
+ 4. **Präzise Abweichungsdauer (Dauer in Minuten):**
96
+ * Anstatt nur Datenpunkte zu zählen, berechnet die Funktion die reale zeitliche Differenz zwischen aufeinanderfolgenden Loggereinträgen. Dadurch werden unregelmäßige Datenaufzeichnungen (z. B. Event-basierte Logger) exakt in Minuten erfasst.
97
+
98
+ ---
99
+
100
+ ## 4. Integration & Nutzung im System
101
+
102
+ Da die Kernfunktion mathematisch dumm gehalten ist, werden die branchenspezifischen Vorgaben (Pharma, Blut, Lebensmittel) **außerhalb** über die Konfigurationsschicht verwaltet.
103
+
104
+ ### Typische Produkt-Grenzwerte im Überblick
105
+
106
+
107
+ | Produktkategorie | Unteres Limit (`limitLow`) | Oberes Limit (`limitHigh`) | Aktivierungsenergie ($\Delta H$) |
108
+ | :--- | :--- | :--- | :--- |
109
+ | **Pharma (Kühlkette)** | $+2.0^\circ\text{C}$ | $+8.0^\circ\text{C}$ | $83144\text{ J/mol}$ (USP Standard) |
110
+ | **Pharma (Raumtemperatur)** | $+15.0^\circ\text{C}$ | $+25.0^\circ\text{C}$ | $83144\text{ J/mol}$ (USP Standard) |
111
+ | **Blutkonserven** | $+2.0^\circ\text{C}$ | $+6.0^\circ\text{C}$ | $83144\text{ J/mol}$ (Standard-Anwendung) |
112
+ | **Lebensmittel (Frische)** | $0.0^\circ\text{C}$ | $+4.0^\circ\text{C}$ | *MKT irrelevant / entfällt meist* |
113
+ | **Tiefkühlkost** | $-40.0^\circ\text{C}$ | $-18.0^\circ\text{C}$ | *MKT irrelevant / entfällt meist* |
114
+
115
+ ### Code-Beispiele zur Verwendung
116
+
117
+ #### Beispiel 1: Standard-Aggregation stündlich ohne Limits
118
+ Es werden nur die Basis-Statistiken und die MKT berechnet. Die Alarmminuten bleiben `null`.
119
+ ```typescript
120
+ import { aggregateBySlot } from "ml-time-graph/analyze";
121
+
122
+ const hourlyStats = aggregateBySlot(rawLoggerData, "hourly");
123
+ ```
124
+
125
+ #### Beispiel 2: Aggregation für Blutkonserven (Tägliches Intervall)
126
+ Hier werden die Grenzwerte injiziert. Die Funktion berechnet automatisch, wie viele Minuten die Blutkonserven außerhalb der erlaubten $2^\circ\text{C} - 6^\circ\text{C}$ lagen.
127
+ ```typescript
128
+ const bloodThresholds = {
129
+ limitLow: 2.0,
130
+ limitHigh: 6.0
131
+ // activationEnergy wird weggelassen -> nutzt automatisch internen 83144 J/mol Fallback
132
+ };
133
+
134
+ const dailyBloodReport = aggregateBySlot(
135
+ rawLoggerData,
136
+ "daily",
137
+ undefined, // customInterval
138
+ bloodThresholds,
139
+ 12 // MKT erst berechnen, wenn mindestens 12 gültige Werte im Tag liegen
140
+ );
141
+ ```
142
+
143
+ #### Beispiel 3: Spezial-Laborwert (Custom Aktivierungsenergie)
144
+ Sollte ein Audit für ein bestimmtes biologisches Plasma-Protein eine abweichende Aktivierungsenergie vorschreiben, kann diese direkt mitgegeben werden:
145
+ ```typescript
146
+ const customLabThresholds = {
147
+ limitLow: 1.0,
148
+ limitHigh: 4.5,
149
+ activationEnergy: 65000.0 // 65 kJ/mol statt 83.144 kJ/mol
150
+ };
151
+
152
+ const labReport = aggregateBySlot(rawLoggerData, "hourly", undefined, customLabThresholds);
153
+ ```
154
+
package/README.de.md ADDED
@@ -0,0 +1,154 @@
1
+ # 📈 MLTimeGraph
2
+
3
+ *[English](README.md) · Deutsch*
4
+
5
+ **TypeScript-Library für die grafische Darstellung von Messwerten** — Zeitreihen
6
+ schnell und unkompliziert aufbereiten, mit Fokus auf Reporting und Incident-Analyse.
7
+
8
+ > Voll typisiert · i18n-aware · DOM-freier SVG-Renderer (Browser & serverseitig) · keine Laufzeit-Abhängigkeiten · ESM-only
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install ml-time-graph # oder: pnpm add / yarn add
14
+ ```
15
+
16
+ Voraussetzung: **Node 18+**. Die Library ist **ESM-only** — funktioniert
17
+ out of the box mit jedem modernen Bundler (Vite, Rollup, esbuild,
18
+ Webpack 5+, Next.js, Astro, …) und in Node-ESM (`"type": "module"` in
19
+ deiner `package.json`, oder `.mjs`-Dateien).
20
+
21
+ Vier Subpath-Imports:
22
+
23
+ | Import | Zweck |
24
+ | --- | --- |
25
+ | `ml-time-graph` | User-API — `mount`, `MLTimeGraph`, `SVGRenderer`, `attachTooltip`, alle Input- + Style-Typen |
26
+ | `ml-time-graph/analyze` | Aggregation + Statistik (`aggregateBySlot`, `rollingMkt`, `rollingStdDev`, `downsample`, …) |
27
+ | `ml-time-graph/interaction` | Optionale Widgets (`Zoom`, `Minimap`, `Tooltip`) |
28
+ | `ml-time-graph/internals` | Building Blocks für eigene Renderer (freie Render-Funktionen, Scales, Axis-Klassen, `DrawCommand`-Modell, …) |
29
+
30
+ ### Reines HTML / CDN
31
+
32
+ Kein Build-Schritt? Hol's direkt von einem ESM-CDN, der npm proxyed:
33
+
34
+ ```html
35
+ <div id="chart"></div>
36
+ <script type="module">
37
+ import { mount } from 'https://esm.sh/ml-time-graph@1';
38
+ // oder: 'https://cdn.jsdelivr.net/npm/ml-time-graph@1/+esm'
39
+
40
+ mount('#chart', {
41
+ series: [{ name: 'Temp', data: [/* … */] }],
42
+ });
43
+ </script>
44
+ ```
45
+
46
+ Pinne eine Major-Version (`@1`), damit dich Breaking Changes nicht überraschen.
47
+
48
+ ## Quickstart
49
+
50
+ ```ts
51
+ import { mount } from 'ml-time-graph';
52
+
53
+ mount('#chart', {
54
+ series: [{
55
+ name: 'Temperatur',
56
+ style: { line: { color: '#ef4444' } },
57
+ data: [
58
+ { time: Date.UTC(2026, 0, 1, 0), value: 18.2 },
59
+ { time: Date.UTC(2026, 0, 1, 1), value: 19.1 },
60
+ ],
61
+ }],
62
+ tooltip: { show: true }, // optionaler Hover-Tooltip mit Visual-Picks
63
+ });
64
+ ```
65
+
66
+ 📖 **Nutzung & API → [USAGE.md](USAGE.md)** *(englisch)*
67
+
68
+ ## Galerie
69
+
70
+ <p align="center">
71
+ <a href="ex1.svg"><img src="ex1.svg" alt="Linie mit Zonen-Färbung und Threshold-Fills" width="300"></a>
72
+ <a href="ex2.svg"><img src="ex2.svg" alt="Multi-Zonen-Hatch-Fills" width="300"></a>
73
+ <a href="ex3.svg"><img src="ex3.svg" alt="stdDevBand + movingMkt-Overlays auf einem Sensorstrom" width="300"></a>
74
+ </p>
75
+
76
+ Typische Sensor-Darstellung (links), Multi-Zonen-Schraffuren für
77
+ Incident-Reports (Mitte), Sensorstrom mit Statistik-Overlays (rechts:
78
+ ±2σ-Band + 12-h-MKT) — alles dieselbe Library, alle in der
79
+ Demo-Galerie zu sehen (`pnpm dev`).
80
+
81
+ ---
82
+
83
+ ## Warum schon wieder eine neue Library?
84
+
85
+ Alle Libraries, die ich in der Vergangenheit professionell genutzt habe, haben
86
+ irgendwann ihr Lizenzmodell von *frei* auf *kostenpflichtig* umgestellt. Das wird
87
+ zum Problem, sobald ein Wechsel nicht möglich ist: Man muss „alte" Libraries
88
+ plötzlich ersetzen oder selbst weiterpflegen (z. B. an aktuelle Browser-Entwicklungen
89
+ anpassen).
90
+
91
+ Ich brauche keine 100 Chart Typen.
92
+
93
+ MLTimeGraph ist **nicht** dazu gedacht, tausend verschiedene Chart-Typen
94
+ abzudecken. Der Fokus liegt darauf, **Messwerte — also Zeit + Daten — schnell und
95
+ unkompliziert grafisch aufzubereiten**. Ein besonderes Augenmerk liegt auf der
96
+ **Incident-Analyse**: etwa wenn Sensoren zur Überwachung von Geräten eingesetzt
97
+ werden und die nachträgliche Auswertung entscheidend ist.
98
+
99
+ ## Ziele
100
+
101
+ - ✅ **Keine Browser-Abhängigkeit** — das Rendering ist vom DOM entkoppelt (SVG-String, im Browser und serverseitig)
102
+ - ✅ **Voll typisiert** — durchgängig TypeScript
103
+ - ✅ **Einfache Integration** in bestehende Projekte
104
+ - ✅ **i18n-aware** — lokalisierte Zeitachsen und Formatierung
105
+ - ✅ **Einfache, aber umfangreiche Konfiguration** — Farben, Linienstärken, Styles
106
+ - ✅ Typische Darstellungen für Messwerte:
107
+ - Markierung von Bereichen (Highlights)
108
+ - Thresholds (Bänder, Linien, Flächen)
109
+ - min / max / avg-Darstellungen
110
+ - Incident-Analysen — markante Ereignisse und Werte markieren
111
+
112
+ ## Nicht-Ziele
113
+
114
+ - ❌ **High Performance** — für den Hauptanwendungsfall (Reporting, Analyse) nicht notwendig
115
+ - ❌ **Volle Interaktivität** — Zoom/Pan ja, aber in Grenzen
116
+ - ❌ GPU-Performance-Optimierungen o. Ä.
117
+
118
+ ## Ausblick / in Überlegung
119
+
120
+ - Möglichkeiten der „Live"-Darstellung
121
+ - Einfache Anbindung von Backends (woher kommen meine Daten?)
122
+ - svelte wrapper
123
+
124
+ ## Demos & Entwicklung
125
+
126
+ ```bash
127
+ pnpm install
128
+ pnpm dev # startet die Demo-Galerie (Vite)
129
+ ```
130
+
131
+ | Script | Zweck |
132
+ | --- | --- |
133
+ | `pnpm dev` | Demo-Galerie lokal starten |
134
+ | `pnpm test` | Test-Suite (Vitest) |
135
+ | `pnpm typecheck` | Library typprüfen |
136
+ | `pnpm typecheck:demos` | Demos typprüfen |
137
+ | `pnpm build` | Library bauen (ESM-Bundle + Types) |
138
+
139
+ ## Lizenz
140
+
141
+ © 2026 Michael Lechner — **MIT mit Attribution-Klausel** (siehe [LICENSE](LICENSE)).
142
+
143
+ Nutzung, Änderung und Verbreitung — **auch kommerziell und in Closed-Source-Produkten** —
144
+ sind kostenlos erlaubt, sofern der Autor **„Michael Lechner" sichtbar genannt** wird
145
+ (z. B. in Doku, Impressum/Rechtliches oder einem About-/Credits-Bereich) und der
146
+ Copyright-Hinweis im Quellcode erhalten bleibt.
147
+
148
+ **Ohne Nennung:** Unternehmen, die die Attribution nicht zeigen möchten, können eine
149
+ **kommerzielle Lizenz auf Anfrage** erhalten, die diese Pflicht aufhebt — Kontakt: ‹mlcgo.eu@michael-lechner.de›.
150
+
151
+ ---
152
+
153
+ <sub>Das Projekt diente ursprünglich als Test für Crush-Development mit Ollama +
154
+ neuem LLM — bestanden 😉, brauchte aber doch einige Nacharbeit.</sub>
package/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # 📈 MLTimeGraph
2
+
3
+ *English · [Deutsch](README.de.md)*
4
+
5
+ **A TypeScript library for visualizing measurement data** — turn time series into
6
+ clear, readable charts quickly, with a focus on reporting and incident analysis.
7
+
8
+ > Fully typed · i18n-aware · DOM-free SVG output (browser & server) · zero runtime dependencies · ESM-only
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install ml-time-graph # or: pnpm add / yarn add
14
+ ```
15
+
16
+ Requires **Node 18+**. The library is **ESM-only** — works out of the
17
+ box in any modern bundler (Vite, Rollup, esbuild, Webpack 5+, Next.js,
18
+ Astro, …) and in Node ESM (`"type": "module"` in your `package.json`,
19
+ or `.mjs` files).
20
+
21
+ Four subpath imports:
22
+
23
+ | Import | Purpose |
24
+ | --- | --- |
25
+ | `ml-time-graph` | User API — `mount`, `MLTimeGraph`, `SVGRenderer`, `attachTooltip`, all input + style types |
26
+ | `ml-time-graph/analyze` | Aggregation + statistics (`aggregateBySlot`, `rollingMkt`, `rollingStdDev`, `downsample`, …) |
27
+ | `ml-time-graph/interaction` | Optional widgets (`Zoom`, `Minimap`, `Tooltip`) |
28
+ | `ml-time-graph/internals` | Building blocks for custom renderers (free render-functions, scales, axis classes, `DrawCommand` model, …) |
29
+
30
+ ### Plain HTML / CDN
31
+
32
+ No build step? Pull it straight from an ESM CDN that proxies npm:
33
+
34
+ ```html
35
+ <div id="chart"></div>
36
+ <script type="module">
37
+ import { mount } from 'https://esm.sh/ml-time-graph@1';
38
+ // or: 'https://cdn.jsdelivr.net/npm/ml-time-graph@1/+esm'
39
+
40
+ mount('#chart', {
41
+ series: [{ name: 'Temp', data: [/* … */] }],
42
+ });
43
+ </script>
44
+ ```
45
+
46
+ Pin a major version (`@1`) to avoid surprise breaking changes.
47
+
48
+ ## Quickstart
49
+
50
+ ```ts
51
+ import { mount } from 'ml-time-graph';
52
+
53
+ mount('#chart', {
54
+ series: [{
55
+ name: 'Temperature',
56
+ style: { line: { color: '#ef4444' } },
57
+ data: [
58
+ { time: Date.UTC(2026, 0, 1, 0), value: 18.2 },
59
+ { time: Date.UTC(2026, 0, 1, 1), value: 19.1 },
60
+ ],
61
+ }],
62
+ tooltip: { show: true }, // optional hover tooltip with visual picks
63
+ });
64
+ ```
65
+
66
+ 📖 **Usage & API → [USAGE.md](USAGE.md)**
67
+
68
+ ## Gallery
69
+
70
+ <p align="center">
71
+ <a href="ex1.svg"><img src="ex1.svg" alt="Line chart with zoned colouring + threshold fills" width="300"></a>
72
+ <a href="ex2.svg"><img src="ex2.svg" alt="Hatched multi-zone threshold fills" width="300"></a>
73
+ <a href="ex3.svg"><img src="ex3.svg" alt="stdDevBand + movingMkt overlays on a sensor stream" width="300"></a>
74
+ </p>
75
+
76
+ A typical sensor visualisation (left), multi-zone hatched fills for
77
+ incident-style reports (center), and a sensor stream with statistic
78
+ overlays (right: ±2σ band + 12 h MKT) — all rendered with the same
79
+ library, all live in the demo gallery (`pnpm dev`).
80
+
81
+ ---
82
+
83
+ ## Why another chart library?
84
+
85
+ Every charting library I relied on professionally eventually switched its license
86
+ from *free* to *paid* — a real problem when you can't follow that move: you end up
87
+ having to replace "old" libraries or maintain them yourself (e.g. keeping up with
88
+ browser changes).
89
+
90
+ I don't need a hundred chart types.
91
+
92
+ MLTimeGraph is **not** meant to cover a thousand chart types. The focus is on turning
93
+ **measurements — time + values — into clear charts quickly**, with particular
94
+ attention to **incident analysis**: e.g. when sensors monitor equipment and the
95
+ after-the-fact evaluation is what matters.
96
+
97
+ ## Goals
98
+
99
+ - ✅ **No browser dependency** — rendering is decoupled from the DOM (SVG string, in the browser and server-side)
100
+ - ✅ **Fully typed** — TypeScript throughout
101
+ - ✅ **Easy integration** into existing projects
102
+ - ✅ **i18n-aware** — localized time axes and formatting
103
+ - ✅ **Simple yet thorough configuration** — colors, line widths, styles
104
+ - ✅ Typical measurement visualizations:
105
+ - region highlights
106
+ - thresholds (lines, fills, zones)
107
+ - min / max / avg displays
108
+ - incident analysis — mark notable events and values
109
+
110
+ ## Non-goals
111
+
112
+ - ❌ **High performance** — not needed for the main use case (reporting, analysis)
113
+ - ❌ **Full interactivity** — zoom/pan yes, but within limits
114
+ - ❌ GPU performance optimizations and the like
115
+
116
+ ## Outlook / under consideration
117
+
118
+ - "Live" rendering options
119
+ - Simple backend integration (where does my data come from?)
120
+ - Svelte wrapper
121
+
122
+ ## Demos & development
123
+
124
+ ```bash
125
+ pnpm install
126
+ pnpm dev # start the demo gallery (Vite)
127
+ ```
128
+
129
+ | Script | Purpose |
130
+ | --- | --- |
131
+ | `pnpm dev` | run the demo gallery locally |
132
+ | `pnpm test` | test suite (Vitest) |
133
+ | `pnpm typecheck` | type-check the library |
134
+ | `pnpm typecheck:demos` | type-check the demos |
135
+ | `pnpm build` | build the library (ESM bundle + types) |
136
+
137
+ ## License
138
+
139
+ © 2026 Michael Lechner — **MIT with an Attribution clause** (see [LICENSE](LICENSE)).
140
+
141
+ Use, modification and distribution — **including in commercial and closed-source
142
+ products** — are free of charge, provided the author **"Michael Lechner" is visibly
143
+ credited** (e.g. in documentation, a legal-notices page, or an About/credits screen)
144
+ and the copyright notice is retained in the source.
145
+
146
+ **Without attribution:** organizations that prefer not to display the attribution can
147
+ obtain a **commercial license on request** that waives it — contact: ‹mlcgo.eu@michael-lechner.de›.
148
+
149
+ ---
150
+
151
+ <sub>Originally a test of Crush development with Ollama + a new LLM — passed 😉, but it
152
+ did need some rework.</sub>