[feature] Prefix metro and tram line labels on the watch display #24
- @github-anthodev opened this issue · Sep 13, 2026edited
🔢 Impact Version (SemVer)
PATCH
📝 Description
On the watch application, line badges render the raw catalog label. Metro favorites display as bare numbers (
13) and tramways as (11), which reads ambiguously next to stop names and does not match how riders recognize IDFM lines. The companion should prepare the displayed label once per favorite: metro labels gain anMprefix and tramway labels aTprefix (13→M13,11→T11). Metro "bis" lines get their suffix abbreviated (3bis,3 bis, and already-prefixedM3bisall becomeM3b;7bisbecomesM7b). Every other mode — bus, RER, Transilien, and favorites without a mode — keeps its label byte-for-byte. The phone-side configuration page is not affected.
📋 [Feature] Acceptance Criteria
-
prepareAppearance(favorite, profile, language)(packages/companion/src/display-layout.js:158) computes a locallineLabelfromfavorite.lineLabelright after source-string validation; its signature, exports, and existing validations (profile/languageTypeError, empty-label checks, 448-byte record limit) are unchanged, and the favorite object is never mutated. - METRO:
/^M?([0-9]+)\s*bis$/imatches are replaced with"M" + digits + "b"; other digit-leading labels gainM(13→M13); already-prefixed (M13) and already-abbreviated (M3b) labels stay intact. - TRAM: digit-leading labels gain
T(11→T11); an already-prefixedT11or a suffixedT3bstays intact; thebiscontraction is never applied outside METRO. - All other cases preserve the exact string: BUS
13, RERA, TransilienL, a bus named3bis, and favorites withoutlineMode. - The same derived
lineLabelfeeds the four line slots of the cut computation AND the serializedlp3field insidecontent(hash input), so slot offsets and the serialized field can never diverge. - Watch rendering (
packages/watch/src/embeddedjs/ui.js:chip,overview,departures,header) is unchanged; slots 0–3 render the prefixed label inside the existing useful widths (28/36/38 px). Measured withdisplay-layout.measured(..., 1):M13= 22 px,T11= 20 px,T3a/T3b= 21 px,M3b/M7b= 23 px — all fit, including the 28 px compact Gabbro favorite, without ellipsis. - The traffic screen shares the line field and shows the same prefix, with no extra field and no new render branch.
- Catalog, persisted favorites, colors, styles, and the D2 protocol are unchanged; the content hash changes naturally and the existing DIFF mechanism transmits the new record at the next synchronization, with no schema-version or generation bump.
🔗 Affected Endpoints/Routes
None — display-only text preparation, no API surface.
File Role packages/companion/src/display-layout.jsProducer: label derivation inside prepareAppearance(lines 158–184)packages/watch/src/embeddedjs/ui.jsUnchanged consumers used for verification ( overviewslots 0/1,departuresslot 2,headerslot 3)packages/watch/test/packed.test.jsInteroperable regression with the real producer
🧪 Tests to Add
Type Coverage Watch packed regression ( node --test packages/watch/test/packed.test.js)Real prepareAppearance+appearanceValid/field/clippedfromwatchModule("packed"), profiles 0 and 1: METRO13→M13, TRAM11→T11, pre-prefixedT11→T11; preservation ofM13,T3b, BUS13, RERA, TransilienL, and a modeless favorite; bis cases3bis/7bis/3 bis/M3bis/M3b; BUS3bisstays3bis; record validity; favorite immutabilityFull suite npm teststays greenRadio gate npm run measure:radiobudgets unchanged (768/192-byte dictionaries, 15-dictionary FULL config, 18+2 startup attempts, 15025 exclusion)Renderer smoke (temporary, under var/)renderHarness-based navigation on both profiles: list and detail views showM13/T11andM3b/M7b, compact Gabbro favorite without ellipsis
⚙️ Constraints
- Frozen wire contract: no message IDs, budgets, targets, schema versions, or generation semantics change; the trilateral alias-map pin stays untouched.
- No watch-side code changes, no badge size or font changes, no catalog or favorites mutation, no new exported helpers, no secret-boundary impact.
- Node 24.18.0 pinned; Bun and the pebble CLI are user-managed (missing tools must exit 127, never auto-install).
🔀 [Conditional] Dependencies
None.
⚠️ [Conditional] Impact on Existing Behavior
- Watch labels for METRO/TRAM favorites change on the first synchronization after the update; the existing DIFF mechanism delivers the new records through the natural content-hash change.
- The traffic detail header shows the prefixed label because it shares the line field.
- The configuration page and the static catalog display remain unchanged.
🔍 [Conditional] Technical Context
- Sole producer caller:
packages/companion/src/index.js:381(layout.prepareAppearance(favorite, binding.profile, language)). - Shared field consumers:
packages/watch/src/embeddedjs/ui.js:44(chip), fed byoverview,departures, andheader. - Native rendering is not certified by host suites; final visual proof requires an isolated emulator run and is out of scope for this ticket's host gates.
-
Sign in to comment.