react-native-audio-browser 0.2.0-next.21 → 0.2.0-next.23

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 (29) hide show
  1. package/README.md +1 -1
  2. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +28 -7
  3. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +2 -0
  4. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +32 -8
  5. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +21 -0
  6. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +6 -0
  7. package/ios/Browser/InertStyleDiagnostic.swift +25 -8
  8. package/ios/Browser/SectionPresentation.swift +18 -5
  9. package/ios/CarPlay/CPInterfaceController+Safe.swift +93 -23
  10. package/ios/CarPlay/CarPlayListItemFactory.swift +74 -54
  11. package/ios/CarPlay/TopTemplateReplacer.swift +96 -0
  12. package/ios/Model/NitroTypeStubs.swift +3 -0
  13. package/ios/Tests/InertStyleDiagnosticTests.swift +17 -8
  14. package/ios/Tests/JsonModelsTests.swift +14 -0
  15. package/ios/Tests/SectionPresentationTests.swift +21 -13
  16. package/ios/Tests/StyleResolverTests.swift +5 -0
  17. package/ios/Tests/TopTemplateReplacerTests.swift +172 -0
  18. package/lib/typescript/src/types/browser-nodes.d.ts +28 -21
  19. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  20. package/nitrogen/generated/android/c++/JGridTile.hpp +3 -0
  21. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +3 -2
  22. package/nitrogen/generated/ios/swift/GridTile.swift +4 -0
  23. package/nitrogen/generated/shared/c++/GridTile.hpp +6 -2
  24. package/package.json +1 -1
  25. package/src/types/browser-nodes.ts +28 -21
  26. package/website/guide/artwork.md +1 -1
  27. package/website/guide/browser.md +11 -5
  28. package/website/guide/carplay.md +30 -0
  29. package/website/guide/track.md +1 -1
@@ -23,7 +23,7 @@ export type StyleDisplay = 'list' | 'grid';
23
23
  */
24
24
  export type ArtworkRendering = 'original' | 'stencil';
25
25
  /**
26
- * The shape a grid tile's artwork is cropped to.
26
+ * The shape an image or condensed grid tile's artwork is cropped to.
27
27
  *
28
28
  * - `'rounded-rectangle'` (default) — the standard tile crop.
29
29
  * - `'circular'` — the standard presentation for *people* (artists, hosts,
@@ -38,26 +38,31 @@ export type ImageShape = 'circular' | 'rounded-rectangle';
38
38
  /**
39
39
  * The kind of tile a grid renders.
40
40
  *
41
- * - `'plain'` (default) — artwork tiles with a title.
41
+ * - `'plain'` (default) — large artwork tiles with a title and subtitle
42
+ * below. No shape and no accessory.
43
+ * - `'image'` — artwork tiles with a title and no subtitle; takes
44
+ * {@link TrackStyle.imageShape} and {@link TrackStyle.accessorySymbol}.
45
+ * The grid of round tiles for people is `'image'` with
46
+ * `imageShape: 'circular'`.
42
47
  * - `'card'` — a larger card with title/subtitle and a
43
48
  * {@link TrackStyle.cardTint} color; its image mode is
44
- * {@link TrackStyle.cardImage}. Cards have no shape.
45
- * - `'condensed'` — a denser cell carrying title + subtitle; takes
46
- * {@link TrackStyle.imageShape} and {@link TrackStyle.accessorySymbol}
47
- * like a plain tile.
49
+ * {@link TrackStyle.cardImage}. Cards have no shape and no accessory.
50
+ * - `'condensed'` — a denser cell carrying title + subtitle beside a small
51
+ * image; takes {@link TrackStyle.imageShape} and
52
+ * {@link TrackStyle.accessorySymbol}.
48
53
  *
49
54
  * Element families are mutually exclusive within a container, so this is
50
55
  * a container property — hence the enum, and the `grid` prefix.
51
56
  */
