From 1f15a131c1f48823387ba900628169d7cdd54750 Mon Sep 17 00:00:00 2001 From: paul-at-equal Date: Tue, 18 Aug 2026 17:59:56 +0530 Subject: [PATCH] feat: add ConnectorStyle.TEARDROP and ConnectorEndStyle.TEARDROP Adds a rounded teardrop/speech-bubble-tail shape as a new connector option, requested to match a reference design where the tooltip's tail is a smooth "hill" bump growing out of the card edge rather than a thin line with a dot/arrow. - ConnectorStyle.TEARDROP: a nub anchored directly on the tooltip card edge and pointing toward the target, with no connector line. - ConnectorEndStyle.TEARDROP: the same rounded shape used as a line endpoint decoration, as a smoother alternative to ARROW. - CoachmarkConfig.connectorTeardropLength / connectorTeardropWidth size the shape; connectorTeardropTargetGap controls the gap left between the nub's tip and the target so it doesn't touch it. - CoachmarkColors.connectorTeardropColor lets the shape's color be set independently of connectorColor (e.g. matched to the tooltip card background for a seamless attached-tail look). Also fixes a pre-existing off-by-one: isTooltipBelow used a strict `>` comparison, which flipped to the wrong tooltip side whenever the computed gap made the tooltip's edge land exactly on the target's edge (reachable via the new TEARDROP gap math, but a latent bug for any style). Bumped to 1.0.0-beta16 and updated CHANGELOG. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 12 ++ lumen/api/android/lumen.api | 80 ++++---- lumen/api/jvm/lumen.api | 80 ++++---- lumen/build.gradle.kts | 2 +- .../CoachmarkScrimScreenshotTest.kt | 33 ++++ .../snapshots/scrim_teardropConnector.png | Bin 0 -> 50567 bytes .../snapshots/scrim_teardropEndStyle.png | Bin 0 -> 49351 bytes .../kotlin/io/luminos/CoachmarkColors.kt | 6 + .../kotlin/io/luminos/CoachmarkScrim.kt | 180 +++++++++++++++++- .../kotlin/io/luminos/CoachmarkTarget.kt | 21 ++ .../kotlin/io/luminos/CoachmarkTargetTest.kt | 6 +- .../sample/examples/ConnectorsExample.kt | 23 ++- 12 files changed, 361 insertions(+), 82 deletions(-) create mode 100644 lumen/src/androidUnitTest/snapshots/scrim_teardropConnector.png create mode 100644 lumen/src/androidUnitTest/snapshots/scrim_teardropEndStyle.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d33f43..6348b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [1.0.0-beta16] - 2026-08-18 + +### Features +- New `ConnectorStyle.TEARDROP`: a rounded teardrop nub anchored directly on the tooltip card edge and pointing toward the target — no connector line at all, like a classic speech-bubble tail +- New `ConnectorEndStyle.TEARDROP`: a rounded petal-shaped connector line endpoint, as a smoother alternative to `ARROW` +- `CoachmarkConfig.connectorTeardropLength` / `connectorTeardropWidth` to size the teardrop shape +- `CoachmarkConfig.connectorTeardropTargetGap` to control the gap left between the teardrop nub and the target it points at +- `CoachmarkColors.connectorTeardropColor` to color the teardrop shape independently of `connectorColor` — e.g. match it to the tooltip card background for a seamless attached-tail look + +### Sample App +- Added "Teardrop Connector" and "Teardrop Endpoint" demos to the Connector Styles example + ## [1.0.0-beta15] - 2026-03-18 ### Features diff --git a/lumen/api/android/lumen.api b/lumen/api/android/lumen.api index 69e1a26..dbd0370 100644 --- a/lumen/api/android/lumen.api +++ b/lumen/api/android/lumen.api @@ -32,11 +32,12 @@ public final class io/luminos/CoachmarkAnalytics { public final class io/luminos/CoachmarkColors { public static final field $stable I - public synthetic fun (JJJJJJJJJJJILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (JJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JJJJJJJJJJJJILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-0d7_KjU ()J public final fun component10-0d7_KjU ()J public final fun component11-0d7_KjU ()J + public final fun component12-0d7_KjU ()J public final fun component2-0d7_KjU ()J public final fun component3-0d7_KjU ()J public final fun component4-0d7_KjU ()J @@ -45,10 +46,11 @@ public final class io/luminos/CoachmarkColors { public final fun component7-0d7_KjU ()J public final fun component8-0d7_KjU ()J public final fun component9-0d7_KjU ()J - public final fun copy-KpyCeHw (JJJJJJJJJJJ)Lio/luminos/CoachmarkColors; - public static synthetic fun copy-KpyCeHw$default (Lio/luminos/CoachmarkColors;JJJJJJJJJJJILjava/lang/Object;)Lio/luminos/CoachmarkColors; + public final fun copy-2qZNXz8 (JJJJJJJJJJJJ)Lio/luminos/CoachmarkColors; + public static synthetic fun copy-2qZNXz8$default (Lio/luminos/CoachmarkColors;JJJJJJJJJJJJILjava/lang/Object;)Lio/luminos/CoachmarkColors; public fun equals (Ljava/lang/Object;)Z public final fun getConnectorColor-0d7_KjU ()J + public final fun getConnectorTeardropColor-0d7_KjU ()J public final fun getCtaButtonColor-0d7_KjU ()J public final fun getCtaTextColor-0d7_KjU ()J public final fun getDescriptionColor-0d7_KjU ()J @@ -72,48 +74,51 @@ public final class io/luminos/CoachmarkColorsKt { public final class io/luminos/CoachmarkConfig { public static final field $stable I - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-D9Ej5fM ()F - public final fun component10 ()I - public final fun component11 ()I - public final fun component12 ()I - public final fun component13 ()Lio/luminos/ScrimOpacity; - public final fun component14 ()Lio/luminos/ScrimTapBehavior; - public final fun component15 ()Z - public final fun component16 ()Z - public final fun component17 ()Lio/luminos/BackPressBehavior; - public final fun component18 ()Lio/luminos/HighlightAnimation; - public final fun component19 ()I + public final fun component10-D9Ej5fM ()F + public final fun component11-D9Ej5fM ()F + public final fun component12-D9Ej5fM ()F + public final fun component13 ()I + public final fun component14 ()I + public final fun component15 ()I + public final fun component16 ()Lio/luminos/ScrimOpacity; + public final fun component17 ()Lio/luminos/ScrimTapBehavior; + public final fun component18 ()Z + public final fun component19 ()Z public final fun component2-D9Ej5fM ()F - public final fun component20 ()Z - public final fun component21 ()Ljava/lang/String; - public final fun component22 ()J + public final fun component20 ()Lio/luminos/BackPressBehavior; + public final fun component21 ()Lio/luminos/HighlightAnimation; + public final fun component22 ()I public final fun component23 ()Z - public final fun component24 ()J - public final fun component25 ()Ljava/lang/String; - public final fun component26 ()J - public final fun component27 ()Z - public final fun component28-D9Ej5fM ()F - public final fun component29-D9Ej5fM ()F + public final fun component24 ()Ljava/lang/String; + public final fun component25 ()J + public final fun component26 ()Z + public final fun component27 ()J + public final fun component28 ()Ljava/lang/String; + public final fun component29 ()J public final fun component3-D9Ej5fM ()F - public final fun component30-D9Ej5fM ()F + public final fun component30 ()Z public final fun component31-D9Ej5fM ()F - public final fun component32-e0LSkKk ()I - public final fun component33-e0LSkKk ()I - public final fun component34-e0LSkKk ()I + public final fun component32-D9Ej5fM ()F + public final fun component33-D9Ej5fM ()F + public final fun component34-D9Ej5fM ()F public final fun component35-e0LSkKk ()I public final fun component36-e0LSkKk ()I - public final fun component37 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; - public final fun component38 ()Z + public final fun component37-e0LSkKk ()I + public final fun component38-e0LSkKk ()I + public final fun component39-e0LSkKk ()I public final fun component4 ()F - public final fun component5 ()Lkotlin/jvm/functions/Function3; + public final fun component40 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; + public final fun component41 ()Z + public final fun component5-D9Ej5fM ()F public final fun component6-D9Ej5fM ()F public final fun component7-D9Ej5fM ()F - public final fun component8-D9Ej5fM ()F + public final fun component8 ()Lkotlin/jvm/functions/Function3; public final fun component9-D9Ej5fM ()F - public final fun copy-ZvfVXdg (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; - public static synthetic fun copy-ZvfVXdg$default (Lio/luminos/CoachmarkConfig;FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; + public final fun copy-I2SBm6M (FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; + public static synthetic fun copy-I2SBm6M$default (Lio/luminos/CoachmarkConfig;FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; public fun equals (Ljava/lang/Object;)Z public final fun getBackPressBehavior ()Lio/luminos/BackPressBehavior; public final fun getConnectorAnimationDuration ()I @@ -121,6 +126,9 @@ public final class io/luminos/CoachmarkConfig { public final fun getConnectorArrowSize-D9Ej5fM ()F public final fun getConnectorCutoutGap-D9Ej5fM ()F public final fun getConnectorDotRadius-D9Ej5fM ()F + public final fun getConnectorTeardropLength-D9Ej5fM ()F + public final fun getConnectorTeardropTargetGap-D9Ej5fM ()F + public final fun getConnectorTeardropWidth-D9Ej5fM ()F public final fun getConnectorTooltipGap-D9Ej5fM ()F public final fun getCtaCornerRadius-D9Ej5fM ()F public final fun getCtaHorizontalArrangement ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; @@ -331,6 +339,7 @@ public final class io/luminos/ConnectorEndStyle : java/lang/Enum { public static final field CUSTOM Lio/luminos/ConnectorEndStyle; public static final field DOT Lio/luminos/ConnectorEndStyle; public static final field NONE Lio/luminos/ConnectorEndStyle; + public static final field TEARDROP Lio/luminos/ConnectorEndStyle; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/luminos/ConnectorEndStyle; public static fun values ()[Lio/luminos/ConnectorEndStyle; @@ -342,6 +351,7 @@ public final class io/luminos/ConnectorStyle : java/lang/Enum { public static final field DIRECT Lio/luminos/ConnectorStyle; public static final field ELBOW Lio/luminos/ConnectorStyle; public static final field HORIZONTAL Lio/luminos/ConnectorStyle; + public static final field TEARDROP Lio/luminos/ConnectorStyle; public static final field VERTICAL Lio/luminos/ConnectorStyle; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/luminos/ConnectorStyle; diff --git a/lumen/api/jvm/lumen.api b/lumen/api/jvm/lumen.api index f7942da..be59e88 100644 --- a/lumen/api/jvm/lumen.api +++ b/lumen/api/jvm/lumen.api @@ -28,11 +28,12 @@ public final class io/luminos/CoachmarkAnalytics { public final class io/luminos/CoachmarkColors { public static final field $stable I - public synthetic fun (JJJJJJJJJJJILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (JJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JJJJJJJJJJJJILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-0d7_KjU ()J public final fun component10-0d7_KjU ()J public final fun component11-0d7_KjU ()J + public final fun component12-0d7_KjU ()J public final fun component2-0d7_KjU ()J public final fun component3-0d7_KjU ()J public final fun component4-0d7_KjU ()J @@ -41,10 +42,11 @@ public final class io/luminos/CoachmarkColors { public final fun component7-0d7_KjU ()J public final fun component8-0d7_KjU ()J public final fun component9-0d7_KjU ()J - public final fun copy-KpyCeHw (JJJJJJJJJJJ)Lio/luminos/CoachmarkColors; - public static synthetic fun copy-KpyCeHw$default (Lio/luminos/CoachmarkColors;JJJJJJJJJJJILjava/lang/Object;)Lio/luminos/CoachmarkColors; + public final fun copy-2qZNXz8 (JJJJJJJJJJJJ)Lio/luminos/CoachmarkColors; + public static synthetic fun copy-2qZNXz8$default (Lio/luminos/CoachmarkColors;JJJJJJJJJJJJILjava/lang/Object;)Lio/luminos/CoachmarkColors; public fun equals (Ljava/lang/Object;)Z public final fun getConnectorColor-0d7_KjU ()J + public final fun getConnectorTeardropColor-0d7_KjU ()J public final fun getCtaButtonColor-0d7_KjU ()J public final fun getCtaTextColor-0d7_KjU ()J public final fun getDescriptionColor-0d7_KjU ()J @@ -68,48 +70,51 @@ public final class io/luminos/CoachmarkColorsKt { public final class io/luminos/CoachmarkConfig { public static final field $stable I - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1-D9Ej5fM ()F - public final fun component10 ()I - public final fun component11 ()I - public final fun component12 ()I - public final fun component13 ()Lio/luminos/ScrimOpacity; - public final fun component14 ()Lio/luminos/ScrimTapBehavior; - public final fun component15 ()Z - public final fun component16 ()Z - public final fun component17 ()Lio/luminos/BackPressBehavior; - public final fun component18 ()Lio/luminos/HighlightAnimation; - public final fun component19 ()I + public final fun component10-D9Ej5fM ()F + public final fun component11-D9Ej5fM ()F + public final fun component12-D9Ej5fM ()F + public final fun component13 ()I + public final fun component14 ()I + public final fun component15 ()I + public final fun component16 ()Lio/luminos/ScrimOpacity; + public final fun component17 ()Lio/luminos/ScrimTapBehavior; + public final fun component18 ()Z + public final fun component19 ()Z public final fun component2-D9Ej5fM ()F - public final fun component20 ()Z - public final fun component21 ()Ljava/lang/String; - public final fun component22 ()J + public final fun component20 ()Lio/luminos/BackPressBehavior; + public final fun component21 ()Lio/luminos/HighlightAnimation; + public final fun component22 ()I public final fun component23 ()Z - public final fun component24 ()J - public final fun component25 ()Ljava/lang/String; - public final fun component26 ()J - public final fun component27 ()Z - public final fun component28-D9Ej5fM ()F - public final fun component29-D9Ej5fM ()F + public final fun component24 ()Ljava/lang/String; + public final fun component25 ()J + public final fun component26 ()Z + public final fun component27 ()J + public final fun component28 ()Ljava/lang/String; + public final fun component29 ()J public final fun component3-D9Ej5fM ()F - public final fun component30-D9Ej5fM ()F + public final fun component30 ()Z public final fun component31-D9Ej5fM ()F - public final fun component32-e0LSkKk ()I - public final fun component33-e0LSkKk ()I - public final fun component34-e0LSkKk ()I + public final fun component32-D9Ej5fM ()F + public final fun component33-D9Ej5fM ()F + public final fun component34-D9Ej5fM ()F public final fun component35-e0LSkKk ()I public final fun component36-e0LSkKk ()I - public final fun component37 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; - public final fun component38 ()Z + public final fun component37-e0LSkKk ()I + public final fun component38-e0LSkKk ()I + public final fun component39-e0LSkKk ()I public final fun component4 ()F - public final fun component5 ()Lkotlin/jvm/functions/Function3; + public final fun component40 ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; + public final fun component41 ()Z + public final fun component5-D9Ej5fM ()F public final fun component6-D9Ej5fM ()F public final fun component7-D9Ej5fM ()F - public final fun component8-D9Ej5fM ()F + public final fun component8 ()Lkotlin/jvm/functions/Function3; public final fun component9-D9Ej5fM ()F - public final fun copy-ZvfVXdg (FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; - public static synthetic fun copy-ZvfVXdg$default (Lio/luminos/CoachmarkConfig;FFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; + public final fun copy-I2SBm6M (FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;Z)Lio/luminos/CoachmarkConfig; + public static synthetic fun copy-I2SBm6M$default (Lio/luminos/CoachmarkConfig;FFFFFFFLkotlin/jvm/functions/Function3;FFFFIIILio/luminos/ScrimOpacity;Lio/luminos/ScrimTapBehavior;ZZLio/luminos/BackPressBehavior;Lio/luminos/HighlightAnimation;IZLjava/lang/String;JZJLjava/lang/String;JZFFFFIIIIILandroidx/compose/foundation/layout/Arrangement$Horizontal;ZIILjava/lang/Object;)Lio/luminos/CoachmarkConfig; public fun equals (Ljava/lang/Object;)Z public final fun getBackPressBehavior ()Lio/luminos/BackPressBehavior; public final fun getConnectorAnimationDuration ()I @@ -117,6 +122,9 @@ public final class io/luminos/CoachmarkConfig { public final fun getConnectorArrowSize-D9Ej5fM ()F public final fun getConnectorCutoutGap-D9Ej5fM ()F public final fun getConnectorDotRadius-D9Ej5fM ()F + public final fun getConnectorTeardropLength-D9Ej5fM ()F + public final fun getConnectorTeardropTargetGap-D9Ej5fM ()F + public final fun getConnectorTeardropWidth-D9Ej5fM ()F public final fun getConnectorTooltipGap-D9Ej5fM ()F public final fun getCtaCornerRadius-D9Ej5fM ()F public final fun getCtaHorizontalArrangement ()Landroidx/compose/foundation/layout/Arrangement$Horizontal; @@ -327,6 +335,7 @@ public final class io/luminos/ConnectorEndStyle : java/lang/Enum { public static final field CUSTOM Lio/luminos/ConnectorEndStyle; public static final field DOT Lio/luminos/ConnectorEndStyle; public static final field NONE Lio/luminos/ConnectorEndStyle; + public static final field TEARDROP Lio/luminos/ConnectorEndStyle; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/luminos/ConnectorEndStyle; public static fun values ()[Lio/luminos/ConnectorEndStyle; @@ -338,6 +347,7 @@ public final class io/luminos/ConnectorStyle : java/lang/Enum { public static final field DIRECT Lio/luminos/ConnectorStyle; public static final field ELBOW Lio/luminos/ConnectorStyle; public static final field HORIZONTAL Lio/luminos/ConnectorStyle; + public static final field TEARDROP Lio/luminos/ConnectorStyle; public static final field VERTICAL Lio/luminos/ConnectorStyle; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/luminos/ConnectorStyle; diff --git a/lumen/build.gradle.kts b/lumen/build.gradle.kts index 3edea9a..a11daa5 100644 --- a/lumen/build.gradle.kts +++ b/lumen/build.gradle.kts @@ -106,7 +106,7 @@ android { } } -val libraryVersion = "1.0.0-beta15" +val libraryVersion = "1.0.0-beta16" val libraryGroup = "io.github.aldefy" val libraryArtifact = "lumen" diff --git a/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkScrimScreenshotTest.kt b/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkScrimScreenshotTest.kt index 705c30e..841ddc7 100644 --- a/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkScrimScreenshotTest.kt +++ b/lumen/src/androidUnitTest/kotlin/io/luminos/screenshot/CoachmarkScrimScreenshotTest.kt @@ -12,6 +12,7 @@ import io.luminos.CoachmarkConfig import io.luminos.CoachmarkController import io.luminos.CoachmarkScrim import io.luminos.CoachmarkTarget +import io.luminos.ConnectorEndStyle import io.luminos.ConnectorStyle import io.luminos.CutoutShape import io.luminos.DarkCoachmarkColors @@ -184,6 +185,38 @@ class CoachmarkScrimScreenshotTest { ) } + @Test + fun scrim_teardropConnector() { + showScrimAndCapture( + target = CoachmarkTarget( + id = "teardrop", + bounds = targetBounds, + shape = CutoutShape.Circle(), + title = "Teardrop Connector", + description = "A rounded teardrop nub anchored on the tooltip edge, no connector line.", + connectorStyle = ConnectorStyle.TEARDROP, + ), + config = noAnimConfig.copy(showTooltipCard = true), + filePath = "src/androidUnitTest/snapshots/scrim_teardropConnector.png", + ) + } + + @Test + fun scrim_teardropEndStyle() { + showScrimAndCapture( + target = CoachmarkTarget( + id = "teardropEnd", + bounds = targetBounds, + shape = CutoutShape.Circle(), + title = "Teardrop Endpoint", + description = "A rounded petal shape at the end of a direct connector line.", + connectorStyle = ConnectorStyle.DIRECT, + connectorEndStyle = ConnectorEndStyle.TEARDROP, + ), + filePath = "src/androidUnitTest/snapshots/scrim_teardropEndStyle.png", + ) + } + @Test fun scrim_tooltipAbove() { val lowerTarget = Rect(left = 150f, top = 600f, right = 250f, bottom = 660f) diff --git a/lumen/src/androidUnitTest/snapshots/scrim_teardropConnector.png b/lumen/src/androidUnitTest/snapshots/scrim_teardropConnector.png new file mode 100644 index 0000000000000000000000000000000000000000..d1fb17edc1bb04bcdfe0cdad1e215fda3ef5adcb GIT binary patch literal 50567 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

