Skip to content

Tab badge hint: carPlayTabBadge (boolean | 'until-opened') #130

Description

@puckey

Depends on #129 (notifyTabsChanged()).

Proposal

A per-tab hint on the tab track, applied whenever tabs (re)build:

tabs: [
  { title: 'Library', path: '/library' },
  { title: 'New', path: '/new', carPlayTabBadge: 'until-opened' },
]
carPlayTabBadge?: boolean | 'until-opened'
  • true — content-controlled. Lit until the data clears it: the "live now" case (a broadcast tab badged during the show). The server toggles it by serving different tabs + notifyTabsChanged() (notifyTabsChanged(): targeted runtime tabs refresh #129).
  • 'until-opened' — declared unseen semantics. The consumer declares click-to-clear, licensing the library to clear the badge when the tab is opened and to hold session-scoped "was opened" memory so tab re-serves don't resurrect the dot mid-session. The library never invents this meaning for plain true.

Name follows the trimming precedent (showsTabBadgecarPlayTabBadge, as accessorySymbolNamecarPlayAccessorySymbol). Boolean mirrors the platform's boolean — no invented count badge until Apple has one. Silently ignored on Android Auto (no tab badge concept) and everywhere else, per the hint convention.

Verified platform constraint

Empirically (iOS 26.3 simulator, docs/carplay-lab badge experiment): mutating showsTabBadge on a presented template does not redraw the tab; CPTabBarTemplate.updateTemplates: applies it. So the hint rides the tab-rebuild path — which is also why the API is declarative: an imperative setCarPlayTabBadge(path, bool) was rejected, since the library would have to cache the override and re-apply it on every rebuild, a second source of truth fighting the declared tabs data. (A favorites-style client-state overlay — setCarPlayTabBadges(paths), mirroring setFavorites — remains a legitimate future layer for consumers with local seen-state; deferred until one exists.)

A badge-only toggle keeps the tab count equal, so it dodges the known selected-tab-reset wart of unequal-count rebuilds.

Cross-session seen-ness (for 'until-opened')

Session memory covers mid-session; across sessions the server must learn of the visit or the dot relights once per session:

  • Fetch heuristic (free): opening the tab fetches its content; the server marks seen. Confounded by surface prefetching — Android Auto eagerly loads root tabs' children — so it sometimes marks tabs seen that no human opened.
  • Visit event (truthful): exposing tab selection to JS (the CPTabBarTemplateDelegate.didSelectTemplate exposure, previously judged low-priority — this use case promotes it) lets the app notify its API through its own channel.

Graceful degradation with neither: the dot relights once per session until the server's data catches up — annoying, never wrong-direction (it never hides genuinely new content).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions