voltra 1.2.0 → 1.3.0-rc.1

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.
Files changed (235) hide show
  1. package/README.md +11 -7
  2. package/android/build.gradle +33 -0
  3. package/android/src/main/java/voltra/VoltraModule.kt +44 -0
  4. package/android/src/main/java/voltra/VoltraRN.kt +41 -32
  5. package/android/src/main/java/voltra/generated/ShortNames.kt +7 -0
  6. package/android/src/main/java/voltra/glance/renderers/ChartBitmapRenderer.kt +827 -0
  7. package/android/src/main/java/voltra/glance/renderers/ChartRenderers.kt +155 -0
  8. package/android/src/main/java/voltra/glance/renderers/RenderCommon.kt +5 -2
  9. package/android/src/main/java/voltra/glance/renderers/TextAndImageRenderers.kt +51 -6
  10. package/android/src/main/java/voltra/glance/renderers/TextBitmapRenderer.kt +134 -0
  11. package/android/src/main/java/voltra/models/parameters/AndroidChartParameters.kt +30 -0
  12. package/android/src/main/java/voltra/payload/ComponentTypeID.kt +2 -0
  13. package/android/src/main/java/voltra/styling/StyleConverter.kt +6 -0
  14. package/android/src/main/java/voltra/styling/StyleModifiers.kt +23 -0
  15. package/android/src/main/java/voltra/styling/StyleStructures.kt +1 -0
  16. package/android/src/main/java/voltra/widget/VoltraCryptoManager.kt +99 -0
  17. package/android/src/main/java/voltra/widget/VoltraWidgetCredentialStore.kt +174 -0
  18. package/android/src/main/java/voltra/widget/VoltraWidgetManager.kt +32 -5
  19. package/android/src/main/java/voltra/widget/VoltraWidgetUpdateScheduler.kt +233 -0
  20. package/android/src/main/java/voltra/widget/VoltraWidgetUpdateWorker.kt +189 -0
  21. package/build/VoltraModule.d.ts +15 -1
  22. package/build/VoltraModule.d.ts.map +1 -1
  23. package/build/VoltraModule.js.map +1 -1
  24. package/build/android/client.d.ts +1 -0
  25. package/build/android/client.d.ts.map +1 -1
  26. package/build/android/client.js +2 -0
  27. package/build/android/client.js.map +1 -1
  28. package/build/android/jsx/AreaMark.d.ts +14 -0
  29. package/build/android/jsx/AreaMark.d.ts.map +1 -0
  30. package/build/android/jsx/AreaMark.js +6 -0
  31. package/build/android/jsx/AreaMark.js.map +1 -0
  32. package/build/android/jsx/BarMark.d.ts +16 -0
  33. package/build/android/jsx/BarMark.d.ts.map +1 -0
  34. package/build/android/jsx/BarMark.js +7 -0
  35. package/build/android/jsx/BarMark.js.map +1 -0
  36. package/build/android/jsx/Chart.d.ts +16 -0
  37. package/build/android/jsx/Chart.d.ts.map +1 -0
  38. package/build/android/jsx/Chart.js +110 -0
  39. package/build/android/jsx/Chart.js.map +1 -0
  40. package/build/android/jsx/LineMark.d.ts +15 -0
  41. package/build/android/jsx/LineMark.d.ts.map +1 -0
  42. package/build/android/jsx/LineMark.js +6 -0
  43. package/build/android/jsx/LineMark.js.map +1 -0
  44. package/build/android/jsx/PointMark.d.ts +14 -0
  45. package/build/android/jsx/PointMark.d.ts.map +1 -0
  46. package/build/android/jsx/PointMark.js +6 -0
  47. package/build/android/jsx/PointMark.js.map +1 -0
  48. package/build/android/jsx/RuleMark.d.ts +13 -0
  49. package/build/android/jsx/RuleMark.d.ts.map +1 -0
  50. package/build/android/jsx/RuleMark.js +6 -0
  51. package/build/android/jsx/RuleMark.js.map +1 -0
  52. package/build/android/jsx/SectorMark.d.ts +15 -0
  53. package/build/android/jsx/SectorMark.d.ts.map +1 -0
  54. package/build/android/jsx/SectorMark.js +6 -0
  55. package/build/android/jsx/SectorMark.js.map +1 -0
  56. package/build/android/jsx/chart-types.d.ts +2 -0
  57. package/build/android/jsx/chart-types.d.ts.map +1 -0
  58. package/build/android/jsx/chart-types.js +2 -0
  59. package/build/android/jsx/chart-types.js.map +1 -0
  60. package/build/android/jsx/primitives.d.ts +8 -0
  61. package/build/android/jsx/primitives.d.ts.map +1 -1
  62. package/build/android/jsx/primitives.js +8 -0
  63. package/build/android/jsx/primitives.js.map +1 -1
  64. package/build/android/jsx/props/Text.d.ts +6 -0
  65. package/build/android/jsx/props/Text.d.ts.map +1 -1
  66. package/build/android/jsx/props/Text.js.map +1 -1
  67. package/build/android/payload/component-ids.d.ts.map +1 -1
  68. package/build/android/payload/component-ids.js +2 -1
  69. package/build/android/payload/component-ids.js.map +1 -1
  70. package/build/android/styles/types.d.ts +2 -0
  71. package/build/android/styles/types.d.ts.map +1 -1
  72. package/build/android/styles/types.js.map +1 -1
  73. package/build/android/widgets/server-credentials.d.ts +18 -0
  74. package/build/android/widgets/server-credentials.d.ts.map +1 -0
  75. package/build/android/widgets/server-credentials.js +24 -0
  76. package/build/android/widgets/server-credentials.js.map +1 -0
  77. package/build/client.d.ts +1 -0
  78. package/build/client.d.ts.map +1 -1
  79. package/build/client.js +2 -0
  80. package/build/client.js.map +1 -1
  81. package/build/jsx/AreaMark.d.ts +13 -0
  82. package/build/jsx/AreaMark.d.ts.map +1 -0
  83. package/build/jsx/AreaMark.js +6 -0
  84. package/build/jsx/AreaMark.js.map +1 -0
  85. package/build/jsx/BarMark.d.ts +15 -0
  86. package/build/jsx/BarMark.d.ts.map +1 -0
  87. package/build/jsx/BarMark.js +6 -0
  88. package/build/jsx/BarMark.js.map +1 -0
  89. package/build/jsx/Chart.d.ts +20 -0
  90. package/build/jsx/Chart.d.ts.map +1 -0
  91. package/build/jsx/Chart.js +124 -0
  92. package/build/jsx/Chart.js.map +1 -0
  93. package/build/jsx/LineMark.d.ts +15 -0
  94. package/build/jsx/LineMark.d.ts.map +1 -0
  95. package/build/jsx/LineMark.js +6 -0
  96. package/build/jsx/LineMark.js.map +1 -0
  97. package/build/jsx/PointMark.d.ts +14 -0
  98. package/build/jsx/PointMark.d.ts.map +1 -0
  99. package/build/jsx/PointMark.js +6 -0
  100. package/build/jsx/PointMark.js.map +1 -0
  101. package/build/jsx/RuleMark.d.ts +13 -0
  102. package/build/jsx/RuleMark.d.ts.map +1 -0
  103. package/build/jsx/RuleMark.js +6 -0
  104. package/build/jsx/RuleMark.js.map +1 -0
  105. package/build/jsx/SectorMark.d.ts +15 -0
  106. package/build/jsx/SectorMark.d.ts.map +1 -0
  107. package/build/jsx/SectorMark.js +6 -0
  108. package/build/jsx/SectorMark.js.map +1 -0
  109. package/build/jsx/chart-types.d.ts +10 -0
  110. package/build/jsx/chart-types.d.ts.map +1 -0
  111. package/build/jsx/chart-types.js +2 -0
  112. package/build/jsx/chart-types.js.map +1 -0
  113. package/build/jsx/primitives.d.ts +8 -0
  114. package/build/jsx/primitives.d.ts.map +1 -1
  115. package/build/jsx/primitives.js +8 -0
  116. package/build/jsx/primitives.js.map +1 -1
  117. package/build/jsx/props/AndroidChart.d.ts +16 -0
  118. package/build/jsx/props/AndroidChart.d.ts.map +1 -0
  119. package/build/jsx/props/AndroidChart.js +5 -0
  120. package/build/jsx/props/AndroidChart.js.map +1 -0
  121. package/build/jsx/props/Chart.d.ts +18 -0
  122. package/build/jsx/props/Chart.d.ts.map +1 -0
  123. package/build/jsx/props/Chart.js +5 -0
  124. package/build/jsx/props/Chart.js.map +1 -0
  125. package/build/live-activity/api.d.ts +10 -0
  126. package/build/live-activity/api.d.ts.map +1 -1
  127. package/build/live-activity/api.js +1 -0
  128. package/build/live-activity/api.js.map +1 -1
  129. package/build/package.json +1 -0
  130. package/build/payload/component-ids.d.ts.map +1 -1
  131. package/build/payload/component-ids.js +2 -1
  132. package/build/payload/component-ids.js.map +1 -1
  133. package/build/payload/short-names.d.ts.map +1 -1
  134. package/build/payload/short-names.js +14 -1
  135. package/build/payload/short-names.js.map +1 -1
  136. package/build/renderer/dispatcher.d.ts +22 -15
  137. package/build/renderer/dispatcher.d.ts.map +1 -1
  138. package/build/renderer/dispatcher.js +27 -0
  139. package/build/renderer/dispatcher.js.map +1 -1
  140. package/build/server.d.ts +1 -0
  141. package/build/server.d.ts.map +1 -1
  142. package/build/server.js +2 -0
  143. package/build/server.js.map +1 -1
  144. package/build/types.d.ts +16 -0
  145. package/build/types.d.ts.map +1 -1
  146. package/build/types.js.map +1 -1
  147. package/build/widget-server.d.ts +103 -0
  148. package/build/widget-server.d.ts.map +1 -0
  149. package/build/widget-server.js +170 -0
  150. package/build/widget-server.js.map +1 -0
  151. package/build/widgets/server-credentials.d.ts +49 -0
  152. package/build/widgets/server-credentials.d.ts.map +1 -0
  153. package/build/widgets/server-credentials.js +55 -0
  154. package/build/widgets/server-credentials.js.map +1 -0
  155. package/ios/app/VoltraImagePreload.swift +166 -0
  156. package/ios/app/VoltraLiveActivityManager.swift +228 -0
  157. package/ios/app/VoltraLiveActivityService.swift +24 -79
  158. package/ios/app/VoltraModule.swift +16 -1
  159. package/ios/app/VoltraModuleImpl.swift +81 -275
  160. package/ios/app/VoltraOptions.swift +4 -53
  161. package/ios/app/VoltraRN.swift +2 -1
  162. package/ios/app/VoltraWidgetService.swift +154 -0
  163. package/ios/shared/ComponentTypeID.swift +4 -0
  164. package/ios/shared/ShortNames.swift +7 -0
  165. package/ios/shared/VoltraConfig.swift +8 -2
  166. package/ios/shared/VoltraConstants.swift +44 -0
  167. package/ios/shared/VoltraEvent.swift +1 -1
  168. package/ios/shared/VoltraEventBus.swift +2 -1
  169. package/ios/shared/VoltraInteractionIntent.swift +2 -2
  170. package/ios/shared/VoltraKeychainHelper.swift +178 -0
  171. package/ios/shared/VoltraLogger.swift +30 -0
  172. package/ios/shared/VoltraNode.swift +7 -0
  173. package/ios/shared/VoltraPersistentEventQueue.swift +7 -6
  174. package/ios/shared/VoltraWidgetDefaults.swift +114 -0
  175. package/ios/shared/VoltraWidgetServerFetcher.swift +157 -0
  176. package/ios/target/VoltraHomeWidget.swift +68 -33
  177. package/ios/ui/Generated/Parameters/ChartParameters.swift +33 -0
  178. package/ios/ui/Layout/VoltraFlexStackLayout.swift +23 -6
  179. package/ios/ui/Protocols/VoltraView.swift +3 -1
  180. package/ios/ui/Style/BackgroundValue.swift +27 -0
  181. package/ios/ui/Style/DecorationStyle.swift +107 -3
  182. package/ios/ui/Style/JSColorParser.swift +230 -33
  183. package/ios/ui/Style/JSGradientParser.swift +674 -0
  184. package/ios/ui/Style/JSStyleParser.swift +16 -4
  185. package/ios/ui/Style/LayoutStyle.swift +7 -7
  186. package/ios/ui/Style/StyleConverter.swift +1 -1
  187. package/ios/ui/Views/VoltraChart.swift +810 -0
  188. package/ios/ui/Views/VoltraGauge.swift +0 -1
  189. package/ios/ui/Views/VoltraGroupBox.swift +0 -1
  190. package/ios/ui/Views/VoltraLinearGradient.swift +2 -2
  191. package/ios/ui/Views/VoltraTimer.swift +10 -11
  192. package/package.json +14 -6
  193. package/plugin/build/android/files/fonts.d.ts +18 -0
  194. package/plugin/build/android/files/fonts.js +97 -0
  195. package/plugin/build/android/files/index.d.ts +1 -0
  196. package/plugin/build/android/files/index.js +10 -1
  197. package/plugin/build/android/files/kotlin.js +52 -2
  198. package/plugin/build/android/index.js +2 -2
  199. package/plugin/build/index.d.ts +1 -1
  200. package/plugin/build/index.js +8 -0
  201. package/plugin/build/ios/entitlements.d.ts +5 -3
  202. package/plugin/build/ios/entitlements.js +26 -6
  203. package/plugin/build/ios/index.d.ts +3 -0
  204. package/plugin/build/ios/index.js +4 -0
  205. package/plugin/build/ios/infoPlist.d.ts +6 -0
  206. package/plugin/build/ios/infoPlist.js +22 -0
  207. package/plugin/build/ios-widget/files/entitlements.d.ts +2 -1
  208. package/plugin/build/ios-widget/files/entitlements.js +11 -3
  209. package/plugin/build/ios-widget/files/index.d.ts +1 -0
  210. package/plugin/build/ios-widget/files/index.js +3 -2
  211. package/plugin/build/ios-widget/index.d.ts +1 -0
  212. package/plugin/build/ios-widget/index.js +3 -3
  213. package/plugin/build/ios-widget/widgetPlist.d.ts +5 -0
  214. package/plugin/build/ios-widget/widgetPlist.js +24 -1
  215. package/plugin/build/ios-widget/xcode/buildPhases.js +0 -1
  216. package/plugin/build/ios-widget/xcode/groups.js +0 -1
  217. package/plugin/build/types.d.ts +57 -0
  218. package/skills/voltra/SKILL.md +75 -0
  219. package/skills/voltra/references/android-widgets.md +35 -0
  220. package/skills/voltra/references/app-config.md +29 -0
  221. package/skills/voltra/references/charts.md +66 -0
  222. package/skills/voltra/references/component-mapping.md +31 -0
  223. package/skills/voltra/references/images.md +50 -0
  224. package/skills/voltra/references/ios-live-activities.md +27 -0
  225. package/skills/voltra/references/ios-server-updates.md +27 -0
  226. package/skills/voltra/references/ios-widgets.md +32 -0
  227. package/skills/voltra/references/plugin-schema.md +68 -0
  228. package/skills/voltra/references/push-flow.md +27 -0
  229. package/skills/voltra/references/runtime-api-checklist.md +23 -0
  230. package/skills/voltra/references/server-driven-widgets.md +45 -0
  231. package/skills/voltra/references/setup.md +23 -0
  232. package/skills/voltra/references/source-of-truth.md +32 -0
  233. package/skills/voltra/references/variant-shapes.md +43 -0
  234. package/skills/voltra/references/widget-families.md +29 -0
  235. package/turbo.json +25 -0
