react-native-audio-browser 0.2.0-next.11 → 0.2.0-next.13

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 (416) hide show
  1. package/README.md +14 -4
  2. package/android/build.gradle +20 -1
  3. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +115 -26
  4. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +262 -188
  5. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
  6. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
  7. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +95 -58
  8. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
  9. package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
  10. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +19 -7
  11. package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
  12. package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
  13. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +1 -1
  14. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
  15. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
  16. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +109 -74
  17. package/android/src/main/java/com/audiobrowser/player/Player.kt +51 -30
  18. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
  19. package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
  20. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
  21. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
  22. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
  23. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +52 -53
  24. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +74 -41
  25. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
  26. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +229 -50
  27. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
  28. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
  29. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
  30. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
  31. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
  32. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
  33. package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
  34. package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
  35. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
  36. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
  37. package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
  38. package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
  39. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
  40. package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
  41. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
  42. package/ios/Browser/BrowserConfig.swift +102 -5
  43. package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
  44. package/ios/Browser/BrowserManager.swift +229 -180
  45. package/ios/Browser/BrowserPathHelper.swift +44 -44
  46. package/ios/Browser/InertStyleDiagnostic.swift +279 -0
  47. package/ios/Browser/JsonModels.swift +258 -106
  48. package/ios/Browser/SearchDrillIn.swift +6 -6
  49. package/ios/Browser/SectionPresentation.swift +64 -0
  50. package/ios/Browser/SectionScope.swift +58 -24
  51. package/ios/Browser/StyleResolver.swift +58 -0
  52. package/ios/Browser/TabBarEntries.swift +9 -3
  53. package/ios/Browser/TrackIdTemplate.swift +37 -0
  54. package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
  55. package/ios/CarPlay/CarPlayController.swift +227 -85
  56. package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
  57. package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
  58. package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
  59. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  60. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  61. package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
  62. package/ios/Extension/Section+Copying.swift +31 -0
  63. package/ios/Extension/Track+Copying.swift +6 -12
  64. package/ios/Extension/Track+Identity.swift +18 -0
  65. package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
  66. package/ios/Http/HttpClient.swift +8 -2
  67. package/ios/HybridAudioBrowser.swift +166 -45
  68. package/ios/Model/NitroTypeStubs.swift +106 -21
  69. package/ios/Model/PlayerUpdateOptions.swift +7 -0
  70. package/ios/Player/MediaIntentCriteria.swift +13 -5
  71. package/ios/Player/MediaLoader.swift +33 -9
  72. package/ios/Player/NowPlayingUpdater.swift +54 -6
  73. package/ios/Player/PlaybackCoordinator.swift +13 -6
  74. package/ios/Player/PlaybackTimer.swift +8 -2
  75. package/ios/Player/QueueManager.swift +36 -10
  76. package/ios/Player/RetryManager.swift +4 -3
  77. package/ios/Player/ShuffleOrder.swift +23 -0
  78. package/ios/Player/SleepTimerHandling.swift +13 -0
  79. package/ios/RemoteCommand/RemoteCommandController.swift +53 -18
  80. package/ios/Tests/BrowserPathHelperTests.swift +50 -20
  81. package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
  82. package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
  83. package/ios/Tests/JsonModelsTests.swift +150 -1
  84. package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
  85. package/ios/Tests/QueueManagerTests.swift +72 -6
  86. package/ios/Tests/RemoteCommandControllerTests.swift +16 -0
  87. package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
  88. package/ios/Tests/SearchDrillInTests.swift +16 -16
  89. package/ios/Tests/SectionPresentationTests.swift +90 -0
  90. package/ios/Tests/SectionScopeTests.swift +87 -55
  91. package/ios/Tests/StyleResolverTests.swift +127 -0
  92. package/ios/Tests/Support/TestTypes.swift +7 -0
  93. package/ios/Tests/TabBarEntriesTests.swift +18 -18
  94. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  95. package/ios/Tests/TrackIdentityTests.swift +20 -0
  96. package/ios/Tests/TrackSelectorTests.swift +168 -33
  97. package/ios/TrackPlayer.swift +57 -34
  98. package/ios/TrackSelector.swift +45 -30
  99. package/ios/Util/ArtworkImageFetcher.swift +16 -1
  100. package/ios/Util/NetworkMonitor.swift +43 -26
  101. package/ios/Util/SFSymbolRenderer.swift +24 -2
  102. package/ios/Util/SVGProcessor.swift +9 -2
  103. package/lib/commonjs/AudioBrowser.js +11 -0
  104. package/lib/commonjs/AudioBrowser.js.map +1 -1
  105. package/lib/commonjs/features/browser-config.js +11 -8
  106. package/lib/commonjs/features/browser-config.js.map +1 -1
  107. package/lib/commonjs/features/browser.js +2 -2
  108. package/lib/commonjs/features/favorites.js +13 -10
  109. package/lib/commonjs/features/favorites.js.map +1 -1
  110. package/lib/commonjs/features/playback/controls.js +4 -2
  111. package/lib/commonjs/features/playback/controls.js.map +1 -1
  112. package/lib/commonjs/features/playback/progress.js +2 -1
  113. package/lib/commonjs/features/playback/progress.js.map +1 -1
  114. package/lib/commonjs/features/playback/rate.js +8 -1
  115. package/lib/commonjs/features/playback/rate.js.map +1 -1
  116. package/lib/commonjs/features/playback/state.js +2 -3
  117. package/lib/commonjs/features/playback/state.js.map +1 -1
  118. package/lib/commonjs/features/player/options.js.map +1 -1
  119. package/lib/commonjs/features/player/setup.js +3 -1
  120. package/lib/commonjs/features/player/setup.js.map +1 -1
  121. package/lib/commonjs/features/player/validateOptions.js +2 -0
  122. package/lib/commonjs/features/player/validateOptions.js.map +1 -1
  123. package/lib/commonjs/features/queue/activeTrack.js +14 -3
  124. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  125. package/lib/commonjs/features/queue/queue.js +11 -4
  126. package/lib/commonjs/features/queue/queue.js.map +1 -1
  127. package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
  128. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  129. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  130. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  131. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  132. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  133. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  134. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  135. package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
  136. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  137. package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
  138. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
  139. package/lib/commonjs/web/browser/BrowserManager.js +144 -87
  140. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  141. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  142. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  143. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  144. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  145. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  146. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  147. package/lib/commonjs/web/browser/sections.js +89 -0
  148. package/lib/commonjs/web/browser/sections.js.map +1 -0
  149. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  150. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  151. package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
  152. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  153. package/lib/module/AudioBrowser.js +1 -0
  154. package/lib/module/AudioBrowser.js.map +1 -1
  155. package/lib/module/features/browser-config.js +11 -8
  156. package/lib/module/features/browser-config.js.map +1 -1
  157. package/lib/module/features/browser.js +2 -2
  158. package/lib/module/features/favorites.js +13 -10
  159. package/lib/module/features/favorites.js.map +1 -1
  160. package/lib/module/features/playback/controls.js +4 -2
  161. package/lib/module/features/playback/controls.js.map +1 -1
  162. package/lib/module/features/playback/progress.js +2 -1
  163. package/lib/module/features/playback/progress.js.map +1 -1
  164. package/lib/module/features/playback/rate.js +8 -1
  165. package/lib/module/features/playback/rate.js.map +1 -1
  166. package/lib/module/features/playback/state.js +2 -3
  167. package/lib/module/features/playback/state.js.map +1 -1
  168. package/lib/module/features/player/options.js.map +1 -1
  169. package/lib/module/features/player/setup.js +3 -1
  170. package/lib/module/features/player/setup.js.map +1 -1
  171. package/lib/module/features/player/validateOptions.js +2 -0
  172. package/lib/module/features/player/validateOptions.js.map +1 -1
  173. package/lib/module/features/queue/activeTrack.js +14 -3
  174. package/lib/module/features/queue/activeTrack.js.map +1 -1
  175. package/lib/module/features/queue/queue.js +11 -4
  176. package/lib/module/features/queue/queue.js.map +1 -1
  177. package/lib/module/utils/LazyNativeEmitter.js +21 -10
  178. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  179. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  180. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  181. package/lib/module/utils/getTrackIdentity.js +20 -0
  182. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  183. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  184. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  185. package/lib/module/web/NativeAudioBrowser.js +32 -24
  186. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  187. package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
  188. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
  189. package/lib/module/web/browser/BrowserManager.js +144 -87
  190. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  191. package/lib/module/web/browser/FavoriteManager.js +31 -21
  192. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  193. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  194. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  195. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  196. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  197. package/lib/module/web/browser/sections.js +83 -0
  198. package/lib/module/web/browser/sections.js.map +1 -0
  199. package/lib/module/web/player/NowPlayingManager.js +4 -3
  200. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  201. package/lib/module/web/util/BrowserPathHelper.js +56 -11
  202. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  203. package/lib/typescript/src/AudioBrowser.d.ts +1 -0
  204. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  205. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  206. package/lib/typescript/src/features/browser.d.ts +2 -2
  207. package/lib/typescript/src/features/favorites.d.ts +13 -10
  208. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  209. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  210. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  211. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  212. package/lib/typescript/src/features/playback/rate.d.ts +8 -1
  213. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
  214. package/lib/typescript/src/features/playback/state.d.ts +2 -3
  215. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  216. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  217. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  218. package/lib/typescript/src/features/player/options.d.ts +21 -19
  219. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  220. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  221. package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
  222. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
  223. package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
  224. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  225. package/lib/typescript/src/features/queue/queue.d.ts +5 -2
  226. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  227. package/lib/typescript/src/types/browser-native.d.ts +2 -2
  228. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  229. package/lib/typescript/src/types/browser-nodes.d.ts +361 -126
  230. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  231. package/lib/typescript/src/types/browser.d.ts +33 -53
  232. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  233. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
  234. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  235. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  236. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  237. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  238. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  239. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  240. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  241. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
  242. package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
  243. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
  244. package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
  245. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  246. package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
  247. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
  248. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
  249. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
  250. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
  251. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
  252. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
  253. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
  254. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
  255. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
  256. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
  257. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
  258. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
  259. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
  260. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
  261. package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
  262. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +33 -14
  263. package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
  264. package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
  265. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
  266. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +22 -10
  267. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
  268. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +10 -7
  269. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
  270. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
  271. package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
  272. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
  273. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  274. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +55 -51
  275. package/nitrogen/generated/android/c++/JSection.hpp +120 -0
  276. package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
  277. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  278. package/nitrogen/generated/android/c++/JTrack.hpp +23 -48
  279. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
  280. package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
  281. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
  282. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  283. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  284. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
  285. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
  286. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
  287. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
  288. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
  289. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
  290. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  291. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
  292. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +18 -6
  293. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
  294. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  295. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
  296. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
  297. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +158 -82
  298. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +25 -10
  299. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +25 -10
  300. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  301. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  302. package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
  303. package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
  304. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  305. package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
  306. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
  307. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  308. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  309. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
  310. package/nitrogen/generated/ios/swift/Section.swift +100 -0
  311. package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
  312. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  313. package/nitrogen/generated/ios/swift/Track.swift +20 -73
  314. package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
  315. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  316. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  317. package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
  318. package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
  319. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  320. package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
  321. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
  322. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  323. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  324. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
  325. package/nitrogen/generated/shared/c++/Section.hpp +106 -0
  326. package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
  327. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  328. package/nitrogen/generated/shared/c++/Track.hpp +15 -31
  329. package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
  330. package/package.json +7 -2
  331. package/src/AudioBrowser.ts +1 -0
  332. package/src/features/browser-config.test.ts +21 -6
  333. package/src/features/browser-config.ts +11 -8
  334. package/src/features/browser.test.ts +1 -1
  335. package/src/features/browser.ts +2 -2
  336. package/src/features/favorites.ts +13 -10
  337. package/src/features/hook-wiring.test.ts +63 -15
  338. package/src/features/playback/controls.ts +4 -2
  339. package/src/features/playback/progress.ts +2 -1
  340. package/src/features/playback/rate.ts +8 -1
  341. package/src/features/playback/state.ts +2 -3
  342. package/src/features/playback/trackPlaybackTime.ts +1 -1
  343. package/src/features/player/options.ts +27 -19
  344. package/src/features/player/setup.test.ts +12 -3
  345. package/src/features/player/setup.ts +3 -1
  346. package/src/features/player/validateOptions.ts +2 -0
  347. package/src/features/queue/activeTrack.ts +14 -3
  348. package/src/features/queue/queue.ts +11 -4
  349. package/src/nitro-stub-drift.test.ts +40 -0
  350. package/src/public-surface.test.ts +40 -0
  351. package/src/specs/audio-browser.nitro.ts +4 -3
  352. package/src/style-resolution.test.ts +210 -0
  353. package/src/types/browser-native.ts +2 -2
  354. package/src/types/browser-nodes.ts +377 -135
  355. package/src/types/browser.ts +34 -55
  356. package/src/utils/LazyNativeEmitter.ts +20 -10
  357. package/src/utils/NativeUpdatedValue.ts +29 -13
  358. package/src/utils/getTrackIdentity.ts +20 -0
  359. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  360. package/src/web/NativeAudioBrowser.ts +37 -22
  361. package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
  362. package/src/web/TrackPlayer/QueueManager.ts +14 -0
  363. package/src/web/browser/BrowserManager.ts +176 -95
  364. package/src/web/browser/FavoriteManager.test.ts +84 -0
  365. package/src/web/browser/FavoriteManager.ts +28 -23
  366. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  367. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  368. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  369. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  370. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  371. package/src/web/browser/sections.test.ts +91 -0
  372. package/src/web/browser/sections.ts +72 -0
  373. package/src/web/player/NowPlayingManager.ts +4 -3
  374. package/src/web/util/BrowserPathHelper.test.ts +52 -0
  375. package/src/web/util/BrowserPathHelper.ts +61 -13
  376. package/website/guide/android-auto.md +114 -0
  377. package/website/guide/android-certificates.md +409 -0
  378. package/website/guide/artwork.md +255 -0
  379. package/website/guide/audio-output.md +135 -0
  380. package/website/guide/automotive.md +86 -0
  381. package/website/guide/basic-usage.md +151 -0
  382. package/website/guide/battery.md +115 -0
  383. package/website/guide/browser.md +581 -0
  384. package/website/guide/carplay.md +620 -0
  385. package/website/guide/configuration.md +173 -0
  386. package/website/guide/equalizer.md +231 -0
  387. package/website/guide/errors.md +310 -0
  388. package/website/guide/favorites.md +154 -0
  389. package/website/guide/gate.md +143 -0
  390. package/website/guide/getting-started.md +46 -0
  391. package/website/guide/hooks.md +238 -0
  392. package/website/guide/live-streams.md +125 -0
  393. package/website/guide/metadata.md +239 -0
  394. package/website/guide/migrating-from-track-player.md +244 -0
  395. package/website/guide/native-callback-fetch.md +127 -0
  396. package/website/guide/network.md +86 -0
  397. package/website/guide/now-playing.md +175 -0
  398. package/website/guide/playback.md +328 -0
  399. package/website/guide/queue.md +300 -0
  400. package/website/guide/remote-controls.md +256 -0
  401. package/website/guide/search.md +255 -0
  402. package/website/guide/sleep-timer.md +268 -0
  403. package/website/guide/track.md +246 -0
  404. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  405. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  406. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  407. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  408. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  409. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  410. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  411. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  412. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  413. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  414. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  415. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  416. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
