You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c957754
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: docs/specifications/App/TDD.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -324,7 +324,7 @@ the host `file` classifier reports as `script text executable` (BLD-11).
324
324
-**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.
325
325
-**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`.
326
326
- **`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.
328
328
-**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.
329
329
330
330
**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`.
Copy file name to clipboardExpand all lines: docs/specifications/App/design-system.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -253,9 +253,9 @@ The primary state signal. Binds to `connStateProvider` (read-only, `ValueListena
253
253
|`running`|`Icons.play_circle` (live) |
254
254
255
255
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).
257
257
-**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.
259
259
- Hit area ≥ 48 dp tall (transparent padding) so it can later become a tappable connection summary.
-**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*)
813
813
-**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*)
814
814
-**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*)
815
815
-**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:
821
821
-**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
822
822
-**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
823
823
-**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
824
825
825
826
### 4.15 About & Open-Source Information (`lib/app/`) — FR-ABOUT
0 commit comments