52
- export type GridTile = 'plain' | 'card' | 'condensed';
57
+ export type GridTile = 'plain' | 'image' | 'card' | 'condensed';
53
58
  /**
54
59
  * How a card tile uses its image.
55
60
  *
56
- * - `'normal'` (default) — image above the labels;
57
- * {@link TrackStyle.cardTint} colors a gradient behind the labels.
61
+ * - `'normal'` (default) — a square image inset above the labels;
62
+ * {@link TrackStyle.cardTint} colors the whole card.
58
63
  * - `'background'` — the image fills the card full-height with the labels
59
- * overlaid; `cardTint` becomes the color behind them. Selects the larger
60
- * artwork target size.
64
+ * overlaid; `cardTint` fades in behind them. Selects the larger artwork
65
+ * target size.
61
66
  *
62
67
  * An enum rather than a boolean: it names the card's image mode and
63
68
  * leaves room for future modes.
@@ -120,12 +125,13 @@ export interface TrackStyle {
120
125
  */
121
126
  artworkRendering?: ArtworkRendering;
122
127
  /**
123
- * Inherited (`track ?? section ?? page`): the shape a grid tile's
124
- * artwork is cropped to — `'circular'` for people, the default
125
- * `'rounded-rectangle'` for everything else. Shape follows the item
126
- * wherever it travels: a circular artist stays circular in whatever
127
- * grid renders it next. Inert where the presentation has no shape
128
- * (list rows, pre-26 CarPlay, Android Auto).
128
+ * Inherited (`track ?? section ?? page`): the shape an image or
129
+ * condensed grid tile's artwork is cropped to — `'circular'` for people,
130
+ * the default `'rounded-rectangle'` for everything else. Shape follows
131
+ * the item wherever it travels: a circular artist stays circular in
132
+ * whatever shaped grid renders it next. Inert where the presentation has
133
+ * no shape (list rows, plain and card tiles, pre-26 CarPlay, Android
134
+ * Auto).
129
135
  *
130
136
  * @default 'rounded-rectangle'
131
137
  *
@@ -135,8 +141,9 @@ export interface TrackStyle {
135
141
  /**
136
142
  * Inherited (`track ?? section ?? page`): an SF Symbol name drawn as
137
143
  * the item's accessory wherever the current presentation supports one —
138
- * the trailing edge of a list row, leading the title on an iOS 26 grid
139
- * tile (placement is the platform's choice per surface). A resolved
144
+ * the trailing edge of a list row or an iOS 26 condensed tile, leading
145
+ * the title on an image tile (placement is the platform's choice per
146
+ * surface); plain and card tiles have no slot for one. A resolved
140
147
  * symbol replaces the derived accessory (the disclosure chevron a
141
148
  * browsable row gets); `'none'` clears an inherited value, restoring
142
149
  * the derived behavior — inheritance has no other escape, and no SF
@@ -148,8 +155,8 @@ export interface TrackStyle {
148
155
  accessorySymbol?: string;
149
156
  /**
150
157
  * Inherited (`track ?? section ?? page`): the card treatment's tint
151
- * color (hex, e.g. `'#1e3a8a'`) — a gradient behind the labels, or the
152
- * color behind them when {@link TrackStyle.cardImage} is
158
+ * color (hex, e.g. `'#1e3a8a'`) — the card's own color, or the color
159
+ * that fades in behind the labels when {@link TrackStyle.cardImage} is
153
160
  * `'background'`. Card-prefixed: inert unless the container renders
154
161
  * `gridTile: 'card'`.
155
162
  *
@@ -1 +1 @@
1
- {"version":3,"file":"browser-nodes.d.ts","sourceRoot":"","sources":["../../../../src/types/browser-nodes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-nodes.d.ts","sourceRoot":"","sources":["../../../../src/types/browser-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE1E;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAA;AAE1C;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,CAAA;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,mBAAmB,CAAA;AAEzD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAA;AAE/D;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,YAAY,CAAA;AAE/C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IAEtB;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,UAAU,CAAA;IAEvB;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAA;IACX,2CAA2C;IAC3C,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAA;CACb;AAOD,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,eAAe,CAAA;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB;;;;;;;;;;;;;;;OAeG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4BAA4B;IAC5B,QAAQ,EAAE,KAAK,EAAE,CAAA;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,KAAK;IACpB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;IAEX;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IAEtB;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,WAAW,CAAA;IAEpC,uFAAuF;IACvF,OAAO,CAAC,EAAE,YAAY,CAAA;IAGtB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAA;IAEb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,UAAU,CAAA;IAElB;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,aAAc,SAAQ,KAAK;IAC1C;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;IAEpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IAEpB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAA;IAElB;;;;;;;;;;;;OAYG;IACH,qBAAqB,CAAC,EAAE,6BAA6B,CAAA;CACtD"}
@@ -45,6 +45,9 @@ namespace margelo::nitro::audiobrowser {
45
45
  case GridTile::PLAIN:
46
46
  static const auto fieldPLAIN = clazz->getStaticField<JGridTile>("PLAIN");
47
47
  return clazz->getStaticFieldValue(fieldPLAIN);
48
+ case GridTile::IMAGE:
49
+ static const auto fieldIMAGE = clazz->getStaticField<JGridTile>("IMAGE");
50
+ return clazz->getStaticFieldValue(fieldIMAGE);
48
51
  case GridTile::CARD:
49
52
  static const auto fieldCARD = clazz->getStaticField<JGridTile>("CARD");
50
53
  return clazz->getStaticFieldValue(fieldCARD);
@@ -17,8 +17,9 @@ import com.facebook.proguard.annotations.DoNotStrip
17
17
  @Keep
18
18
  enum class GridTile(@DoNotStrip @Keep val value: Int) {
19
19
  PLAIN(0),
20
- CARD(1),
21
- CONDENSED(2);
20
+ IMAGE(1),
21
+ CARD(2),
22
+ CONDENSED(3);
22
23
 
23
24
  companion object
24
25
  }
@@ -19,6 +19,8 @@ public extension GridTile {
19
19
  switch string {
20
20
  case "plain":
21
21
  self = .plain
22
+ case "image":
23
+ self = .image
22
24
  case "card":
23
25
  self = .card
24
26
  case "condensed":
@@ -35,6 +37,8 @@ public extension GridTile {
35
37
  switch self {
36
38
  case .plain:
37
39
  return "plain"
40
+ case .image:
41
+ return "image"
38
42
  case .card:
39
43
  return "card"
40
44
  case .condensed:
@@ -30,8 +30,9 @@ namespace margelo::nitro::audiobrowser {
30
30
  */