@@ -62,41 +62,196 @@ extension JsonArtwork {
62
62
  }
63
63
  }
64
64
 
65
- /// JSON model for image row items (horizontal thumbnail row).
66
- struct JsonImageRowItem: Codable {
67
- let id: String?
68
- let url: String?
69
- let src: String?
70
- let artwork: String?
71
- let title: String
72
- let artist: String?
73
- let album: String?
74
- let albumUrl: String?
75
- let live: Bool?
76
- let request: JsonTrackRequest?
65
+ /// A style declaration block on the wire (ADR 0011):
66
+ /// `{ display?, gridWrap?, artworkRendering? }`.
67
+ ///
68
+ /// One JSON model serves both the `TrackStyle` and `SectionStyle` positions —
69
+ /// keys that don't exist at the narrower position simply don't map.
70
+ ///
71
+ /// Decoding is tolerant by design: a stale payload — the retired string
72
+ /// styles, a wrong-typed field, an unknown enum value — must degrade to "no
73
+ /// declaration", never fail the page or the persisted playback state (the
74
+ /// decoders above this are strict, so a throw here would kill the whole
75
+ /// payload). `init(from:)` therefore never throws, and unknown enum values
76
+ /// map to nil at `toNitro` time.
77
+ struct JsonStyle: Codable, Equatable {
78
+ let display: String?
79
+ let gridWrap: Bool?
80
+ let gridTile: String?
81
+ let artworkRendering: String?
82
+ let imageShape: String?
83
+ let accessorySymbol: String?
84
+ let cardTint: String?
85
+ let cardImage: String?
77
86
 
78
87
  init(
79
- id: String? = nil,
80
- url: String? = nil,
81
- src: String? = nil,
82
- artwork: String? = nil,
83
- title: String,
84
- artist: String? = nil,
85
- album: String? = nil,
86
- albumUrl: String? = nil,
87
- live: Bool? = nil,
88
- request: JsonTrackRequest? = nil,
88
+ display: String? = nil,
89
+ gridWrap: Bool? = nil,
90
+ gridTile: String? = nil,
91
+ artworkRendering: String? = nil,
92
+ imageShape: String? = nil,
93
+ accessorySymbol: String? = nil,
94
+ cardTint: String? = nil,
95
+ cardImage: String? = nil,
96
+ ) {
97
+ self.display = display
98
+ self.gridWrap = gridWrap
99
+ self.gridTile = gridTile
100
+ self.artworkRendering = artworkRendering
101
+ self.imageShape = imageShape
102
+ self.accessorySymbol = accessorySymbol
103
+ self.cardTint = cardTint
104
+ self.cardImage = cardImage
105
+ }
106
+
107
+ init(from decoder: Decoder) throws {
108
+ // A non-object value (the retired `style: 'grid'` string) decodes as an
109
+ // empty block rather than failing the parent.
110
+ guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
111
+ self.init()
112
+ return
113
+ }
114
+ self.init(
115
+ display: (try? container.decodeIfPresent(String.self, forKey: .display)) ?? nil,
116
+ gridWrap: (try? container.decodeIfPresent(Bool.self, forKey: .gridWrap)) ?? nil,
117
+ gridTile: (try? container.decodeIfPresent(String.self, forKey: .gridTile)) ?? nil,
118
+ artworkRendering: (try? container.decodeIfPresent(String.self, forKey: .artworkRendering)) ?? nil,
119
+ imageShape: (try? container.decodeIfPresent(String.self, forKey: .imageShape)) ?? nil,
120
+ accessorySymbol: (try? container.decodeIfPresent(String.self, forKey: .accessorySymbol)) ?? nil,
121
+ cardTint: (try? container.decodeIfPresent(String.self, forKey: .cardTint)) ?? nil,
122
+ cardImage: (try? container.decodeIfPresent(String.self, forKey: .cardImage)) ?? nil,
123
+ )
124
+ }
125
+ }
126
+
127
+ extension JsonStyle {
128
+ /// The wire strings map to Nitro enums here, tolerantly: an unknown value
129
+ /// (a future `display` mode, say) is no declaration, not an error. A block
130
+ /// that resolves to no declarations at all — `{}`, the tolerant decoder's
131
+ /// empty result for the retired string vocabulary, or all-unknown values —
132
+ /// maps to nil, so "no declaration" has one shape on every platform
133
+ /// (Android's lenient mapper makes the same collapse).
134
+ func toTrackStyle() -> TrackStyle? {
135
+ let display = display.flatMap { StyleDisplay(fromString: $0.lowercased()) }
136
+ let artworkRendering = artworkRendering.flatMap { ArtworkRendering(fromString: $0.lowercased()) }
137
+ let imageShape = imageShape.flatMap { ImageShape(fromString: $0.lowercased()) }
138
+ // The 'none' sentinel is a legitimate value; only emptiness collapses.
139
+ let accessorySymbol = accessorySymbol?.isEmpty == false ? accessorySymbol : nil
140
+ let cardTint = cardTint?.isEmpty == false ? cardTint : nil
141
+ let cardImage = cardImage.flatMap { CardImage(fromString: $0.lowercased()) }
142
+ guard display != nil || artworkRendering != nil || imageShape != nil
143
+ || accessorySymbol != nil || cardTint != nil || cardImage != nil
144
+ else { return nil }
145
+ return TrackStyle(
146
+ display: display,
147
+ artworkRendering: artworkRendering,
148
+ imageShape: imageShape,
149
+ accessorySymbol: accessorySymbol,
150
+ cardTint: cardTint,
151
+ cardImage: cardImage,
152
+ )
153
+ }
154
+
155
+ func toSectionStyle() -> SectionStyle? {
156
+ let display = display.flatMap { StyleDisplay(fromString: $0.lowercased()) }
157
+ let gridTile = gridTile.flatMap { GridTile(fromString: $0.lowercased()) }
158
+ let artworkRendering = artworkRendering.flatMap { ArtworkRendering(fromString: $0.lowercased()) }
159
+ let imageShape = imageShape.flatMap { ImageShape(fromString: $0.lowercased()) }
160
+ let accessorySymbol = accessorySymbol?.isEmpty == false ? accessorySymbol : nil
161
+ let cardTint = cardTint?.isEmpty == false ? cardTint : nil
162
+ let cardImage = cardImage.flatMap { CardImage(fromString: $0.lowercased()) }
163
+ guard display != nil || artworkRendering != nil || gridWrap != nil
164
+ || gridTile != nil || imageShape != nil || accessorySymbol != nil
165
+ || cardTint != nil || cardImage != nil
166
+ else { return nil }
167
+ // Argument order is the generated init's (own properties before
168
+ // inherited ones — Nitro flattens `extends` own-props-first).
169
+ return SectionStyle(
170
+ gridWrap: gridWrap,
171
+ gridTile: gridTile,
172
+ display: display,
173
+ artworkRendering: artworkRendering,
174
+ imageShape: imageShape,
175
+ accessorySymbol: accessorySymbol,
176
+ cardTint: cardTint,
177
+ cardImage: cardImage,
178
+ )
179
+ }
180
+
181
+ /// Snapshot a live track block back to its JSON model (for persistence).
182
+ /// An all-nil block snapshots as no block — the decoders collapse it to
183
+ /// "no declaration" anyway, so never persist the distinction.
184
+ init?(_ style: TrackStyle?) {
185
+ guard let style,
186
+ style.display != nil || style.artworkRendering != nil
187
+ || style.imageShape != nil || style.accessorySymbol != nil
188
+ || style.cardTint != nil || style.cardImage != nil
189
+ else { return nil }
190
+ self.init(
191
+ display: Self.string(style.display),
192
+ artworkRendering: Self.string(style.artworkRendering),
193
+ imageShape: Self.string(style.imageShape),
194
+ accessorySymbol: style.accessorySymbol,
195
+ cardTint: style.cardTint,
196
+ cardImage: Self.string(style.cardImage),
197
+ )
198
+ }
199
+
200
+ private static func string(_ display: StyleDisplay?) -> String? {
201
+ switch display {
202
+ case .list: "list"
203
+ case .grid: "grid"
204
+ case nil: nil
205
+ }
206
+ }
207
+
208
+ private static func string(_ rendering: ArtworkRendering?) -> String? {
209
+ switch rendering {
210
+ case .original: "original"
211
+ case .stencil: "stencil"
212
+ case nil: nil
213
+ }
214
+ }
215
+
216
+ private static func string(_ shape: ImageShape?) -> String? {
217
+ switch shape {
218
+ case .circular: "circular"
219
+ case .roundedRectangle: "rounded-rectangle"
220
+ case nil: nil
221
+ }
222
+ }
223
+
224
+ private static func string(_ mode: CardImage?) -> String? {
225
+ switch mode {
226
+ case .normal: "normal"
227
+ case .background: "background"
228
+ case nil: nil
229
+ }
230
+ }
231
+ }
232
+
233
+ /// JSON model for a page section (ADR 0010). Legacy `groupTitle`/`imageRow`
234
+ /// keys in payloads are simply unknown to these models and decode as
235
+ /// ignored dead weight.
236
+ struct JsonSection: Codable {
237
+ let title: String?
238
+ let subtitle: String?
239
+ let style: JsonStyle?
240
+ let path: String?
241
+ let children: [JsonTrack]
242
+
243
+ init(
244
+ title: String? = nil,
245
+ subtitle: String? = nil,
246
+ style: JsonStyle? = nil,
247
+ path: String? = nil,
248
+ children: [JsonTrack],
89
249
  ) {
90
- self.id = id
91
- self.url = url
92
- self.src = src
93
- self.artwork = artwork
94
250
  self.title = title
95
- self.artist = artist
96
- self.album = album
97
- self.albumUrl = albumUrl
98
- self.live = live
99
- self.request = request
251
+ self.subtitle = subtitle
252
+ self.style = style
253
+ self.path = path
254
+ self.children = children
100
255
  }
101
256
  }
