KMPCharts/CHANGELOG.md
Trentin Davide 07197ed8df
Some checks are pending
CI / build (push) Waiting to run
chore(portfolio): v1.0.0 prep — docs, CI, demos
Autonomous portfolio prep batch. All gates green: 207 tests pass on
iosSimulatorArm64Test, assembleRelease green, apiCheck green, app builds.

Docs:
- CHANGELOG.md (Keep-a-Changelog, retroactive v0.1-v0.5)
- CONTRIBUTING.md (build + style + PR workflow + perf rules)
- chart-realtime/README.md rewrite for v0.5.0 API surface
- docs/PERFORMANCE.md (LoD perf table, alloc budget, memory budget,
  stability report, threading model)

CI (.github/):
- workflows/ci.yml — macos-latest, JDK 21, konan cache, full gate
- workflows/release.yml — tag-triggered, GitHub release with XCFramework
  + AAR; Maven Central as commented TODO (needs OSSRH + GPG secrets)
- dependabot.yml — weekly gradle + actions updates
- pull_request_template.md + 2 issue templates (bug, feature)

Android demo polish:
- DemoScreen + ControlPanel (Material3 surface)
- Sample rate slider (10..200Hz), 1-6 signal count, per-signal waveform
  picker (sine/square/triangle/noise), window seconds, LoD picker, Y
  label mode, Y formatter, interaction toggle, theme switch, status chips
- Single producer coroutine (cleaner crosshair readout, easier rate
  accounting)
- SignalGenerator + WaveformType + DemoConfig + PushCounter

iOS demo scaffold (samples/ios/ChartRealtimeDemo/):
- SPM Package.swift with local binaryTarget pointing to
  chart-realtime/build/XCFrameworks/debug/ChartRealtime.xcframework
- DemoApp.swift + DemoView.swift (smoke test confirming Kotlin types
  accessible from Swift)
- README documents the ComposeUIViewController shim needed in
  chart-realtime/src/iosMain/ for full SwiftUI hosting (deferred to
  v1.0.0 T1)
- xcodebuild against iOS Simulator builds green

Debt:
- gradle.properties: removed kotlin.internal.klibs.non-packed=false
  (Compose-MP 1.11.0 final no longer needs the bypass; apiCheck +
  native compile + 207 tests still green)
- 8 remaining deprecation warnings tied to com.android.library + KMP
  plugin combo; clearing them requires migration to
  com.android.kotlin.multiplatform.library 9.2.0 which forces ABI
  baseline regen + Maven publish restructure + XCFramework hierarchy
  expansion. Deferred as dedicated ticket.

Skipped (need user):
- CODE_OF_CONDUCT.md (content classifier blocks Contributor Covenant
  verbatim)
- Maven Central T1 (Sonatype + GPG)
- Benchmark vs competitors T4 (methodology + Maven coords)
- Logo/banner/GIF T7 (design assets)
- Tag + GitHub release T10 (rename pending)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:54:26 +02:00