31
31
  enum class GridTile {
32
32
  PLAIN SWIFT_NAME(plain) = 0,
33
- CARD SWIFT_NAME(card) = 1,
34
- CONDENSED SWIFT_NAME(condensed) = 2,
33
+ IMAGE SWIFT_NAME(image) = 1,
34
+ CARD SWIFT_NAME(card) = 2,
35
+ CONDENSED SWIFT_NAME(condensed) = 3,
35
36
  } CLOSED_ENUM;
36
37
 
37
38
  } // namespace margelo::nitro::audiobrowser
@@ -45,6 +46,7 @@ namespace margelo::nitro {
45
46
  std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
46
47
  switch (hashString(unionValue.c_str(), unionValue.size())) {
47
48
  case hashString("plain"): return margelo::nitro::audiobrowser::GridTile::PLAIN;
49
+ case hashString("image"): return margelo::nitro::audiobrowser::GridTile::IMAGE;
48
50
  case hashString("card"): return margelo::nitro::audiobrowser::GridTile::CARD;
49
51
  case hashString("condensed"): return margelo::nitro::audiobrowser::GridTile::CONDENSED;
50
52
  default: [[unlikely]]
@@ -54,6 +56,7 @@ namespace margelo::nitro {
54
56
  static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::audiobrowser::GridTile arg) {
55
57
  switch (arg) {
56
58
  case margelo::nitro::audiobrowser::GridTile::PLAIN: return JSIConverter<std::string>::toJSI(runtime, "plain");
59
+ case margelo::nitro::audiobrowser::GridTile::IMAGE: return JSIConverter<std::string>::toJSI(runtime, "image");
57
60
  case margelo::nitro::audiobrowser::GridTile::CARD: return JSIConverter<std::string>::toJSI(runtime, "card");
58
61
  case margelo::nitro::audiobrowser::GridTile::CONDENSED: return JSIConverter<std::string>::toJSI(runtime, "condensed");
59
62
  default: [[unlikely]]
@@ -68,6 +71,7 @@ namespace margelo::nitro {
68
71
  std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
69
72
  switch (hashString(unionValue.c_str(), unionValue.size())) {
70
73
  case hashString("plain"):
74
+ case hashString("image"):
71
75
  case hashString("card"):
72
76
  case hashString("condensed"):
73
77
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-browser",
3
- "version": "0.2.0-next.21",
3
+ "version": "0.2.0-next.23",
4
4
  "description": "react-native-audio-browser is a react native package built with Nitro",
5
5
  "keywords": [
6
6
  "react-native",
@@ -26,7 +26,7 @@ export type StyleDisplay = 'list' | 'grid'
26
26
  export type ArtworkRendering = 'original' | 'stencil'
27
27
 
28
28
  /**
29
- * The shape a grid tile's artwork is cropped to.
29
+ * The shape an image or condensed grid tile's artwork is cropped to.
30
30
  *
31
31
  * - `'rounded-rectangle'` (default) — the standard tile crop.
32
32
  * - `'circular'` — the standard presentation for *people* (artists, hosts,
@@ -42,27 +42,32 @@ export type ImageShape = 'circular' | 'rounded-rectangle'
42
42
  /**
43
43
  * The kind of tile a grid renders.
44
44
  *
45
- * - `'plain'` (default) — artwork tiles with a title.
45
+ * - `'plain'` (default) — large artwork tiles with a title and subtitle
46
+ * below. No shape and no accessory.
47
+ * - `'image'` — artwork tiles with a title and no subtitle; takes
48
+ * {@link TrackStyle.imageShape} and {@link TrackStyle.accessorySymbol}.
49
+ * The grid of round tiles for people is `'image'` with
50
+ * `imageShape: 'circular'`.
46
51
  * - `'card'` — a larger card with title/subtitle and a
47
52
  * {@link TrackStyle.cardTint} color; its image mode is
48
- * {@link TrackStyle.cardImage}. Cards have no shape.
49
- * - `'condensed'` — a denser cell carrying title + subtitle; takes
50
- * {@link TrackStyle.imageShape} and {@link TrackStyle.accessorySymbol}
51
- * like a plain tile.
53
+ * {@link TrackStyle.cardImage}. Cards have no shape and no accessory.
54
+ * - `'condensed'` — a denser cell carrying title + subtitle beside a small
55
+ * image; takes {@link TrackStyle.imageShape} and
56
+ * {@link TrackStyle.accessorySymbol}.
52
57
  *
53
58
  * Element families are mutually exclusive within a container, so this is
54
59
  * a container property — hence the enum, and the `grid` prefix.
55
60
  */
56
- export type GridTile = 'plain' | 'card' | 'condensed'
61
+ export type GridTile = 'plain' | 'image' | 'card' | 'condensed'
57
62
 
58
63
  /**
59
64
  * How a card tile uses its image.
60
65
  *
61
- * - `'normal'` (default) — image above the labels;
62
- * {@link TrackStyle.cardTint} colors a gradient behind the labels.
66
+ * - `'normal'` (default) — a square image inset above the labels;
67
+ * {@link TrackStyle.cardTint} colors the whole card.
63
68
  * - `'background'` — the image fills the card full-height with the labels
64
- * overlaid; `cardTint` becomes the color behind them. Selects the larger
65
- * artwork target size.
69
+ * overlaid; `cardTint` fades in behind them. Selects the larger artwork
70
+ * target size.
66
71
  *
67
72
  * An enum rather than a boolean: it names the card's image mode and
68
73
  * leaves room for future modes.
@@ -128,12 +133,13 @@ export interface TrackStyle {
128
133
  artworkRendering?: ArtworkRendering
129
134
 
130
135
  /**
131
- * Inherited (`track ?? section ?? page`): the shape a grid tile's
132
- * artwork is cropped to — `'circular'` for people, the default
133
- * `'rounded-rectangle'` for everything else. Shape follows the item
134
- * wherever it travels: a circular artist stays circular in whatever
135
- * grid renders it next. Inert where the presentation has no shape
136
- * (list rows, pre-26 CarPlay, Android Auto).
136
+ * Inherited (`track ?? section ?? page`): the shape an image or
137
+ * condensed grid tile's artwork is cropped to — `'circular'` for people,
138
+ * the default `'rounded-rectangle'` for everything else. Shape follows
139
+ * the item wherever it travels: a circular artist stays circular in
140
+ * whatever shaped grid renders it next. Inert where the presentation has
141
+ * no shape (list rows, plain and card tiles, pre-26 CarPlay, Android
142
+ * Auto).
137
143
  *
138
144
  * @default 'rounded-rectangle'
139
145
  *
@@ -144,8 +150,9 @@ export interface TrackStyle {
144
150
  /**
145
151
  * Inherited (`track ?? section ?? page`): an SF Symbol name drawn as
146
152
  * the item's accessory wherever the current presentation supports one —
147
- * the trailing edge of a list row, leading the title on an iOS 26 grid
148
- * tile (placement is the platform's choice per surface). A resolved
153
+ * the trailing edge of a list row or an iOS 26 condensed tile, leading
154
+ * the title on an image tile (placement is the platform's choice per
155
+ * surface); plain and card tiles have no slot for one. A resolved
149
156
  * symbol replaces the derived accessory (the disclosure chevron a
150
157
  * browsable row gets); `'none'` clears an inherited value, restoring
151
158
  * the derived behavior — inheritance has no other escape, and no SF
@@ -158,8 +165,8 @@ export interface TrackStyle {
158
165
 
159
166
  /**
160
167
  * Inherited (`track ?? section ?? page`): the card treatment's tint
161
- * color (hex, e.g. `'#1e3a8a'`) — a gradient behind the labels, or the
162
- * color behind them when {@link TrackStyle.cardImage} is
168
+ * color (hex, e.g. `'#1e3a8a'`) — the card's own color, or the color
169
+ * that fades in behind the labels when {@link TrackStyle.cardImage} is
163
170
  * `'background'`. Card-prefixed: inert unless the container renders
164
171
  * `gridTile: 'card'`.
165
172
  *
@@ -238,7 +238,7 @@ A page's [`Section`](/api/types/browser-nodes/#section) can render its tracks as
238
238
  }
239
239
  ```
240
240
 
241
- Each tile is an ordinary Track, so its `artwork` runs through the same pipeline as any other. Tile styles presume artwork: a track without any renders as a placeholder tile plus its title — the artwork `resolve` hook is the place to supply fallback art. On CarPlay a single-line grid (`gridWrap: false`) shows the tiles that fit (roughly eight, width-dependent; the rest are truncated) and a wrapping grid takes as many lines as it needs on iOS 26+ (rendering as a list before that); Android Auto's grid always wraps, so it renders both identically. See [Browser → Presentation](/guide/browser#presentation) for the full per-surface rundown.
241
+ Each tile is an ordinary Track, so its `artwork` runs through the same pipeline as any other. Tile styles presume artwork: a track without any renders as a placeholder tile plus its title — the artwork `resolve` hook is the place to supply fallback art. On CarPlay a single-line grid (`gridWrap: false`) shows the tiles that fit on one line (seven on a wide screen, or two condensed cells; the rest are truncated) and a wrapping grid takes as many lines as it needs on iOS 26+ (rendering as a list before that); Android Auto's grid always wraps, so it renders both identically. See [Browser → Presentation](/guide/browser#presentation) for the full per-surface rundown.
242
242
 
243
243
  ## API summary
244
244
 
@@ -521,9 +521,9 @@ Presentation lives in `style` blocks, separate from content, plus a few Track fi
521
521
  | `style` (same block) | a page | declaration for the whole page — a section overrides it for its own children | all |
522
522
  | `style: { display: 'grid' }` | a browsable track | the layout of the page it opens — Android Auto reads it from the parent item | Android Auto / AAOS |
523
523
  | `style: { artworkRendering: 'stencil' }` | a track / section / page | tint monochrome artwork to the surface appearance | CarPlay |
524
- | `style: { imageShape: 'circular' }` | a track / section / page | crop grid-tile artwork circular (people) instead of rounded | CarPlay (iOS 26+) |
525
- | `style: { accessorySymbol: 'lock.fill' }` | a track / section / page | SF Symbol accessory on rows and tiles (`'none'` restores the derived one) | CarPlay |
526
- | `style: { gridTile: 'card' }` | a section / page | the grid's tile kind: `'plain'` (default), `'card'`, or `'condensed'` | CarPlay (iOS 26+) |
524
+ | `style: { imageShape: 'circular' }` | a track / section / page | crop `'image'` and `'condensed'` tile artwork circular (people) | CarPlay (iOS 26+) |
525
+ | `style: { accessorySymbol: 'lock.fill' }` | a track / section / page | SF Symbol accessory on rows and on `'image'` / `'condensed'` tiles | CarPlay |
526
+ | `style: { gridTile: 'card' }` | a section / page | the grid's tile kind: `'plain'` (default), `'image'`, `'card'`, or `'condensed'` | CarPlay (iOS 26+) |
527
527
  | `style: { cardTint: '#1e3a8a' }` (+ `cardImage`) | a track / section / page | card color and image mode (`'background'` fills the card full-height) | CarPlay (iOS 26+) |
528
528
  | `disabled: true` | a track | unavailable: never plays; grayed where drawable, hidden elsewhere | all |
529
529
  | `artwork: 'sf:heart.fill'` | any item | SF Symbol icon (supports `?bg=…&fg=…`) | iOS |
@@ -532,11 +532,11 @@ Presentation lives in `style` blocks, separate from content, plus a few Track fi
532
532
  | `albumPath` + `resolveAlbumPath` | track + config | make the now-playing album line tappable | CarPlay |
533
533
 
534
534
  ::: info Caveats from the table
535
- **`albumPath` requires `album`** — CarPlay renders the tappable line from the album metadata, so without an `album` there is no line to tap. **\*Layouts render each platform's nearest supported form** — a single-line grid (`gridWrap: false`) on CarPlay shows the tiles that fit (roughly eight, width-dependent) and truncates the rest; a wrapping grid takes as many lines as it needs on iOS 26+ and renders as a list before that. Android Auto's grid always wraps, so it shows more, never less. No car surface scrolls horizontally; an app UI typically renders `gridWrap: false` as a horizontal scroller. Truncation is visual only: tapping a tile queues the whole section (see [Playback behavior](#playback-behavior)). Item properties like `artworkRendering`, `imageShape`, and `accessorySymbol` are inherited — `track ?? section ?? page`, per property — so a section-wide value can be overridden per item; `accessorySymbol: 'none'` is the per-item escape from an inherited symbol, and where the accessory draws is the platform's choice per surface (trailing on list rows, leading the title on tiles). `display` is never inherited: it always describes the children of whatever it's set on. On a page or section it lays out that page or section; on a browsable track it declares the layout of the page that track opens — Android Auto needs it there, or that page renders as a list. `gridWrap` is inert unless `display` is `'grid'`. One AAOS caveat: AOSP-based Automotive units honor only the parent item's `display`, so a page mixing grid and list sections renders uniformly there — see [Android Auto → Browse display](/guide/android-auto#browse-display).
535
+ **`albumPath` requires `album`** — CarPlay renders the tappable line from the album metadata, so without an `album` there is no line to tap. **\*Layouts render each platform's nearest supported form** — a single-line grid (`gridWrap: false`) on CarPlay shows the tiles that fit on one line (seven on a wide screen, or two condensed cells) and truncates the rest; a wrapping grid takes as many lines as it needs on iOS 26+ and renders as a list before that. Android Auto's grid always wraps, so it shows more, never less. No car surface scrolls horizontally; an app UI typically renders `gridWrap: false` as a horizontal scroller. Truncation is visual only: tapping a tile queues the whole section (see [Playback behavior](#playback-behavior)). Item properties like `artworkRendering`, `imageShape`, and `accessorySymbol` are inherited — `track ?? section ?? page`, per property — so a section-wide value can be overridden per item; `accessorySymbol: 'none'` is the per-item escape from an inherited symbol, and where the accessory draws is the platform's choice per surface (trailing on list rows and condensed tiles, leading the title on image tiles). On an iOS 26 grid, which tile draws what is `gridTile`'s choice: `'plain'` has a subtitle but no shape and no accessory, `'image'` has a shape and an accessory but no subtitle, `'condensed'` has all three beside a small image, and `'card'` has a subtitle, a tint and an image mode. `display` is never inherited: it always describes the children of whatever it's set on. On a page or section it lays out that page or section; on a browsable track it declares the layout of the page that track opens — Android Auto needs it there, or that page renders as a list. `gridWrap` is inert unless `display` is `'grid'`. One AAOS caveat: AOSP-based Automotive units honor only the parent item's `display`, so a page mixing grid and list sections renders uniformly there — see [Android Auto → Browse display](/guide/android-auto#browse-display).
536
536
  :::
537
537
 
538
538
  ::: tip Inert declarations are logged in debug builds
539
- A combination that can never render — `gridWrap` or `gridTile` outside a grid, `cardTint` with no card treatment in scope, `imageShape` on a list or a card, `accessorySymbol` on a card, `display` on a track that renders playable — is inert, not an error, so nothing breaks. In a **debug build**, each one is logged once per page resolution (`Timber` on Android, `os.log` on iOS) naming the section, the effective style, and the fix:
539
+ A combination that can never render — `gridWrap` or `gridTile` outside a grid, `cardTint` with no card treatment in scope, `imageShape` anywhere but an image or condensed grid, `accessorySymbol` on a plain or card grid, `display` on a track that renders playable — is inert, not an error, so nothing breaks. In a **debug build**, each one is logged once per page resolution (`Timber` on Android, `os.log` on iOS) naming the section, the effective style, and the fix:
540
540
 
541
541
  ```
542
542
  Inert style declaration on page '/home': `style.gridWrap` on section 'Featured'
@@ -557,6 +557,8 @@ A property a _surface_ can't draw is never reported — that's the aspirational
557
557
  title: 'Home',
558
558
  sections: [
559
559
  { title: 'Popular', style: { display: 'grid', gridWrap: false }, path: '/popular', children: [...] },
560
+ // Round tiles for people: the image tile is the one with a shape.
561
+ { title: 'Artists', style: { display: 'grid', gridTile: 'image', imageShape: 'circular' }, children: [...] },
560
562
  { title: 'All stations', children: [...] }
561
563
  ]
562
564
  }
@@ -567,6 +569,10 @@ A property a _surface_ can't draw is never reported — that's the aspirational
567
569
  }
568
570
  ```
569
571
 
572
+ ::: tip See every style rendered
573
+ The example app's **Style Lab** tab draws each `style` declaration on CarPlay: one page per `gridTile` value, plus pages for section headers and for inheritance from page to section to track. Every shelf is titled with the block it renders, so what you see on screen names what to copy. The pages are plain route data in [`style-lab.ts`](https://github.com/radio-garden/react-native-audio-browser/blob/main/apps/example-native/src/utils/style-lab.ts), with a comment on each example saying what it shows.
574
+ :::
575
+
570
576
  Two config-level platform options round it out: `carPlayLoadingTitle` (localized "Loading…" on older CarPlay) and `androidControllerOfflineError`. The album-line tap needs both `albumPath` on the track and a `resolveAlbumPath` callback in the config that maps it to a browse path — see [CarPlay](/guide/carplay) and [Android Auto](/guide/android-auto) for the platform setup these build on.
571
577
 
572
578
  ## Where to go next
@@ -601,6 +601,36 @@ Return a browse path to enable the album line for that track, or `undefined` to
601
601
  CarPlay renders the tappable album/artist button as a **separate line built from the track's `album`** (visible as the third metadata line, with a chevron). Tracks without an `album` have no string for CarPlay to turn into a button — the artist line alone is never tappable. Set `album` on any track that should offer album-line navigation. See [Now Playing](/guide/now-playing) for the full field-by-surface rendering matrix.
602
602
  :::
603
603
 
604
+ ## Template Depth
605
+
606
+ CarPlay limits an audio app's template stack to **five templates, the tab bar included** (CarPlay App Programming Guide, p. 15), so the stack is full four pushes in. Now Playing and Up Next each take a slot like any list. A browse tree with a fixed shape (tabs → show → episodes) never gets there; one that can be drilled without end — a place, a place near it, a place near that — does.
607
+
608
+ At the limit, the library replaces the **top** page with the new one, so a push always lands:
609
+
610
+ ```
611
+ [tabs, A, B, C, D] + push E
612
+ [tabs, A, B, C, E]
613
+ ```
614
+
615
+ - **Forward never blocks.** A deeper list, Now Playing and Up Next all open at full depth.
616
+ - **Back skips one page.** From `E` it goes to `C`: `D`, the page the listener came from, is the one that was replaced. Below full depth, back works as usual.
617
+ - **The swap is a pop and a push.** CarPlay can't replace a template in place, so the library pops the top one without animation and then pushes the new one.
618
+
619
+ Each replacement is logged under the `com.audiobrowser` subsystem, category `TemplateStack`:
620
+
621
+ ```
622
+ push at the depth limit: replacing the top, CPListTemplate, with CPListTemplate
623
+ ```
624
+
625
+ To watch for it on the simulator:
626
+
627
+ ```sh
628
+ xcrun simctl spawn booted log stream --level info \
629
+ --predicate 'subsystem == "com.audiobrowser" AND category == "TemplateStack"'
630
+ ```
631
+
632
+ An `error` line in that category means the pop or the push failed; it names the template involved.
633
+
604
634
  ## Testing
605
635
 
606
636
  Test your app in the `CarPlay Simulator.app`, which is part of the [Additional Tools for Xcode](https://developer.apple.com/download/all/?q=additional%20tools%20for%20xcode). Siri voice search works there, so you can exercise the full intent flow without a car. CarPlay support on the iOS Simulator can be a little flaky — if something looks broken, retry or fall back to a physical device for final verification.
@@ -142,7 +142,7 @@ Optional fields that change how a Track renders on CarPlay / Android Auto. Each
142
142
  | ------------------------------------------ | ---------------------------------------------------------------------- | -------------------------- |
143
143
  | `style: { display: 'list' \| 'grid' }` | on a browsable track: the layout of the page it opens | Android Auto / AAOS |
144
144
  | `style: { artworkRendering: 'stencil' }` | tint monochrome artwork to the surface appearance | CarPlay |
145
- | `style: { imageShape: 'circular' }` | crop grid-tile artwork circular (people) instead of rounded | CarPlay (iOS 26+) |
145
+ | `style: { imageShape: 'circular' }` | crop `'image'` and `'condensed'` tile artwork circular (people) | CarPlay (iOS 26+) |
146
146
  | `style: { accessorySymbol: '…' }` | SF Symbol accessory; `'none'` restores the derived accessory | CarPlay |
147
147
  | `style: { cardTint: '…', cardImage: '…' }` | card color and image mode, when the section renders `gridTile: 'card'` | CarPlay (iOS 26+) |
148
148
  | `disabled` | unavailable: never plays; grayed where drawable, hidden elsewhere | all |