Some checks are pending
CI / build (push) Waiting to run
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>
5.8 KiB
5.8 KiB
Contributing to chart-realtime
Thanks for considering a contribution. This document covers the build process, project conventions, and the PR workflow.
Project layout
chart-realtime/ Kotlin Multiplatform library module
├── src/commonMain/ Shared code (Android + iOS)
├── src/commonTest/ Shared tests
├── src/androidMain/ Android-only code (currently empty)
├── src/iosMain/ iOS-only code (currently empty post-v0.5.0)
└── api/ ABI baseline (managed by BCV)
app/ Android demo app (sandbox)
samples/ios/ SwiftUI iOS demo (planned for v1.0.0)
.paul/ Project planning state (PAUL workflow)
Prerequisites
- JDK 21 (Temurin recommended)
- Xcode (for iOS targets; macOS only)
- Android SDK with
compileSdk 35 - No global Gradle install needed — wrapper provided
Building
# Library — Android variant
./gradlew :chart-realtime:assembleRelease
# Library — iOS variants
./gradlew :chart-realtime:compileKotlinIosArm64
./gradlew :chart-realtime:compileKotlinIosSimulatorArm64
# XCFramework (consumes the library from Swift)
./gradlew :chart-realtime:assembleChartRealtimeXCFramework
# Android demo app
./gradlew :app:installDebug
# Publish library to local Maven (for consumer testing)
./gradlew :chart-realtime:publishToMavenLocal
Testing
# Shared common tests run on iOS simulator (canonical CI gate)
./gradlew :chart-realtime:iosSimulatorArm64Test
# ABI compatibility check (must stay green; regen via apiDump only on intentional break)
./gradlew :chart-realtime:apiCheck
./gradlew :chart-realtime:apiDump
207 tests on the canonical run as of v0.5.0. Any PR must keep that count from
shrinking.
Code style
- Kotlin official conventions;
explicitApi() Strictenforced onchart-realtime - 4-space indent, no tabs
- 120-column soft limit; wrap when readability suffers
- Avoid
!!and@SuppressincommonMain(audit blockers since v0.4.0) @Immutableon data classes participating in@Composableparameter inference;@Stableon mutable state holders observing throughMutableState- Use
kotlin.time.Clockfor current time; do not reintroducekotlinx-datetime
Architecture conventions
- Buffer layer (
buffer/) — zero-alloc data structures. Pre-allocated scratch arrays. No allocations on the render hot path - LoD layer (
lod/) — pluggableLodStrategyimpls. Each constructor takesmaxCount+maxPixelWidthto size its scratch arrays once - Render layer (
render/) —internal objectfor the Canvas extension funs. CacheStroke/TextStyleinstances; rely on ComposeTextMeasurerLRU for text layout - Interaction layer (
interaction/) — hoistable@Stablestate holders. Mutations route through Compose-observable backing state. Pointer-input lambdas read fromLongArraycache slots populated inside the draw lambda (1-frame stale is acceptable for gesture handling)
Pull request workflow
- Open an issue first for non-trivial changes (new features, API additions, performance work). Bug fixes can skip this
- Branch from
master. Name:feat/<short>,fix/<short>,refactor/<short> - Write tests. Coverage requirements:
- Bug fix: regression test reproducing the bug, then the fix
- New public API: unit tests for happy path + edge cases
- Performance change: benchmark before/after numbers in PR body
- Run the full gate locally before pushing:
./gradlew :chart-realtime:iosSimulatorArm64Test \ :chart-realtime:assembleRelease \ :chart-realtime:apiCheck \ :app:assembleDebug - ABI changes — if
apiCheckfails:- If the change is intentional and you accept the break, regenerate the baseline
with
./gradlew :chart-realtime:apiDump, commit the diff, and call it out in the PR description - If the change is unintentional, fix the code instead
- If the change is intentional and you accept the break, regenerate the baseline
with
- Commit format —
<scope>: <imperative summary>for the subject. Body lists touched modules and intent. Example:feat(interaction): add momentum scrolling to drag pan Modules: chart-realtime (interaction, RealtimeChart) - No
--no-verifyon commit hooks. Investigate and fix the underlying issue if a hook fails - Squash on merge for small PRs; preserve commits for large feature work
Issue triage
Bug reports must include:
- Affected version
- Platform (Android + API level / iOS + sim/device + iOS version)
- Reproducer (smallest snippet that triggers the issue)
- Expected vs actual behavior
Feature requests must include:
- Use case (the underlying problem, not the proposed solution)
- Why the existing API doesn't already solve it
- Acceptance criteria (what "done" looks like)
Performance philosophy
Library targets sustained 200Hz × multi-signal sensor data with bounded memory. The hot path is the per-frame Compose recomposition + Canvas draw lambda.
Three rules:
- No allocations on the render hot path. All scratch arrays pre-allocated;
Stroke/TextStylecached;PairandMapiterators replaced withFloatArrayout-params and cached arrays - Snapshot once per signal per frame.
SignalEntry.scratchTs/scratchVpre-allocated; the Y-range scan and the path generation read the same snapshot - Bounded memory regardless of session length.
CircularBuffer.writeIndexisLong; tier ring buffers overwrite oldest first; 1h buffer = ~1.14 MB per signal
Any change that breaks one of these requires explicit justification + benchmark numbers + tests that pin the new behavior.
Reporting security issues
Email davide.trentin@henesis.eu directly. Do not open a public issue.
License
By contributing, you agree your contributions are licensed under the MIT License of this project.