&vETm>-&h$oLy|#{BAS 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 Unit)? = null, /** Minimum distance from screen edges for the tooltip */ @@ -547,9 +563,12 @@ private fun CoachmarkScrimContent( } else { 0f } + val teardropLengthPx = with(density) { config.connectorTeardropLength.toPx() } + val teardropWidthPx = with(density) { config.connectorTeardropWidth.toPx() } + val teardropTargetGapPx = with(density) { config.connectorTeardropTargetGap.toPx() } val tooltipPosition = - remember(target, screenSize, tooltipSize, connectorLengthPx) { + remember(target, screenSize, tooltipSize, connectorLengthPx, teardropLengthPx, teardropTargetGapPx) { calculateTooltipPosition( targetBounds = target.bounds, tooltipSize = tooltipSize, @@ -559,6 +578,8 @@ private fun CoachmarkScrimContent( gap = with(density) { config.tooltipGap.toPx() }, connectorLength = connectorLengthPx, connectorStyle = target.connectorStyle, + teardropLength = teardropLengthPx, + teardropTargetGap = teardropTargetGapPx, ) } @@ -575,7 +596,7 @@ private fun CoachmarkScrimContent( // When below: composed dot renders at the top of the tooltip (beside the title). // When above: composed dot renders at the bottom of the tooltip. val isTooltipBelow = if (tooltipSize != IntSize.Zero) { - tooltipPosition.y > target.bounds.bottom + tooltipPosition.y >= target.bounds.bottom } else { true } @@ -596,7 +617,7 @@ private fun CoachmarkScrimContent( (resolvedConnectorStyle == ConnectorStyle.VERTICAL || resolvedConnectorStyle == ConnectorStyle.ELBOW) val connectorPathData = - remember(target, tooltipPosition, tooltipSize, density, connectorTooltipGapPx, strokeWidthPx, connectorDotRadiusPx, connectorCutoutGapPx, isInlineTitleActive, inlineDotCenter) { + remember(target, tooltipPosition, tooltipSize, density, connectorTooltipGapPx, strokeWidthPx, connectorDotRadiusPx, connectorCutoutGapPx, isInlineTitleActive, inlineDotCenter, teardropLengthPx) { calculateConnectorPath( target = target, tooltipPosition = tooltipPosition, @@ -608,6 +629,8 @@ private fun CoachmarkScrimContent( connectorCutoutGap = connectorCutoutGapPx, isInlineTitleActive = isInlineTitleActive, inlineDotCenter = inlineDotCenter, + teardropLength = teardropLengthPx, + tooltipMargin = tooltipMarginPx, ) } @@ -878,6 +901,11 @@ private fun CoachmarkScrimContent( val arrowSizePx = with(density) { config.connectorArrowSize.toPx() } val arrowHalfAngleRad = (config.connectorArrowAngle * PI / 180f).toFloat() val effectiveEndStyle = if (isInlineTitleActive) ConnectorEndStyle.NONE else target.connectorEndStyle + val teardropColor = if (colors.connectorTeardropColor.isSpecified) { + colors.connectorTeardropColor + } else { + colors.connectorColor + } when (connectorPathData) { is ConnectorPathData.Segments -> { if (connectorPathData.points.isNotEmpty()) { @@ -891,6 +919,9 @@ private fun CoachmarkScrimContent( arrowSize = arrowSizePx, arrowHalfAngle = arrowHalfAngleRad, customEnd = config.customConnectorEnd, + teardropLength = teardropLengthPx, + teardropWidth = teardropWidthPx, + teardropColor = teardropColor, ) } } @@ -905,6 +936,17 @@ private fun CoachmarkScrimContent( arrowSize = arrowSizePx, arrowHalfAngle = arrowHalfAngleRad, customEnd = config.customConnectorEnd, + teardropLength = teardropLengthPx, + teardropWidth = teardropWidthPx, + teardropColor = teardropColor, + ) + } + is ConnectorPathData.Teardrop -> { + drawConnectorTeardropNub( + teardrop = connectorPathData, + progress = connectorProgress.value, + color = teardropColor, + width = teardropWidthPx, ) } } @@ -1268,6 +1310,9 @@ private fun DrawScope.drawConnectorPath( arrowSize: Float = 0f, arrowHalfAngle: Float = 0f, customEnd: (DrawScope.(Offset, Float) -> Unit)? = null, + teardropLength: Float = 0f, + teardropWidth: Float = 0f, + teardropColor: Color = Color.Unspecified, ) { if (points.size < 2) return @@ -1308,7 +1353,7 @@ private fun DrawScope.drawConnectorPath( if (progress >= 1f) { val angle = calculateConnectorAngle(points) - drawConnectorEndpoint(endStyle, color, dotRadius, points.last(), angle, arrowSize, arrowHalfAngle, customEnd) + drawConnectorEndpoint(endStyle, color, dotRadius, points.last(), angle, arrowSize, arrowHalfAngle, customEnd, teardropLength, teardropWidth, teardropColor) } } @@ -1322,6 +1367,9 @@ private fun DrawScope.drawConnectorEndpoint( arrowSize: Float, arrowHalfAngle: Float, customEnd: (DrawScope.(Offset, Float) -> Unit)?, + teardropLength: Float = 0f, + teardropWidth: Float = 0f, + teardropColor: Color = Color.Unspecified, ) { when (endStyle) { ConnectorEndStyle.DOT -> { @@ -1334,6 +1382,11 @@ private fun DrawScope.drawConnectorEndpoint( ConnectorEndStyle.CUSTOM -> { customEnd?.invoke(this, center, angle) } + ConnectorEndStyle.TEARDROP -> { + val base = Offset(center.x - teardropLength * cos(angle), center.y - teardropLength * sin(angle)) + val resolvedColor = if (teardropColor.isSpecified) teardropColor else color + drawTeardrop(tip = center, base = base, width = teardropWidth, color = resolvedColor) + } } } @@ -1363,6 +1416,51 @@ private fun DrawScope.drawArrowHead( drawPath(path = path, color = color) } +/** + * Builds a smooth teardrop "hill" outline: a flat base of the given [width] centered on [base], + * rising via two mirrored S-curves to a single rounded peak at [tip]. Used for + * [ConnectorEndStyle.TEARDROP] and the [ConnectorStyle.TEARDROP] nub. The flat base is meant to + * sit flush against (and be partly hidden behind) the tooltip card or connector line it grows + * out of, so only the rounded hill silhouette reads as visible. + */ +private fun teardropPath(tip: Offset, base: Offset, width: Float): Path { + val dirX = tip.x - base.x + val dirY = tip.y - base.y + val length = kotlin.math.sqrt(dirX * dirX + dirY * dirY) + if (length < 0.01f) return Path() + + val ux = dirX / length + val uy = dirY / length + val perpX = -uy + val perpY = ux + val halfWidth = width / 2f + + val baseLeft = Offset(base.x - perpX * halfWidth, base.y - perpY * halfWidth) + val baseRight = Offset(base.x + perpX * halfWidth, base.y + perpY * halfWidth) + + // Control points a quarter-width in from each base corner, at that corner's own height + // (flat tangent at the base) or at the peak's height (flat tangent at the peak). + val quarterWidth = halfWidth / 2f + val leftNearBase = Offset(baseLeft.x + perpX * quarterWidth, baseLeft.y + perpY * quarterWidth) + val leftNearPeak = Offset(tip.x - perpX * quarterWidth, tip.y - perpY * quarterWidth) + val rightNearPeak = Offset(tip.x + perpX * quarterWidth, tip.y + perpY * quarterWidth) + val rightNearBase = Offset(baseRight.x - perpX * quarterWidth, baseRight.y - perpY * quarterWidth) + + return Path().apply { + moveTo(baseLeft.x, baseLeft.y) + cubicTo(leftNearBase.x, leftNearBase.y, leftNearPeak.x, leftNearPeak.y, tip.x, tip.y) + cubicTo(rightNearPeak.x, rightNearPeak.y, rightNearBase.x, rightNearBase.y, baseRight.x, baseRight.y) + lineTo(baseLeft.x, baseLeft.y) + close() + } +} + +/** Draws a smooth teardrop "hill" shape from [base] (flat, hidden) to [tip] (rounded peak). */ +private fun DrawScope.drawTeardrop(tip: Offset, base: Offset, width: Float, color: Color) { + val path = teardropPath(tip, base, width) + drawPath(path = path, color = color) +} + /** Draws a Bezier curve connector with progressive animation. */ private fun DrawScope.drawConnectorCurve( curve: ConnectorPathData.Curve, @@ -1374,6 +1472,9 @@ private fun DrawScope.drawConnectorCurve( arrowSize: Float = 0f, arrowHalfAngle: Float = 0f, customEnd: (DrawScope.(Offset, Float) -> Unit)? = null, + teardropLength: Float = 0f, + teardropWidth: Float = 0f, + teardropColor: Color = Color.Unspecified, ) { val steps = 50 val totalSteps = (steps * progress).toInt().coerceAtLeast(1) @@ -1408,10 +1509,24 @@ private fun DrawScope.drawConnectorCurve( points.last().y - points[points.size - 2].y, points.last().x - points[points.size - 2].x, ) - drawConnectorEndpoint(endStyle, color, dotRadius, points.last(), angle, arrowSize, arrowHalfAngle, customEnd) + drawConnectorEndpoint(endStyle, color, dotRadius, points.last(), angle, arrowSize, arrowHalfAngle, customEnd, teardropLength, teardropWidth, teardropColor) } } +/** Draws the [ConnectorStyle.TEARDROP] nub, growing from [ConnectorPathData.Teardrop.base] to its tip as [progress] advances. */ +private fun DrawScope.drawConnectorTeardropNub( + teardrop: ConnectorPathData.Teardrop, + progress: Float, + color: Color, + width: Float, +) { + val animatedTip = Offset( + x = teardrop.base.x + (teardrop.tip.x - teardrop.base.x) * progress, + y = teardrop.base.y + (teardrop.tip.y - teardrop.base.y) * progress, + ) + drawTeardrop(tip = animatedTip, base = teardrop.base, width = width, color = color) +} + private fun calculateTooltipPosition( targetBounds: Rect, tooltipSize: IntSize, @@ -1421,6 +1536,8 @@ private fun calculateTooltipPosition( gap: Float, connectorLength: Float, connectorStyle: ConnectorStyle = ConnectorStyle.AUTO, + teardropLength: Float = 0f, + teardropTargetGap: Float = 0f, ): Offset { if (screenSize == IntSize.Zero) { return Offset.Zero @@ -1430,8 +1547,19 @@ private fun calculateTooltipPosition( val targetCenterX = targetBounds.center.x val targetCenterY = targetBounds.center.y - // Use connectorLength if specified, otherwise use the default gap - val effectiveGap = if (connectorLength > 0f) connectorLength else gap + // TEARDROP replaces the connector line with a nub that spans from the visible card edge + // toward the target, leaving teardropTargetGap of clearance before the target — so the + // tooltip sits just far enough away for the nub plus that gap to bridge the distance. + // connectorLength is ignored. The visible card sits `margin` inside the measured tooltip + // box (TooltipContainer pads its content by tooltipMargin), so that inset is subtracted + // back out here to keep the total cutout-to-card distance equal to + // teardropLength + teardropTargetGap. + val effectiveGap = when { + connectorStyle == ConnectorStyle.TEARDROP -> + (teardropLength + teardropTargetGap - margin).coerceAtLeast(0f) + connectorLength > 0f -> connectorLength + else -> gap + } // For HORIZONTAL connector style, position tooltip beside the target if (connectorStyle == ConnectorStyle.HORIZONTAL) { @@ -1476,6 +1604,9 @@ private fun calculateTooltipPosition( private sealed interface ConnectorPathData { data class Segments(val points: List) : ConnectorPathData data class Curve(val start: Offset, val control: Offset, val end: Offset) : ConnectorPathData + + /** [base] anchors on the tooltip edge (partially hidden behind the card); [tip] points at the target. */ + data class Teardrop(val base: Offset, val tip: Offset) : ConnectorPathData } private fun resolveConnectorStyle( @@ -1508,6 +1639,8 @@ private fun calculateConnectorPath( connectorCutoutGap: Float = 0f, isInlineTitleActive: Boolean = false, inlineDotCenter: Offset = Offset.Unspecified, + teardropLength: Float = 0f, + tooltipMargin: Float = 0f, ): ConnectorPathData { if (tooltipSize == IntSize.Zero) { return ConnectorPathData.Segments(emptyList()) @@ -1562,7 +1695,7 @@ private fun calculateConnectorPath( val tooltipCenterX = tooltipPosition.x + tooltipSize.width / 2f val tooltipCenterY = tooltipPosition.y + tooltipSize.height / 2f - val isTooltipBelow = tooltipPosition.y > targetBounds.bottom + val isTooltipBelow = tooltipPosition.y >= targetBounds.bottom val resolvedStyle = resolveConnectorStyle( connectorStyle = connectorStyle, @@ -1703,6 +1836,37 @@ private fun calculateConnectorPath( val controlPoint = Offset(targetCenter.x, (cutoutEdgePoint.y + endPoint.y) / 2f) ConnectorPathData.Curve(cutoutEdgePoint, controlPoint, endPoint) } + + ConnectorStyle.TEARDROP -> { + // Anchor on the *visible* card's top/bottom edge — the measured tooltip box is + // padded by tooltipMargin on all sides (TooltipContainer), so the actual card sits + // inset from tooltipPosition/tooltipSize by that amount. Clamped away from the + // card's rounded corners. + val cardLeft = tooltipPosition.x + tooltipMargin + val cardRight = tooltipPosition.x + tooltipSize.width - tooltipMargin + val anchorX = targetCenter.x.coerceIn( + cardLeft + 20f, + (cardRight - 20f).coerceAtLeast(cardLeft + 20f), + ) + val anchorY = if (isTooltipBelow) { + tooltipPosition.y + tooltipMargin + } else { + tooltipPosition.y + tooltipSize.height - tooltipMargin + } + val base = Offset(anchorX, anchorY) + + val dx = targetCenter.x - base.x + val dy = targetCenter.y - base.y + val distance = kotlin.math.sqrt(dx * dx + dy * dy) + val (normalizedDx, normalizedDy) = if (distance < 1f) { + 0f to (if (isTooltipBelow) -1f else 1f) + } else { + (dx / distance) to (dy / distance) + } + + val tip = Offset(base.x + normalizedDx * teardropLength, base.y + normalizedDy * teardropLength) + ConnectorPathData.Teardrop(base = base, tip = tip) + } } } diff --git a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt index f0992e9..12e2a99 100644 --- a/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt +++ b/lumen/src/commonMain/kotlin/io/luminos/CoachmarkTarget.kt @@ -177,6 +177,21 @@ enum class ConnectorStyle { /** Smooth curved connector using a quadratic Bezier curve. */ CURVED, + + /** + * Rounded teardrop-shaped nub anchored directly on the tooltip edge and pointing + * toward the target, like a classic speech-bubble tail — no connecting line is drawn. + * + * [CoachmarkTarget.connectorLength] is ignored for this style; use + * [CoachmarkConfig.connectorTeardropLength] and [CoachmarkConfig.connectorTeardropWidth] + * to size the nub, and [CoachmarkConfig.connectorTeardropTargetGap] to control the gap + * left between the nub's tip and the target. [CoachmarkTarget.connectorEndStyle] is also + * ignored, since the nub itself is both the connector and its endpoint. + * + * Tip: set [CoachmarkColors.connectorTeardropColor] to match your tooltip card background + * for a seamless attached-tail look, without affecting other connectors' color. + */ + TEARDROP, } /** @@ -205,6 +220,12 @@ enum class ConnectorEndStyle { NONE, /** Custom rendering via CoachmarkConfig.customConnectorEnd lambda. */ CUSTOM, + /** + * Rounded teardrop/petal shape pointing toward the tooltip, with smooth curved + * sides instead of the sharp edges of [ARROW]. Sized via + * [CoachmarkConfig.connectorTeardropLength] and [CoachmarkConfig.connectorTeardropWidth]. + */ + TEARDROP, } /** diff --git a/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt b/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt index f598093..b21fa98 100644 --- a/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt +++ b/lumen/src/commonTest/kotlin/io/luminos/CoachmarkTargetTest.kt @@ -153,13 +153,14 @@ class CoachmarkTargetTest { @Test fun connectorStyle_values() { val values = ConnectorStyle.entries - assertEquals(6, values.size) + assertEquals(7, values.size) assertTrue(values.contains(ConnectorStyle.AUTO)) assertTrue(values.contains(ConnectorStyle.DIRECT)) assertTrue(values.contains(ConnectorStyle.HORIZONTAL)) assertTrue(values.contains(ConnectorStyle.VERTICAL)) assertTrue(values.contains(ConnectorStyle.ELBOW)) assertTrue(values.contains(ConnectorStyle.CURVED)) + assertTrue(values.contains(ConnectorStyle.TEARDROP)) } @Test @@ -171,11 +172,12 @@ class CoachmarkTargetTest { @Test fun connectorEndStyle_values() { val values = ConnectorEndStyle.entries - assertEquals(4, values.size) + assertEquals(5, values.size) assertTrue(values.contains(ConnectorEndStyle.DOT)) assertTrue(values.contains(ConnectorEndStyle.ARROW)) assertTrue(values.contains(ConnectorEndStyle.NONE)) assertTrue(values.contains(ConnectorEndStyle.CUSTOM)) + assertTrue(values.contains(ConnectorEndStyle.TEARDROP)) } // ── TargetTapBehavior ────────────────────────────────────────────────── diff --git a/sample/src/commonMain/kotlin/com/aditlal/sample/examples/ConnectorsExample.kt b/sample/src/commonMain/kotlin/com/aditlal/sample/examples/ConnectorsExample.kt index 27d8ecd..97e34a5 100644 --- a/sample/src/commonMain/kotlin/com/aditlal/sample/examples/ConnectorsExample.kt +++ b/sample/src/commonMain/kotlin/com/aditlal/sample/examples/ConnectorsExample.kt @@ -69,6 +69,9 @@ fun ConnectorsExample( showSkipButton = true, skipButtonText = "Skip", scrimTapBehavior = ScrimTapBehavior.NONE, + // TEARDROP connector style anchors its nub directly on the tooltip card edge, + // so it reads best with the card background enabled. + showTooltipCard = true, customConnectorEnd = { center, angle -> // Diamond shape endpoint val size = 10f @@ -134,7 +137,7 @@ fun ConnectorsExample( Spacer(modifier = Modifier.height(8.dp)) Text( - text = "Eight different ways to connect tooltips to targets", + text = "Ten different ways to connect tooltips to targets", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) @@ -289,6 +292,24 @@ fun ConnectorsExample( connectorLength = 90.dp, ctaText = "Got it!", ), + CoachmarkTarget( + id = "vertical", + title = "Teardrop Connector", + description = "A rounded teardrop nub grows straight out of the tooltip card toward the target — no connector line at all, like a classic speech-bubble tail.", + shape = CutoutShape.Circle(radiusPadding = 10.dp), + connectorStyle = ConnectorStyle.TEARDROP, + ctaText = "Next", + ), + CoachmarkTarget( + id = "direct", + title = "Teardrop Endpoint", + description = "Same idea, applied to the connector line's endpoint instead: a rounded petal shape with smooth curved sides, in place of the sharp arrowhead.", + shape = CutoutShape.Circle(radiusPadding = 10.dp), + connectorStyle = ConnectorStyle.DIRECT, + connectorEndStyle = ConnectorEndStyle.TEARDROP, + connectorLength = 80.dp, + ctaText = "Got it!", + ), ) ) showSequence = false