102
257
 
@@ -116,59 +271,59 @@ struct JsonTrackRequest: Codable {
116
271
  /// JSON model for resolved track (container with children).
117
272
  struct JsonResolvedTrack: Codable {
118
273
  let id: String?
119
- let url: String
274
+ let path: String
120
275
  let title: String
121
276
  let subtitle: String?
122
277
  let artwork: JsonArtwork?
123
278
  let artist: String?
124
- let albumUrl: String?
279
+ let albumPath: String?
125
280
  let album: String?
126
281
  let description: String?
127
282
  let genre: String?
128
283
  let duration: Double?
284
+ let sections: [JsonSection]?
129
285
  let children: [JsonTrack]?
130
286
  let src: String?
131
- let style: String?
132
- let childrenStyle: String?
133
- let groupTitle: String?
287
+ let style: JsonStyle?
288
+ let disabled: Bool?
134
289
  let live: Bool?
135
290
  let carPlaySiriListButton: String?
136
291
 
137
292
  init(
138
293
  id: String? = nil,
139
- url: String,
294
+ path: String,
140
295
  title: String,
141
296
  subtitle: String? = nil,
142
297
  artwork: JsonArtwork? = nil,
143
298
  artist: String? = nil,
144
- albumUrl: String? = nil,
299
+ albumPath: String? = nil,
145
300
  album: String? = nil,
146
301
  description: String? = nil,
147
302
  genre: String? = nil,
148
303
  duration: Double? = nil,
304
+ sections: [JsonSection]? = nil,
149
305
  children: [JsonTrack]? = nil,
150
306
  src: String? = nil,
151
- style: String? = nil,
152
- childrenStyle: String? = nil,
153
- groupTitle: String? = nil,
307
+ style: JsonStyle? = nil,
308
+ disabled: Bool? = nil,
154
309
  live: Bool? = nil,
155
310
  carPlaySiriListButton: String? = nil,
156
311
  ) {
157
- self.url = url
312
+ self.path = path
158
313
  self.title = title
159
314
  self.subtitle = subtitle
160
315
  self.artwork = artwork
161
316
  self.artist = artist
162
- self.albumUrl = albumUrl
317
+ self.albumPath = albumPath
163
318
  self.album = album
164
319
  self.description = description
165
320
  self.genre = genre
166
321
  self.duration = duration
322
+ self.sections = sections
167
323
  self.children = children
168
324
  self.src = src
169
325
  self.style = style
170
- self.childrenStyle = childrenStyle
171
- self.groupTitle = groupTitle
326
+ self.disabled = disabled
172
327
  self.live = live
173
328
  self.carPlaySiriListButton = carPlaySiriListButton
174
329
  self.id = id
@@ -178,50 +333,46 @@ struct JsonResolvedTrack: Codable {
178
333
  /// JSON model for individual tracks.
179
334
  struct JsonTrack: Codable {
180
335
  let id: String?
181
- let url: String?
336
+ let path: String?
182
337
  let title: String
183
338
  let subtitle: String?
184
339
  let artwork: JsonArtwork?
185
340
  let artist: String?
186
- let albumUrl: String?
341
+ let albumPath: String?
187
342
  let album: String?
188
343
  let description: String?
189
344
  let genre: String?
190
345
  let duration: Double?
191
346
  let src: String?
192
347
  let request: JsonTrackRequest?
193
- let style: String?
194
- let childrenStyle: String?
195
- let groupTitle: String?
348
+ let style: JsonStyle?
349
+ let disabled: Bool?
196
350
  let live: Bool?
197
- let imageRow: [JsonImageRowItem]?
198
351
 
199
352
  init(
200
353
  id: String? = nil,
201
- url: String? = nil,
354
+ path: String? = nil,
202
355
  title: String,
203
356
  subtitle: String? = nil,
204
357
  artwork: JsonArtwork? = nil,
205
358
  artist: String? = nil,
206
- albumUrl: String? = nil,
359
+ albumPath: String? = nil,
207
360
  album: String? = nil,
208
361
  description: String? = nil,
209
362
  genre: String? = nil,
210
363
  duration: Double? = nil,
211
364
  src: String? = nil,
212
365
  request: JsonTrackRequest? = nil,
213
- style: String? = nil,
214
- childrenStyle: String? = nil,
215
- groupTitle: String? = nil,
366
+ style: JsonStyle? = nil,
367
+ disabled: Bool? = nil,
216
368
  live: Bool? = nil,
217
- imageRow: [JsonImageRowItem]? = nil,
218
369
  ) {
219
- self.url = url
370
+ self.path = path
220
371
  self.title = title
221
372
  self.subtitle = subtitle
222
373
  self.artwork = artwork
223
374
  self.artist = artist
224
- self.albumUrl = albumUrl
375
+ self.albumPath = albumPath
225
376
  self.album = album
226
377
  self.description = description
227
378
  self.genre = genre
@@ -229,10 +380,8 @@ struct JsonTrack: Codable {
229
380
  self.src = src
230
381
  self.request = request
231
382
  self.style = style
232
- self.childrenStyle = childrenStyle
233
- self.groupTitle = groupTitle
383
+ self.disabled = disabled
234
384
  self.live = live
235
- self.imageRow = imageRow
236
385
  self.id = id
237
386
  }
238
387
  }
@@ -250,26 +399,27 @@ struct JsonTrack: Codable {
250
399
 
251
400
  extension JsonTrack {
252
401
  /// Snapshot the persistable subset of a live Track (inverse of `toNitro()`).
402
+ /// The track's own declared style block and `disabled` persist (ADR 0011);
403
+ /// section/page-inherited values are never stamped onto tracks and
404
+ /// re-resolve at render, exactly as they do live.
253
405
  init(from track: Track) {
254
406
  self.init(
255
407
  id: track.id,
256
- url: track.url,
408
+ path: track.path,
257
409
  title: track.title,
258
410
  subtitle: track.subtitle,
259
411
  artwork: JsonArtwork(track.artwork),
260
412
  artist: track.artist,
261
- albumUrl: track.albumUrl,
413
+ albumPath: track.albumPath,
262
414
  album: track.album,
263
415
  description: track.description,
264
416
  genre: track.genre,
265
417
  duration: track.duration,
266
418
  src: track.src,
267
419
  request: track.request.map(JsonTrackRequest.init(from:)),
268
- style: nil,
269
- childrenStyle: nil,
270
- groupTitle: track.groupTitle,
420
+ style: JsonStyle(track.style),
421
+ disabled: track.disabled,
271
422
  live: track.live,
272
- imageRow: nil,
273
423
  )
274
424
  }
275
425
  }
@@ -289,14 +439,16 @@ struct JsonTrack: Codable {
289
439
  init(from track: Track) {
290
440
  self.init(
291
441
  id: track.id,
292
- url: track.url,
442
+ path: track.path,
293
443
  title: track.title,
294
444
  artwork: JsonArtwork(track.artwork),
295
445
  artist: track.artist,
296
- albumUrl: track.albumUrl,
446
+ albumPath: track.albumPath,
297
447
  album: track.album,
298
448
  src: track.src,
299
449
  request: track.request.map(JsonTrackRequest.init(from:)),
450
+ style: JsonStyle(track.style),
451
+ disabled: track.disabled,
300
452
  live: track.live,
301
453
  )
302
454
  }
@@ -308,26 +460,14 @@ struct JsonTrack: Codable {
308
460
 
309
461
  #if canImport(NitroModules)
310
462
 
311
- private extension String {
312
- func toTrackStyle() -> TrackStyle? {
313
- TrackStyle(fromString: lowercased())
314
- }
315
- }
316
-
317
- extension JsonImageRowItem {
318
- func toNitro() -> ImageRowItem {
319
- ImageRowItem(
320
- id: id,
321
- url: url,
322
- src: src,
323
- artwork: artwork,
324
- artworkSource: nil,
463
+ extension JsonSection {
464
+ func toNitro() -> Section {
465
+ Section(
325
466
  title: title,
326
- artist: artist,
327
- album: album,
328
- albumUrl: albumUrl,
329
- live: live,
330
- request: request?.toNitro(),
467
+ subtitle: subtitle,
468
+ style: style?.toSectionStyle(),
469
+ path: path,
470
+ children: children.map { $0.toNitro() },
331
471
  )
332
472
  }
333
473
  }
@@ -335,28 +475,26 @@ struct JsonTrack: Codable {
335
475
  extension JsonResolvedTrack {
336
476
  func toNitro() -> ResolvedTrack {
337
477
  ResolvedTrack(
338
- url: url,
478
+ path: path,
479
+ style: style?.toSectionStyle(),
480
+ sections: sections?.map { $0.toNitro() },
339
481
  children: children?.map { $0.toNitro() },
340
482
  carPlaySiriListButton: carPlaySiriListButton.flatMap { CarPlaySiriListButtonPosition(fromString: $0) },
341
483
  id: id,
342
484
  src: src,
343
485
  artwork: artwork?.toNitro(),
344
486
  artworkSource: nil, request: nil,
345
- artworkCarPlayTinted: nil,
346
487
  title: title,
347
488
  subtitle: subtitle,
348
489
  artist: artist,
349
- albumUrl: albumUrl,
490
+ albumPath: albumPath,
350
491
  album: album,
351
492
  description: description,
352
493
  genre: genre,
353
494
  duration: duration,
354
- style: style?.toTrackStyle(),
355
- childrenStyle: childrenStyle?.toTrackStyle(),
495
+ disabled: disabled,
356
496
  favorited: nil,
357
- groupTitle: groupTitle,
358
497
  live: live,
359
- imageRow: nil,
360
498
  )
361
499
  }
362
500
  }
@@ -365,26 +503,23 @@ struct JsonTrack: Codable {
365
503
  func toNitro() -> Track {
366
504
  Track(
367
505
  id: id,
368
- url: url,
506
+ path: path,
369
507
  src: src,
370
508
  artwork: artwork?.toNitro(),
371
509
  artworkSource: nil,
372
510
  request: request?.toNitro(),
373
- artworkCarPlayTinted: nil,
374
511
  title: title,
375
512
  subtitle: subtitle,
376
513
  artist: artist,
377
- albumUrl: albumUrl,
514
+ albumPath: albumPath,
378
515
  album: album,
379
516
  description: description,
380
517
  genre: genre,
381
518
  duration: duration,
382
519
  style: style?.toTrackStyle(),
383
- childrenStyle: childrenStyle?.toTrackStyle(),
520
+ disabled: disabled,
384
521
  favorited: nil,
385
- groupTitle: groupTitle,
386
522
  live: live,
387
- imageRow: imageRow?.map { $0.toNitro() },
388
523
  )
389
524
  }
390
525
  }
@@ -396,15 +531,29 @@ struct JsonTrack: Codable {
396
531
  func toNitro() -> Track {
397
532
  Track(
398
533
  id: id ?? "",
399
- url: url,
534
+ path: path,
400
535
  src: src,
536
+ artwork: artwork?.toNitro(),
401
537
  request: request.map { TrackRequest(userAgent: $0.userAgent, headers: $0.headers, query: $0.query) },
402
538
  title: title,
403
539
  artist: artist,
404
- albumUrl: albumUrl,
540
+ albumPath: albumPath,
405
541
  album: album,
542
+ style: style?.toTrackStyle(),
543
+ disabled: disabled,
406
544
  live: live,
407
- artwork: artwork?.toNitro(),
545
+ )
546
+ }
547
+ }
548
+
549
+ extension JsonSection {
550
+ func toNitro() -> Section {
551
+ Section(
552
+ title: title,
553
+ subtitle: subtitle,
554
+ style: style?.toSectionStyle(),
555
+ path: path,
556
+ children: children.map { $0.toNitro() },
408
557
  )
409
558
  }
410
559
  }
@@ -412,10 +561,13 @@ struct JsonTrack: Codable {
412
561
  extension JsonResolvedTrack {
413
562
  func toNitro() -> ResolvedTrack {
414
563
  ResolvedTrack(
415
- url: url,
564
+ path: path,
565
+ style: style?.toSectionStyle(),
566
+ sections: sections?.map { $0.toNitro() },
416
567
  children: children?.map { $0.toNitro() },
417
568
  artwork: artwork?.toNitro(),
418
569
  title: title,
570
+ disabled: disabled,
419
571
  )
420
572
  }
421
573
  }
@@ -1,18 +1,18 @@
1
1
  import Foundation
2
2
 
3
3
  /// A search result that may or may not be directly playable. `src` is the
4
- /// playable stream; `url` is a browsable container path. Abstracting these two
4
+ /// playable stream; `path` is a browsable container path. Abstracting these two
5
5
  /// fields keeps the drill-in decision free of the Nitro `Track` type so it can
6
6
  /// be unit-tested.
7
7
  protocol PlayableSearchItem {
8
8
  var src: String? { get }
9
- var url: String? { get }
9
+ var path: String? { get }
10
10
  }
11
11
 
12
12
  enum SearchDrillIn {
13
13
  /// Picks the playable items for a "play «X»" voice search.
14
14
  ///
15
- /// If the first result is browsable-only (a `url` container with no `src` — a
15
+ /// If the first result is browsable-only (a `path` container with no `src` — a
16
16
  /// place/genre page), drills into it via `resolveChildren` and uses its
17
17
  /// playable children, so we play the first station *inside* the page rather
18
18
  /// than skipping the page for a later inline station. Falls back to the flat
@@ -25,8 +25,8 @@ enum SearchDrillIn {
25
25
  guard !children.isEmpty else { return nil }
26
26
 
27
27
  let candidates: [Item]
28
- if let first = children.first, first.src == nil, let url = first.url {
29
- let drilled = try await resolveChildren(url).filter { $0.src != nil }
28
+ if let first = children.first, first.src == nil, let path = first.path {
29
+ let drilled = try await resolveChildren(path).filter { $0.src != nil }
30
30
  candidates = drilled.isEmpty ? children : drilled
31
31
  } else {
32
32
  candidates = children
@@ -37,7 +37,7 @@ enum SearchDrillIn {
37
37
  }
38
38
  }
39
39
 
40
- // The real Nitro `Track` already carries `src`/`url`; conform it for the app
40
+ // The real Nitro `Track` already carries `src`/`path`; conform it for the app
41
41
  // build. (The testable target builds without NitroModules and uses a stub item.)
42
42
  #if canImport(NitroModules)
43
43
  extension Track: PlayableSearchItem {}
@@ -0,0 +1,64 @@
1
+ #if canImport(NitroModules)
2
+ import NitroModules
3
+ #endif
4
+
5
+ /// The rendered form of a section — the single place where (resolved style,
6
+ /// platform capability) maps to what CarPlay draws. Degradation drops
7
+ /// decorations before layout (ADR 0011): a *wrapping* grid can only reach the
8
+ /// tile path where the platform has a wrapping tile container (CarPlay:
9
+ /// iOS 26+'s element API); without one it becomes a list, where every item
10
+ /// stays reachable instead of truncating at an unknowable width. A
11
+ /// single-line grid (`gridWrap: false`) renders everywhere — the legacy image
12
+ /// row IS the single-line presentation.
13
+ enum SectionPresentation: Equatable {
14
+ case list
15
+ case singleLineRow
16
+ case wrappingGrid
17
+
18
+ /// - Parameter supportsWrappingGrid: whether the platform has a wrapping
19
+ /// tile container. Injected (rather than read via `#available` here) so
20
+ /// the mapping is testable off-device.
21
+ init(for style: SectionStyle?, supportsWrappingGrid: Bool) {
22
+ guard style?.display == .grid else {
23
+ self = .list
24
+ return
25
+ }
26
+ if style?.gridWrap == false {
27
+ self = .singleLineRow
28
+ } else if supportsWrappingGrid {
29
+ self = .wrappingGrid
30
+ } else {
31
+ self = .list
32
+ }
33
+ }
34
+
35
+ /// The accessory symbol a resolved block actually draws: nil when
36
+ /// undeclared, and nil for the `'none'` sentinel — the inheritance escape
37
+ /// that restores the derived accessory (a browsable row's chevron). Here,
38
+ /// not in the CarPlay factory, so the sentinel rule is testable off-device.
39
+ static func effectiveAccessorySymbol(_ style: TrackStyle?) -> String? {
40
+ guard let symbol = style?.accessorySymbol, symbol != "none" else { return nil }
41
+ return symbol
42
+ }
43
+
44
+ /// The element family a tile presentation renders on iOS 26+ — here, not
45
+ /// in the CarPlay factory, so the style→family mapping is testable
46
+ /// off-device. `gridTile` picks the family (plain by default); the plain
47
+ /// single-line shelf keeps row elements, the one plain element with a
48
+ /// subtitle slot. Pre-26 has no element API at all: the treatment drops
49
+ /// with the legacy image row (decorations drop before layout).
50
+ enum TileElementFamily: Equatable {
51
+ case rowElements
52
+ case imageGridElements
53
+ case cardElements
54
+ case condensedElements
55
+ }
56
+
57
+ static func tileFamily(for style: SectionStyle?, singleLine: Bool) -> TileElementFamily {
58
+ switch style?.gridTile {
59
+ case .card: .cardElements
60
+ case .condensed: .condensedElements
61
+ case .plain, nil: singleLine ? .rowElements : .imageGridElements
62
+ }
63
+ }
64
+ }