Skip to content

Commit c957754

Browse files
committed
[docs] Keep board identity visible when connected
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent 112c23d commit c957754

4 files changed

Lines changed: 9 additions & 5 deletions

File tree

‎docs/specifications/App/TDD.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ the host `file` classifier reports as `script text executable` (BLD-11).
324324
- **Neutral scan seam (`lib/pble`).** `class ScanHit { final String id; final String name; final int rssi; }` — the advertised board, retyped 1:1 off `BleScanResult` (`name` is the advertised name **verbatim**: the user-set label, else `PyBLE-XXXX`; value equality by `id`). `abstract interface class Scanner { Stream<List<ScanHit>> scan({Duration? timeout}); Future<void> stopScan(); }` — emits an **accumulating, deduped-by-`id`, latest-RSSI snapshot list** (accumulation lives in `lib/pble`, not the UI). A shipped `FakeScanner implements Scanner` scripts snapshots for tests. **This supersedes the illustrative `DiscoveredBoard`/`Scanner`-in-`lib/connect` sketch** — the seam is a neutral `lib/pble` type, exported through the `pble` barrel.
325325
- **Readiness seam.** `BleReadinessSource` (`BleReadiness current` + `Stream<BleReadiness> readiness`, [§7.4](#74-permissions--platform-quirks)) is **re-exported through the `pble` barrel** (the enum already is), so the connect UI observes adapter/permission transitions without importing `lib/ble`.
326326
- **`ConnectionManager` (neutral, `lib/pble`).** Holds the session; composes a `Scanner`, a `BleReadinessSource`, and a `ConnectionFactory` (`typedef ConnectionFactory = Future<Connection> Function(String deviceId)`). Surface: `Connection get connection` (the **stable facade**, [§6.2](#62-binding-pattern)); `Stream<List<ScanHit>> get scanResults`; `BleReadinessSource get readiness`; `ValueListenable<ConnectPhase> get phase`; `ScanHit? get selected`; `Object? get lastError`; and `startScan({Duration? timeout})` / `stopScan()` / `connect(String id)` / `disconnect()` / `dispose()`. `enum ConnectPhase { idle, scanning, connecting, connected, failed }` is the **connect-flow session phase — distinct from `ConnState`** (`scanning`/`connected` are deliberately not `ConnState` members, [§5.2](#52-shared-types)). `connect(id)` calls the `ConnectionFactory`, publishes the resulting `Connection`, and lets `PbleConnection.fromLink`'s link observation drive `connecting → ready`. The production `PbleConnectionManager.production({required String appName, required String appVersion})` wires the real `flutter_blue_plus` stack (`FbpBleAdapter` as scan source **and** connector, `FbpBleReadinessSource`) **internally**, so `main.dart` names no `lib/ble` type ([§6.2](#62-binding-pattern)).
327-
- **Connect controller + view (`lib/connect`).** A `ConnectController` (Riverpod `Notifier`, ADR-0007) projects the manager into an immutable `ConnectState { ConnectPhase phase, BleReadiness readiness, List<ScanHit> hits, ScanHit? selected, DeviceInfo? deviceInfo, Object? error }` (with `int get hitCount`), exposing `startScan`/`stopScan`/`connect(id)`/`disconnect`. On reaching `connected` it reads `connection.deviceInfo()` and stores it — the **DeviceInfo proof** the UI shows. `ConnectScreen` (the surface, hosted by `lib/app/pages/connect_page.dart` as a thin delegator) renders: the readiness banner (adapter-off / unauthorized / unsupported with localized rationale, FR-CONNECT-3); the scan CTA → live `ScanHit` list (name + RSSI, FR-CONNECT-1) → connecting indicator → connected `DeviceInfo` card (board ID / PyBLE firmware / chip / mpy / free-mem, FR-CONNECT-2) → disconnect; the unsupported-`proto_version` update prompt (FR-CONNECT-6); and a **live diagnostics panel** (adapter state, scanning yes/no, hit count, last-error detail) for on-hardware debugging (FR-CONN-6). The board-ID row reads only `DeviceInfo.deviceId` (never `ScanHit.id`); board ID and agent version are verbatim technical values. Empty compatibility defaults render localized **Not reported** rows and do not gate the connection.
327+
- **Connect controller + view (`lib/connect`).** A `ConnectController` (Riverpod `Notifier`, ADR-0007) projects the manager into an immutable `ConnectState { ConnectPhase phase, BleReadiness readiness, List<ScanHit> hits, ScanHit? selected, DeviceInfo? deviceInfo, Object? error }` (with `int get hitCount`), exposing `startScan`/`stopScan`/`connect(id)`/`disconnect`. On reaching `connected` it reads `connection.deviceInfo()` and stores it — the **DeviceInfo proof** the UI shows. `ConnectScreen` (the surface, hosted by `lib/app/pages/connect_page.dart` as a thin delegator) renders: the readiness banner (adapter-off / unauthorized / unsupported with localized rationale, FR-CONNECT-3); the scan CTA → live `ScanHit` list (name + RSSI, FR-CONNECT-1) → connecting indicator → connected `DeviceInfo` card (board ID / PyBLE firmware / chip / mpy / free-mem, FR-CONNECT-2) → disconnect; the unsupported-`proto_version` update prompt (FR-CONNECT-6); and a **live diagnostics panel** (adapter state, scanning yes/no, hit count, last-error detail) for on-hardware debugging (FR-CONN-6). The board-ID row reads only `DeviceInfo.deviceId` (never `ScanHit.id`); board ID and agent version are verbatim technical values. Empty compatibility defaults render localized **Not reported** rows and do not gate the connection. Because ADR-0011 replaces the Connect surface with the IDE at `ready`, the connected shell's `ConnectionStatusPill` also watches `connectControllerProvider`; this keeps the auto-disposed controller alive, defensively fetches `deviceInfo()` for an already-attached board, and appends its board ID + agent version to the state signal. The pill shows state alone while the fetch is pending, preserves the full state word under width pressure, and exposes the complete labeled summary through semantics.
328328
- **Scope of this freeze.** Delivers FR-CONNECT-1/2/3/5/6 + FR-CONN-6. **Saved-boards persistence** (FR-CONNECT-4 list/reconnect-by-remembered-id) depends on Drift `board_ref` (`lib/data`, A-24) which does not exist yet — auto-reattempt on an *unexpected* drop works today (A-03 `ReconnectingBleLink` + re-HELLO), but the persisted saved-board **list** is A-24/S5. The **screenless-identity UX** (rename / Identify, `setLabel`/`setIdentifyLed`/`identify`) is A-36/A-37/S7 and is **not** in this increment.
329329

330330
**Public API (sketch):** `ConnectController` (Riverpod `Notifier`) over the neutral `ConnectionManager` above; plus (A-36/A-37) `renameBoard(String label)`, `configureIdentifyLed(IdentifyLed)`, and `identify()` delegating to `Connection`.

‎docs/specifications/App/design-system.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,9 @@ The primary state signal. Binds to `connStateProvider` (read-only, `ValueListena
253253
| `running` | `Icons.play_circle` (live) |
254254

255255
The glyph is the state color at full strength (≥ 3:1 graphic).
256-
- **Contents** (row, `SignalSpacing.sm` gap, `EdgeInsets.symmetric(horizontal: SignalSpacing.md, vertical: SignalSpacing.xs)`): the state glyph + the **localized state label** in **`colorScheme.onSurface`** (`labelMedium`, scaler ≤ 1.3). The label uses `onSurface` (not the state color) so text is AA-safe at any hue (§1.3). The **state word never ellipsizes to nothing** — the pill's minimum content is glyph + full word (§7.1 shrink order).
256+
- **Contents** (row, `SignalSpacing.sm` gap, `EdgeInsets.symmetric(horizontal: SignalSpacing.md, vertical: SignalSpacing.xs)`): the state glyph + the **localized state label** in **`colorScheme.onSurface`** (`labelMedium`, scaler ≤ 1.3). On `ready`/`running`, once `DeviceInfo` is available, a localized compact pattern appends its verbatim board ID + PyBLE agent version (English: `Ready · 5646 · 0.6.0`); empty values use the localized **Not reported** fallback. The label uses `onSurface` (not the state color) so text is AA-safe at any hue (§1.3). The full **state word never ellipsizes** — under width pressure only the appended identity summary may ellipsize, and the pill's minimum content remains glyph + full word (§7.1 shrink order).
257257
- **Motion:** `connecting`/`running` glyphs pulse (`SignalMotion.pulse`, opacity 0.55↔1.0) under the reduced-motion gate (§6). Color transitions animate over `SignalMotion.standard`.
258-
- **Screen reader (folded from review):** wrap the pill in a `Semantics` node with `label: l10n.connStatusSemanticLabel(stateWord)` → "Connection status: {state}", so the state is announced *with context* rather than a bare "Ready".
258+
- **Screen reader (folded from review):** wrap the pill in one `Semantics` node. Without live metadata its label remains `l10n.connStatusSemanticLabel(stateWord)` → "Connection status: {state}". With metadata it uses a localized label that announces the complete state, **Board ID**, and **PyBLE firmware** values with context; visual ellipsis never truncates semantics.
259259
- Hit area ≥ 48 dp tall (transparent padding) so it can later become a tappable connection summary.
260260

261261
### 7.7 Toolbar actions (slots; FR-UI-3)

‎docs/specifications/App/specs.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ implementation:
809809
### 4.14 Scan/Connect Flow UI (`lib/connect/`) — FR-CONNECT
810810

811811
- **FR-CONNECT-1** — The connect screen MUST present a scan **filtered to the PyBLE service UUID**, listing each board by its **advertised name with RSSI** — the user-set device **label** when one is set, otherwise the default `PyBLE-XXXX` ([protocol.md §2](../protocol.md#2-ble-transport-gatt)) — and MUST NOT present a raw, unfiltered BLE device list. MUST (*source: PRD §9.6, §19.3, §8.1, protocol.md §2; verify: widget; story: A-22*)
812-
- **FR-CONNECT-2** — On Connect, the app MUST open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, and display `DeviceInfo` (display-only board ID, PyBLE agent firmware version, chip, MicroPython version, and free memory) before enabling editor/console/file actions. The board ID MUST be `DeviceInfo.deviceId`, never the platform scan identifier; board ID and agent version MUST be rendered verbatim, MUST NOT be derived or used as an authorization/profile gate, and an empty value MUST render a localized **Not reported** fallback without blocking use. MUST (*source: PRD §9.6, §19.3, §8.1, protocol.md §7; verify: widget/integration; story: A-22*)
812+
- **FR-CONNECT-2** — On Connect, the app MUST open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, and display `DeviceInfo` (display-only board ID, PyBLE agent firmware version, chip, MicroPython version, and free memory) before enabling editor/console/file actions. After the ready/running IDE shell replaces the pre-connection Connect surface, the always-visible connection-status pill MUST retain a compact board-ID + PyBLE-firmware summary. The board ID MUST be `DeviceInfo.deviceId`, never the platform scan identifier; board ID and agent version MUST be rendered verbatim, MUST NOT be derived or used as an authorization/profile gate, and an empty value MUST render a localized **Not reported** fallback without blocking use. MUST (*source: PRD §9.6, §19.3, §8.1, protocol.md §7; verify: widget/integration; story: A-22*)
813813
- **FR-CONNECT-3** — The screen MUST surface BLE permission and adapter state with localized rationale (iOS `NSBluetoothAlwaysUsageDescription`; Android `BLUETOOTH_SCAN`/`BLUETOOTH_CONNECT` + older-Android location). MUST (*source: PRD §9.6, §19.3, app.md §5; verify: widget/integration; story: A-04/A-22*)
814814
- **FR-CONNECT-4** — The screen MUST list **saved boards** and reconnect them by remembered identifier; on link loss the app MUST auto-reattempt and in-flight transfers MUST resume per [protocol.md §5](../protocol.md#5-file-transfer-the-reliability-core). MUST (*source: PRD §8.1, §19.3, §13.1; verify: integration; story: A-03/A-22*)
815815
- **FR-CONNECT-5** — The flow MUST be scan → connect → use only: no pairing-code step, no account, no session-lock/heartbeat, and no board-identity gating (the non-goals of [PRD §4.3](../prd.md)). MUST (*source: PRD §8.1, §9.6, §19.3, §4.3; verify: widget; story: A-22*)
@@ -821,6 +821,7 @@ implementation:
821821
- **AC-2 (firmware distinction)** — The card MUST label and show `DeviceInfo.agentVersion` verbatim as **PyBLE firmware**, distinct from the existing **MicroPython** version row. It MUST NOT derive the value from app metadata or `mpyVersion`. MUST
822822
- **AC-3 (additive compatibility)** — When either additive field is empty, its row MUST remain visible with a localized **Not reported** value; absence MUST NOT fail the connection, hide the existing DeviceInfo proof, or gate any action. MUST
823823
- **AC-4 (existing proof retained)** — The chip, MicroPython version, free-memory value, and Disconnect action MUST remain present and retain their current semantics. MUST
824+
- **AC-5 (connected-shell reachability)** — On `ready` and `running`, the always-visible status pill MUST append the same Board ID and PyBLE firmware values after its full localized state word. Tight layouts MAY ellipsize only the appended summary, never the state word; the semantics label MUST expose the complete state, **Board ID**, and **PyBLE firmware** values with context. While `DeviceInfo` is still loading the pill MAY show the state alone. MUST
824825

825826
### 4.15 About & Open-Source Information (`lib/app/`) — FR-ABOUT
826827

‎docs/specifications/app.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ The connected Board-info card renders `DeviceInfo.deviceId` as **Board ID** and
8888
the platform scan identifier, derive either value, or use either value as an
8989
authorization/profile gate. When a legacy or nonconforming agent reports an
9090
empty value, the card shows a localized **Not reported** fallback and keeps the
91-
connection usable.
91+
connection usable. When the ready/running shell replaces the pre-connection
92+
Connect surface, its always-visible connection-status pill retains a compact
93+
board-ID + PyBLE-firmware summary from the same `DeviceInfo`; the state word
94+
remains fully visible and the complete labeled values remain in semantics.
9295

9396
`DeviceInfo.chip == "esp32-s3"` does not identify which provisioning image is
9497
installed and cannot distinguish the lean `esp32-s3-n16r8` image from the

0 commit comments

Comments
 (0)