package/README.md CHANGED
@@ -24,14 +24,18 @@ Voltra turns React Native JSX into SwiftUI and Jetpack Compose Glance so you can
24
24
 
25
25
  The documentation is available at [use-voltra.dev](https://use-voltra.dev). You can also use the following links to jump to specific topics:
26
26
 
27
- - [Quick Start](https://use-voltra.dev/getting-started/quick-start)
28
- - [Development](https://www.use-voltra.dev/development/developing-live-activities)
29
- - [Components](https://www.use-voltra.dev/components/overview)
30
- - [API Reference](https://use-voltra.dev/api/configuration)
27
+ - [Getting Started](https://use-voltra.dev/getting-started/introduction)
28
+ - [Installation](https://use-voltra.dev/getting-started/installation)
29
+ - [iOS Setup](https://use-voltra.dev/ios/setup)
30
+ - [Android Setup](https://use-voltra.dev/android/setup)
31
+ - [iOS Development](https://use-voltra.dev/ios/development/developing-live-activities)
32
+ - [Android Development](https://use-voltra.dev/android/development/developing-widgets)
33
+ - [iOS API Reference](https://use-voltra.dev/ios/api/configuration)
34
+ - [Android API Reference](https://use-voltra.dev/android/api/plugin-configuration)
31
35
 
32
36
  ## Getting started
33
37
 
34
- > [!NOTE]
38
+ > [!NOTE]
35
39
  > The library isn't supported in Expo Go. To set it up correctly, you need to use [Expo Dev Client](https://docs.expo.dev/versions/latest/sdk/dev-client/).
36
40
 
37
41
  Install the package:
@@ -52,7 +56,7 @@ Add the config plugin to your `app.json`:
52
56
 
53
57
  Then run `npx expo prebuild --clean` to generate the native extension targets.
54
58
 
55
- See the [documentation](https://use-voltra.dev/getting-started/quick-start) for detailed setup instructions.
59
+ See the [documentation](https://use-voltra.dev/getting-started/installation) for detailed setup instructions.
56
60
 
57
61
  ## Quick example
58
62
 
@@ -90,7 +94,7 @@ Voltra is a cross-platform library that supports:
90
94
 
91
95
  ## Authors
92
96
 
93
- `voltra` is an open source collaboration between [Saúl Sharma](https://github.com/saul-sharma) and [Szymon Chmal](https://github.com/szymonchmal) at [Callstack][callstack-readme-with-love].
97
+ `voltra` is an open source collaboration between [Saúl Sharma](https://github.com/saulsharma) and [Szymon Chmal](https://github.com/szymonchmal) at [Callstack][callstack-readme-with-love].
94
98
 
95
99
  If you think it's cool, please star it 🌟. This project will always remain free to use.
96
100
 
@@ -51,11 +51,20 @@ if (useManagedAndroidSdkVersions) {
51
51
  }
52
52
  }
53
53
 
54
+ // Read version from package.json for BuildConfig
55
+ def packageJsonFile = new File(project.projectDir.parentFile, "package.json")
56
+ def packageJson = new groovy.json.JsonSlurper().parseText(packageJsonFile.text)
57
+ def voltraVersion = packageJson.version
58
+
54
59
  android {
55
60
  namespace "voltra"
56
61
  defaultConfig {
57
62
  versionCode 1
58
63
  versionName "0.1.0"
64
+ buildConfigField "String", "VOLTRA_VERSION", "\"${voltraVersion}\""
65
+ }
66
+ buildFeatures {
67
+ buildConfig true
59
68
  }
60
69
  lintOptions {
61
70
  abortOnError false
@@ -63,6 +72,17 @@ android {
63
72
  buildFeatures {
64
73
  compose true
65
74
  }
75
+ testOptions {
76
+ unitTests.all {
77
+ testLogging {
78
+ events "passed", "failed", "skipped"
79
+ exceptionFormat "full"
80
+ showCauses true
81
+ showExceptions true
82
+ showStackTraces true
83
+ }
84
+ }
85
+ }
66
86
  }
67
87
 
68
88
  dependencies {
@@ -73,6 +93,15 @@ dependencies {
73
93
  // Compose runtime (required for Glance)
74
94
  api "androidx.compose.runtime:runtime:1.6.8"
75
95
 
96
+ // WorkManager (for periodic server-driven widget updates)
97
+ api "androidx.work:work-runtime-ktx:2.9.1"
98
+
99
+ // DataStore (credential storage for widget server updates)
100
+ api "androidx.datastore:datastore-preferences:1.1.4"
101
+
102
+ // Google Tink (encryption for credential storage)
103
+ implementation "com.google.crypto.tink:tink-android:1.19.0"
104
+
76
105
  // JSON parsing
77
106
  implementation "com.google.code.gson:gson:2.10.1"
78
107
 
@@ -81,4 +110,8 @@ dependencies {
81
110
 
82
111
  // Kotlinx Serialization
83
112
  implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
113
+
114
+ // Unit tests
115
+ testImplementation "junit:junit:4.13.2"
116
+ testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1"
84
117
  }
@@ -328,6 +328,50 @@ class VoltraModule : Module() {
328
328
  imageManager.clearPreloadedImages(keys)
329
329
  }
330
330
 
331
+ // Widget Server Credentials APIs
332
+
333
+ AsyncFunction("setWidgetServerCredentials") { credentials: Map<String, Any?> ->
334
+ Log.d(TAG, "setWidgetServerCredentials called")
335
+
336
+ val context = appContext.reactContext!!
337
+ val token =
338
+ credentials["token"] as? String
339
+ ?: throw IllegalArgumentException("token is required in credentials")
340
+
341
+ @Suppress("UNCHECKED_CAST")
342
+ val headers = credentials["headers"] as? Map<String, String>
343
+
344
+ runBlocking {
345
+ voltra.widget.VoltraWidgetCredentialStore.saveToken(context, token)
346
+ if (headers != null && headers.isNotEmpty()) {
347
+ voltra.widget.VoltraWidgetCredentialStore.saveHeaders(context, headers)
348
+ }
349
+ }
350
+
351
+ Log.d(TAG, "Widget server credentials saved")
352
+
353
+ val widgetManager = voltra.widget.VoltraWidgetManager(context)
354
+ runBlocking {
355
+ widgetManager.reloadAllWidgets()
356
+ }
357
+ }
358
+
359
+ AsyncFunction("clearWidgetServerCredentials") {
360
+ Log.d(TAG, "clearWidgetServerCredentials called")
361
+
362
+ val context = appContext.reactContext!!
363
+ runBlocking {
364
+ voltra.widget.VoltraWidgetCredentialStore.clearAll(context)
365
+ }
366
+
367
+ Log.d(TAG, "Widget server credentials cleared")
368
+
369
+ val widgetManager = voltra.widget.VoltraWidgetManager(context)
370
+ runBlocking {
371
+ widgetManager.reloadAllWidgets()
372
+ }
373
+ }
374
+
331
375
  AsyncFunction("reloadLiveActivities") { activityNames: List<String>? ->
332
376
  // On Android, we don't have "Live Activities" in the same sense as iOS,
333
377
  // but we might want to refresh widgets or notifications.
@@ -60,6 +60,10 @@ class VoltraRN(
60
60
  val widthDp = frameLayout.width.toFloat() / density
61
61
  val heightDp = frameLayout.height.toFloat() / density
62
62
 
63
+ // Don't render until we have real dimensions from layout.
64
+ // onLayout() will call updateView() once dimensions are available.
65
+ if (widthDp <= 1f || heightDp <= 1f) return
66
+
63
67
  // Avoid redundant updates if nothing significant changed and we already have a view
64
68
  if (frameLayout.childCount > 0 &&
65
69
  payloadStr == lastRenderedPayload &&
@@ -94,14 +98,7 @@ class VoltraRN(
94
98
  return@launch
95
99
  }
96
100
 
97
- // Determine size for Glance composition.
98
- // Glance needs a non-zero size. If we don't have one yet, use a fallback.
99
- val composeSize =
100
- if (widthDp > 1f && heightDp > 1f) {
101
- DpSize(widthDp.dp, heightDp.dp)
102
- } else {
103
- DpSize(300.dp, 200.dp)
104
- }
101
+ val composeSize = DpSize(widthDp.dp, heightDp.dp)
105
102
 
106
103
  val glanceRemoteViews = GlanceRemoteViews()
107
104
  val factory = GlanceFactory(id, voltraPayload.e, voltraPayload.s)
@@ -117,19 +114,27 @@ class VoltraRN(
117
114
 
118
115
  val remoteViews = result.remoteViews
119
116
 
117
+ // Check if the frameLayout dimensions have changed since we started composing.
118
+ // If so, skip this render — a new updateView() will be triggered by onLayout.
119
+ val currentWidthDp = frameLayout.width.toFloat() / density
120
+ val currentHeightDp = frameLayout.height.toFloat() / density
121
+ if (abs(currentWidthDp - widthDp) >= 1.0f || abs(currentHeightDp - heightDp) >= 1.0f) {
122
+ return@launch
123
+ }
124
+
120
125
  withContext(Dispatchers.Main) {
121
126
  try {
122
- // Try to reapply to the existing view first to avoid flickering/replacing
127
+ // Try to reapply to the existing view first to avoid flickering/replacing.
128
+ // IMPORTANT: Only use reapply for dimension-only changes (same payload).
129
+ // When the payload changes, always do a fresh apply to prevent stale
130
+ // style bleed (e.g. padding from a previous widget persisting because
131
+ // the new widget's RemoteViews doesn't explicitly reset it to zero).
123
132
  var applied = false
124
- if (frameLayout.childCount > 0) {
133
+ if (frameLayout.childCount > 0 && payloadStr == lastRenderedPayload) {
125
134
  try {
126
135
  val existingView = frameLayout.getChildAt(0)
127
136
  remoteViews.reapply(context, existingView)
128
137
  applied = true
129
- // Update tracking state
130
- lastRenderedPayload = payloadStr
131
- lastRenderedWidthDp = widthDp
132
- lastRenderedHeightDp = heightDp
133
138
  } catch (e: Exception) {
134
139
  }
135
140
  }
@@ -145,25 +150,29 @@ class VoltraRN(
145
150
  if (childCount > 1) {
146
151
  frameLayout.removeViews(0, childCount - 1)
147
152
  }
148
-
149
- // CRITICAL FIX: Manually trigger layout for the new content.
150
- // Since we are adding this async, the parent won't do it for us automatically.
151
- frameLayout.measure(
152
- View.MeasureSpec.makeMeasureSpec(frameLayout.width, View.MeasureSpec.EXACTLY),
153
- View.MeasureSpec.makeMeasureSpec(frameLayout.height, View.MeasureSpec.EXACTLY),
154
- )
155
- frameLayout.layout(
156
- frameLayout.left,
157
- frameLayout.top,
158
- frameLayout.right,
159
- frameLayout.bottom,
160
- )
161
-
162
- // Update tracking state
163
- lastRenderedPayload = payloadStr
164
- lastRenderedWidthDp = widthDp
165
- lastRenderedHeightDp = heightDp
166
153
  }
154
+
155
+ // Always re-measure and re-layout after applying RemoteViews.
156
+ // This is critical because:
157
+ // 1) After fresh inflate: the view was added asynchronously so the parent
158
+ // won't trigger layout automatically.
159
+ // 2) After reapply: text content or styles may have changed, requiring
160
+ // re-measurement to avoid stale layout constraints (e.g. truncated text).
161
+ frameLayout.measure(
162
+ View.MeasureSpec.makeMeasureSpec(frameLayout.width, View.MeasureSpec.EXACTLY),
163
+ View.MeasureSpec.makeMeasureSpec(frameLayout.height, View.MeasureSpec.EXACTLY),
164
+ )
165
+ frameLayout.layout(
166
+ frameLayout.left,
167
+ frameLayout.top,
168
+ frameLayout.right,
169
+ frameLayout.bottom,
170
+ )
171
+
172
+ // Update tracking state
173
+ lastRenderedPayload = payloadStr
174
+ lastRenderedWidthDp = widthDp
175
+ lastRenderedHeightDp = heightDp
167
176
  } catch (e: Exception) {
168
177
  }
169
178
  }
@@ -67,6 +67,7 @@ object ShortNames {
67
67
  "fvar" to "fontVariant",
68
68
  "fw" to "fontWeight",
69
69
  "fgs" to "foregroundStyle",
70
+ "fss" to "foregroundStyleScale",
70
71
  "f" to "frame",
71
72
  "g" to "gap",
72
73
  "gs" to "gaugeStyle",
@@ -83,6 +84,7 @@ object ShortNames {
83
84
  "ly" to "layout",
84
85
  "lp" to "layoutPriority",
85
86
  "l" to "left",
87
+ "lgv" to "legendVisibility",
86
88
  "ls" to "letterSpacing",
87
89
  "lh" to "lineHeight",
88
90
  "ll" to "lineLimit",
@@ -95,6 +97,7 @@ object ShortNames {
95
97
  "mr" to "marginRight",
96
98
  "mt" to "marginTop",
97
99
  "mv" to "marginVertical",
100
+ "mrk" to "marks",
98
101
  "me" to "maskElement",
99
102
  "maxh" to "maxHeight",
100
103
  "max" to "maximumValue",
@@ -163,6 +166,10 @@ object ShortNames {
163
166
  "valig" to "verticalAlignment",
164
167
  "wt" to "weight",
165
168
  "w" to "width",
169
+ "xgs" to "xAxisGridStyle",
170
+ "xav" to "xAxisVisibility",
171
+ "ygs" to "yAxisGridStyle",
172
+ "yav" to "yAxisVisibility",
166
173
  "zi" to "zIndex",
167
174
  )
168
175