Collecting follow-ups found during the audit in #66 that were intentionally deferred because they change the public API of the library. These should ship together in the next major version (e.g. 5.0) so consumers only migrate once.
1. JetLimeEventStyle.position / pointPlacement should be val
Current: both are public var properties on an @Immutable class and are mutated via setPosition / setPointPlacement / JetLimeEventDefaults.eventStyle(...).apply { ... }.
Problem: @Immutable annotation is dishonest; Compose compiler trusts it, but the mutable backing fields are observable through public setters. Any consumer that hoists a style and calls .position = X directly is relying on that mutability.
Proposed fix:
- Move
position and pointPlacement into the primary constructor as val.
- Keep
setPosition / setPointPlacement but have them return a new instance (they already document "returns a JetLimeEventStyle instance with the updated value").
- Update
JetLimeEventDefaults.eventStyle(...) to pass values through the constructor instead of .apply { this.position = ... }.
Break: source and binary break for any caller that writes style.position = X directly.
2. ItemsList backed by ImmutableList<T>
Current: class ItemsList<T>(val items: List<T>) — the Compose compiler treats List<T> as unstable; @Immutable on the class papers over it.
Proposed fix:
- Change
items property type to kotlinx.collections.immutable.ImmutableList<T>.
- Add a secondary convenience constructor
ItemsList(list: List<T>) that wraps via toImmutableList(), and / or a varargs factory.
Break: source break for anyone reading itemsList.items as a List<T> in a non-covariant context; binary break via getter signature change.
3. EventPointType.equals over Painter
Current: equals / hashCode include icon: Painter?. Painter does not override equals by content — it falls back to referential identity. Two different painter instances with identical content are considered "not equal", defeating stability if the caller doesn't hoist / remember the painter.
Proposed fix (pick one):
- Drop
icon from equals / hashCode and document that callers must remember the painter upstream.
- Or use a small content key (e.g. a user-supplied
id) for equality.
Break: behavior change that could alter recomposition-skipping semantics for existing users.
4. Convert EventPosition to enum class
Current: class EventPosition internal constructor(val name: String) with three singletons in the companion (START private, MIDDLE private, END internal) and a dynamic(index, listSize) factory.
Proposed fix:
enum class EventPosition {
START, MIDDLE, END;
fun isNotEnd() = this != END
fun isNotStart() = this != START
companion object {
fun dynamic(index: Int, listSize: Int): EventPosition = when (index) {
listSize - 1 -> END
0 -> START
else -> MIDDLE
}
}
}
Benefits: real compiler-level stability, removes the fragile string-literal comparisons, simpler equals / hashCode.
Break: widens visibility of START / MIDDLE / END (now public). Class → enum also changes compiled bytecode flags; reflective users may break.
5. Per-item eventStyle(...) should be remembered
Current: the sample and README call JetLimeEventDefaults.eventStyle(position = position) fresh inside every lazy item on every recomposition, allocating a new JetLimeEventStyle. With the stability fixes above, value equality would still let the item skip — but the allocation is wasted.
Proposed fix:
- Update the sample and README snippets to
remember(position) { eventStyle(position) } where appropriate.
- Consider adding an overload or helper in
JetLimeEventDefaults that memoizes by position.
Break: documentation / sample only; no library break.
Tracking
Target: next major version.
🤖 Generated with Claude Code
Collecting follow-ups found during the audit in #66 that were intentionally deferred because they change the public API of the library. These should ship together in the next major version (e.g. 5.0) so consumers only migrate once.
1.
JetLimeEventStyle.position/pointPlacementshould bevalCurrent: both are public
varproperties on an@Immutableclass and are mutated viasetPosition/setPointPlacement/JetLimeEventDefaults.eventStyle(...).apply { ... }.Problem:
@Immutableannotation is dishonest; Compose compiler trusts it, but the mutable backing fields are observable through public setters. Any consumer that hoists a style and calls.position = Xdirectly is relying on that mutability.Proposed fix:
positionandpointPlacementinto the primary constructor asval.setPosition/setPointPlacementbut have them return a new instance (they already document "returns a JetLimeEventStyle instance with the updated value").JetLimeEventDefaults.eventStyle(...)to pass values through the constructor instead of.apply { this.position = ... }.Break: source and binary break for any caller that writes
style.position = Xdirectly.2.
ItemsListbacked byImmutableList<T>Current:
class ItemsList<T>(val items: List<T>)— the Compose compiler treatsList<T>as unstable;@Immutableon the class papers over it.Proposed fix:
itemsproperty type tokotlinx.collections.immutable.ImmutableList<T>.ItemsList(list: List<T>)that wraps viatoImmutableList(), and / or a varargs factory.Break: source break for anyone reading
itemsList.itemsas aList<T>in a non-covariant context; binary break via getter signature change.3.
EventPointType.equalsoverPainterCurrent:
equals/hashCodeincludeicon: Painter?.Painterdoes not overrideequalsby content — it falls back to referential identity. Two different painter instances with identical content are considered "not equal", defeating stability if the caller doesn't hoist /rememberthe painter.Proposed fix (pick one):
iconfromequals/hashCodeand document that callers mustrememberthe painter upstream.id) for equality.Break: behavior change that could alter recomposition-skipping semantics for existing users.
4. Convert
EventPositiontoenum classCurrent:
class EventPosition internal constructor(val name: String)with three singletons in the companion (STARTprivate,MIDDLEprivate,ENDinternal) and adynamic(index, listSize)factory.Proposed fix:
Benefits: real compiler-level stability, removes the fragile string-literal comparisons, simpler
equals/hashCode.Break: widens visibility of
START/MIDDLE/END(now public). Class → enum also changes compiled bytecode flags; reflective users may break.5. Per-item
eventStyle(...)should berememberedCurrent: the sample and README call
JetLimeEventDefaults.eventStyle(position = position)fresh inside every lazy item on every recomposition, allocating a newJetLimeEventStyle. With the stability fixes above, value equality would still let the item skip — but the allocation is wasted.Proposed fix:
remember(position) { eventStyle(position) }where appropriate.JetLimeEventDefaultsthat memoizes by position.Break: documentation / sample only; no library break.
Tracking
JetLimeEventStylefields →valItemsList→ImmutableList<T>EventPointTypeequality semanticsEventPosition→enum classremember(position) { eventStyle(...) }Target: next major version.
🤖 Generated with Claude Code