181 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Changelog
All notable changes to `chart-realtime` documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
Project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) from `1.0.0` onward.
Pre-`1.0.0` releases share the `0.5.0-SNAPSHOT` Maven coordinate per
`group:dev.dtrentin`, `artifact:chart-realtime`. Stable coordinates and Maven
Central publishing land in `1.0.0`.
## [Unreleased]
### Changed
- KGP 2.3 / AGP 9 deprecation warnings cleanup (in progress)
## [0.5.0] — 2026-05-22 — Architecture + Interaction
### Added
- **Interaction layer (T7)** — pinch zoom, drag pan, tap crosshair via `ChartInteractionState`
+ `rememberChartInteractionState()`. `ViewportMode` sealed (`Following` / `Frozen` /
`History(anchorMs)`). Swipe-to-right-edge resumes `Following`. `InverseProjection`
helper: pixel → ms + bisect nearest-sample
- **`LodStrategy` interface (T2)** + three default impls in `lod/` package:
- `MinMaxLodStrategy` — per-pixel-column min+max preservation
- `LttbLodStrategy` — Largest-Triangle-Three-Buckets
- `MinMaxLttbLodStrategy` — SOTA per [arXiv 2305.00332](https://arxiv.org/abs/2305.00332),
1.80× faster than pure LTTB on Kotlin/Native scalar code (10× target claim assumes Rust + SIMD)
- **`SignalRenderer` public interface (T3)** + `LineSignalRenderer` default object.
Custom renderer impl-able in ~30 LOC. Primitive-only signature, no `SignalEntry` leak
- **`AxisFormatter` interface (T4)** + four default impls: `TimeAxisFormatter`,
`DecimalAxisFormatter`, `DateTimeAxisFormatter`, `UnitAxisFormatter`
- **`ChartConfig` split (T5)** — `DataConfig` + `AxisConfig` + `RenderConfig` + new
`FrameRate` sealed type (`Display` / `Fixed(fps)`)
- **`TieredBuffer.snapshotWindow` (T1)** — bisect-based windowed snapshot for 30× fewer
per-frame copies on small windows in large buffers
- **`RealtimeChartState.clear()` (T10)** — public purge API (preserves signal registrations)
- **`RealtimeChartState.signalsArray` (T9)** — cached array view of signals, invalidated
only by `addSignal` / `removeSignal`
- **`@Immutable` / `@Stable` annotations (T6)** on all public types — 0 unstable
- **`NumberFormat.formatFixed` overflow guard (D4)** — routes to scientific @ `|v|≥1e19`
### Changed (BREAKING)
- **Removed `LodMode` enum** — replaced by `LodStrategy` interface
- **Removed `LodDecimator` internal class** — replaced by `LodStrategy` impls
- **Removed `ChartConfig.targetFps` field** — replaced by `RenderConfig.frameRate: FrameRate`
- **`ChartConfig` flat fields removed** — accessed via nested configs: `config.data.xWindowSeconds`,
`config.render.theme`, `config.axis.xLabelMode`, etc. Top-level inline accessors retained for
`xWindowSeconds` and `theme`
- **`SignalRenderer` signature changed** — internal object → public interface with
primitive-only params (`Color`, `Float`, `FloatArray`, `Path`)
- **`SignalConfig.renderer` field added** — per-signal renderer override, defaults to
`LineSignalRenderer` singleton
- **LTTB upper bound aligned to half-open semantic (D3)** — `[start, start+windowMs)`
matches `TieredBuffer.snapshot`. Samples at exact upper bound now excluded
- **`Pair<Float, Float>` removed from `AxisRenderer.resolveYRange`** — replaced by
`FloatArray` out param
### Performance
- LineSignalRenderer caches `Stroke` instances per strokeWidth (T8)
- AxisRenderer caches `TextStyle` instances (16-entry cap)
- Render path: 0 `Pair`/`Map`/`Iterator` allocations per frame at steady state
### Toolchain
- Kotlin 2.1.0 → 2.3.21
- Compose Multiplatform 1.8.0 → 1.11.0
- Android Gradle Plugin 8.7.3 → 9.2.0
- Gradle 8.11.1 → 9.5.1
- coroutines 1.9.0 → 1.11.0; kotlin-test → 2.3.21
- **Dropped `kotlinx-datetime`** — replaced by stdlib `kotlin.time.Clock` (Kotlin 2.3 stable)
- **Dropped `iosX64` target** — Compose Multiplatform 1.11.0 no longer ships `ios_x64`
variants (Intel Mac iOS simulators deprecated by Apple since Xcode 15+)
### Tests
- 107 → **207** (+100). Coverage: `lod/` (24), `interaction/` (29), `model/ChartConfigTest` (8),
`render/AxisFormatterTest` (10), `RealtimeChartStateTest` (+9), `NumberFormatTest` (+6),
`TieredBufferTest` (+10), `CircularBufferTest` (+10), `AxisRendererTest` (3),
`LineSignalRendererTest` (2)
### API surface
- `chart-realtime.api`: 161 → 428 LOC
- `chart-realtime.klib.api`: 211 → 501 LOC
## [0.4.0] — 2026-05-21 — Portfolio Hardening
15 tasks shipped via 3-reviewer audit (android-reviewer + mobile-performance-reviewer +
mobile-architect). Library moved from "prototype" to "senior-engineer-built KMP library".
### Added
- LICENSE MIT
- `explicitApi() Strict` + Binary Compatibility Validator (BCV) 0.16.3 + klib enabled
- ABI baseline locked (`chart-realtime.api` 161 LOC, `chart-realtime.klib.api` 211 LOC)
- `hasData` boolean flag (replaces `POSITIVE_INFINITY` / `Float.MAX_VALUE` sentinels)
- `Snapshot.withMutableSnapshot` + `SnapshotApplyConflictException` retry helper
- M4 binning for Tier1/Tier2 (firstTs/V, minTs/V, maxTs/V, lastTs/V) with chronological dedup
- NaN/Inf/backward-timestamp guards on `push`; per-signal `lastPushedTs` tracking
- NumberFormat extracted from AxisRenderer (38 tests)
- 25 buffer/LoD tests (tier roll, window crossing, clear, LTTB branches)
### Changed
- `CircularBuffer.writeIndex` `Int``Long` (fixes 124d overflow @ 200Hz)
- `kotlinx-datetime` 0.6.2 replaces `expect/actual currentTimeMs` (Platform.kt deleted)
- `dataVersion``mutableLongStateOf` (Compose-observable, idle = 0 recompositions)
- Single `buffer.snapshot()` per signal per frame via per-signal scratch arrays
- `TIER1_CAPACITY` 12k → 24k, `TIER2_CAPACITY` 5.4k → 10.8k (×2 → ×4 for M4 binning)
- Memory per signal: ~760 KB → ~1.14 MB
### Removed
- `RenderLoop.kt`, `rememberFrameTick` (deleted)
- `maxBufferSeconds`, `xLabelDecimals`, `SignalConfig.label` (dead config)
- All `!!` and `@Suppress("UNUSED_EXPRESSION")` from commonMain
### Internal lockdown
- `CircularBuffer`, `TieredBuffer`, `LodDecimator`, `dataVersion`, `rememberFrameTick`,
capacity constants
### Tests
- 33 → 107 (+74)
## [0.3.0] — 2026-05-21 — iOS Build Pipeline + Dirty Flag Render
### Added
- iOS targets compile + link green: `iosX64`, `iosArm64`, `iosSimulatorArm64`
- `applyDefaultHierarchyTemplate()` auto-wires `iosMain` / `iosTest`
- XCFramework `ChartRealtime` (debug + release): `ios-arm64` + `ios-arm64_x86_64-simulator`
- Dirty-flag render: `dataVersion` counter, skip Canvas draw when no new data
- `targetFps = 30` default in `ChartConfig` (null = max display refresh rate)
- iOS-side `Platform.ios.kt`: `NSDate()` + `timeIntervalSinceReferenceDate` + 1970 epoch
offset (978_307_200s)
- KDoc on `RealtimeChart`, `RealtimeChartState`, `ChartConfig`, `LodMode`, `AxisLabelMode`
- README sections: LoD, AxisLabels, Material3
### Fixed
- Multiplatform replacements for JVM-only `String.format`: `formatFixed` /
`formatScientific` / `pow10` helpers in `AxisRenderer`
- `kotlin.concurrent.Volatile` + `kotlin.jvm.JvmName` imports (OptionalExpectation)
- X axis: show negative values instead of clamping to `"0s"`
### Tests
- 17 → 33 (`RealtimeChartStateTest`: dataVersion + signal lifecycle)
## [0.2.0] — 2026-05-21 — Polish
### Added
- `TieredBuffer`: 3-tier ring buffer (5min full-rate, 10min @ 10Hz, 45min @ 1Hz),
~5MB footprint for 1h of data
- `LodMode.LTTB` (Largest-Triangle-Three-Buckets, O(n) decimation)
- `AxisLabelMode` (`INSIDE` / `BESIDE` / `HIDDEN`) with `chartLeft` / `chartBottom` insets
- `T0`-relative X-axis timestamps with mm:ss / hh:mm:ss formatting (negative supported)
- Material3 dynamic theming (light/dark, wallpaper-based, API 31+)
- Camera cutout / edge-to-edge support
- Sensor domain layer (Clean Architecture)
- Koin 4.0.0 DI wiring
- Sensor rate cap: 200Hz (5_000μs)
### Changed
- Package rename: `eu.henesis``dev.dtrentin`
- `TieredBuffer` binning: `MEAN``MIN_MAX` (composable across tier boundaries, no
synthetic values, no C7 spike artifacts)
- `LodDecimator`: pre-allocated scratch arrays (zero GC at render time)
### Removed
- `LodMode.MEAN` (introduced synthetic values absent in raw signal)
## [0.1.0] — 2026-05-20 — MVP
Initial implementation. Six phases shipped:
1. Module scaffold (KMP + Compose Multiplatform + Android library)
2. `CircularBuffer` + `LodDecimator`
3. `RealtimeChartState` API
4. `RealtimeChart` composable + Canvas-based `SignalRenderer` + `AxisRenderer`
5. Demo app (`:app`)
6. Polish + initial docs
[Unreleased]: https://github.com/davide-trentin/KMPCharts/compare/v0.5.0...HEAD
[0.5.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.5.0
[0.4.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.4.0
[0.3.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.3.0
[0.2.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.2.0
[0.1.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.1.0