From 018ad54f9242f84ce6e90126cb26f4223a6f0e42 Mon Sep 17 00:00:00 2001 From: hooshyar Date: Thu, 24 Sep 2026 15:45:38 +0300 Subject: [PATCH 1/2] docs: add 2026-09 improvement plan and backlog tasks Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_015kUbfXrmCv9ZQr3ZZFkKQd --- ... Fix red CI on main (dart format drift).md | 21 +++ ...d motion (MediaQuery.disableAnimations).md | 20 +++ ...d fix grid overflow when rows > columns.md | 22 +++ ... Per-dot size callback (GitHub issue 1).md | 21 +++ ..._ui and flutter_streaming_text_markdown.md | 19 +++ ...I-state presets and README hero refresh.md | 20 +++ docs/IMPROVEMENT-PLAN-2026-09.md | 152 ++++++++++++++++++ 7 files changed, 275 insertions(+) create mode 100644 backlog/tasks/task-001 - Fix red CI on main (dart format drift).md create mode 100644 backlog/tasks/task-002 - Honor reduced motion (MediaQuery.disableAnimations).md create mode 100644 backlog/tasks/task-003 - Non-square sizing and fix grid overflow when rows > columns.md create mode 100644 backlog/tasks/task-004 - Per-dot size callback (GitHub issue 1).md create mode 100644 backlog/tasks/task-005 - Cross-promote DotLoader in flutter_gen_ai_chat_ui and flutter_streaming_text_markdown.md create mode 100644 backlog/tasks/task-006 - AI-state presets and README hero refresh.md create mode 100644 docs/IMPROVEMENT-PLAN-2026-09.md diff --git a/backlog/tasks/task-001 - Fix red CI on main (dart format drift).md b/backlog/tasks/task-001 - Fix red CI on main (dart format drift).md new file mode 100644 index 0000000..18b2824 --- /dev/null +++ b/backlog/tasks/task-001 - Fix red CI on main (dart format drift).md @@ -0,0 +1,21 @@ +--- +id: TASK-001 +title: Fix red CI on main (dart format drift) +status: To Do +priority: high +labels: [improvement-plan-2026-09] +created_date: '2026-09-24' +--- + +## Description + +CI has been failing on main since the 1.0.0 commit (run 29142043830): `dart format --set-exit-if-changed` reformats test/flutter_dot_loader_test.dart on current stable (3.47.5). Also fix README alias-count drift (13 -> 16) and strip the flutter-create boilerplate from pubspec.yaml. + +Acceptance criteria: +- [ ] `dart format .` applied; CI green on main +- [ ] CI status badge added to README +- [ ] README says 16 semantic aliases +- [ ] pubspec boilerplate comment block removed; pana still 160/160 +- [ ] Ship as 1.0.1 + +See docs/IMPROVEMENT-PLAN-2026-09.md (P0-1, B1, B5, B6). diff --git a/backlog/tasks/task-002 - Honor reduced motion (MediaQuery.disableAnimations).md b/backlog/tasks/task-002 - Honor reduced motion (MediaQuery.disableAnimations).md new file mode 100644 index 0000000..570bf4e --- /dev/null +++ b/backlog/tasks/task-002 - Honor reduced motion (MediaQuery.disableAnimations).md @@ -0,0 +1,20 @@ +--- +id: TASK-002 +title: Honor reduced motion (MediaQuery.disableAnimations) +status: To Do +priority: high +labels: [improvement-plan-2026-09] +created_date: '2026-09-24' +--- + +## Description + +No loader honours the OS reduce-motion setting. thinking_orbs_flutter does; spinkit/loading_animation_widget/loading_indicator do not, so this is a cheap differentiator. + +Acceptance criteria: +- [ ] New `respectReducedMotion` (default true) on MatrixLoader/DotLoader/TriangleLoader +- [ ] When `MediaQuery.maybeDisableAnimationsOf(context) == true`: ticker stopped, static representative frame painted (documented behaviour) +- [ ] Reacts to the setting changing at runtime (didChangeDependencies) +- [ ] Widget tests for on/off; README a11y row + CHANGELOG updated + +See docs/IMPROVEMENT-PLAN-2026-09.md (P0-2). diff --git a/backlog/tasks/task-003 - Non-square sizing and fix grid overflow when rows > columns.md b/backlog/tasks/task-003 - Non-square sizing and fix grid overflow when rows > columns.md new file mode 100644 index 0000000..37e1f95 --- /dev/null +++ b/backlog/tasks/task-003 - Non-square sizing and fix grid overflow when rows > columns.md @@ -0,0 +1,22 @@ +--- +id: TASK-003 +title: Non-square sizing and fix grid overflow when rows > columns +status: To Do +priority: high +labels: [improvement-plan-2026-09] +created_date: '2026-09-24' +--- + +## Description + +The loader box is always size x size, so a 3x1 DotLoader wastes ~60px of height in a chat bubble (dot_matrix_loader supports inline sizing). Bug: auto spacing is derived from columns only and reused vertically, so columns:1 rows:5 size:64 paints a 260px-tall grid outside the box; _handleTap shares the bad math. Triangle mask divides by zero for 1-row/1-column grids. + +Acceptance criteria: +- [ ] Optional `width`/`height`; `size` kept as square shorthand (non-breaking) +- [ ] Per-axis auto-spacing that fits both dimensions; tap hit-test uses the same geometry +- [ ] DotLoader default box is inline/text-height friendly +- [ ] Triangle mask safe for rows==1 / columns==1 +- [ ] Tests: rows>columns, 1xN, Nx1, triangle 1-row -- painted bounds stay inside the widget +- [ ] Ship in 1.1.0 + +See docs/IMPROVEMENT-PLAN-2026-09.md (P0-3, B2, B3). diff --git a/backlog/tasks/task-004 - Per-dot size callback (GitHub issue 1).md b/backlog/tasks/task-004 - Per-dot size callback (GitHub issue 1).md new file mode 100644 index 0000000..8451fe8 --- /dev/null +++ b/backlog/tasks/task-004 - Per-dot size callback (GitHub issue 1).md @@ -0,0 +1,21 @@ +--- +id: TASK-004 +title: Per-dot size callback (GitHub issue 1) +status: To Do +priority: high +labels: [improvement-plan-2026-09] +created_date: '2026-09-24' +--- + +## Description + +Issue #1 (WD-J, 2026-05-31) requests per-dot sizing, unanswered for ~4 months. Reply on the issue first, then implement. + +Acceptance criteria: +- [ ] Reply posted on issue #1 acknowledging and linking the plan +- [ ] `customDotSize: double Function(int row, int col)?` returning a scale factor (1.0 = dotSize, 0 = hidden), mirroring customMask +- [ ] Hit-testing unaffected for scaled dots (documented) +- [ ] Tests + README API table row + CHANGELOG +- [ ] Issue closed referencing the release + +See docs/IMPROVEMENT-PLAN-2026-09.md (P0-4). diff --git a/backlog/tasks/task-005 - Cross-promote DotLoader in flutter_gen_ai_chat_ui and flutter_streaming_text_markdown.md b/backlog/tasks/task-005 - Cross-promote DotLoader in flutter_gen_ai_chat_ui and flutter_streaming_text_markdown.md new file mode 100644 index 0000000..fed31af --- /dev/null +++ b/backlog/tasks/task-005 - Cross-promote DotLoader in flutter_gen_ai_chat_ui and flutter_streaming_text_markdown.md @@ -0,0 +1,19 @@ +--- +id: TASK-005 +title: Cross-promote DotLoader in flutter_gen_ai_chat_ui and flutter_streaming_text_markdown +status: To Do +priority: medium +labels: [improvement-plan-2026-09] +created_date: '2026-09-24' +--- + +## Description + +Sibling packages have 30-100x our traffic (gen_ai_chat_ui ~3.5k DL/30d, streaming_text_markdown ~12.9k DL/30d) and target the same AI-chat developer. Depends on TASK-003 so DotLoader looks right inline in a bubble. + +Acceptance criteria: +- [ ] flutter_dot_loader README: 'Works great with' section with a ~10-line DotLoader-in-chat-bubble snippet linking both packages +- [ ] Backlog task filed in flutter_gen_ai_chat_ui to document/show DotLoader as a custom typing indicator via its builder (no hard dependency) +- [ ] Same for flutter_streaming_text_markdown example/README + +See docs/IMPROVEMENT-PLAN-2026-09.md (P1-1). diff --git a/backlog/tasks/task-006 - AI-state presets and README hero refresh.md b/backlog/tasks/task-006 - AI-state presets and README hero refresh.md new file mode 100644 index 0000000..ef4788b --- /dev/null +++ b/backlog/tasks/task-006 - AI-state presets and README hero refresh.md @@ -0,0 +1,20 @@ +--- +id: TASK-006 +title: AI-state presets and README hero refresh +status: To Do +priority: medium +labels: [improvement-plan-2026-09] +created_date: '2026-09-24' +--- + +## Description + +Orb packages win on vocabulary (thinking/searching/generating). Hero GIFs are 1.5MB, gallery-focused, and ship in the archive. + +Acceptance criteria: +- [ ] DotLoader.thinking()/.typing()/.searching()/.generating() (or DotLoaderPreset enum) with curated pattern/grid/duration; tested; in README + AGENTS.md +- [ ] New first-screen GIF of DotLoader in a chat bubble +- [ ] GIFs referenced by absolute raw.githubusercontent.com URLs; *.gif in .pubignore; archive < 300 KB +- [ ] Live-demo badge in README + +See docs/IMPROVEMENT-PLAN-2026-09.md (P1-2, P1-3). diff --git a/docs/IMPROVEMENT-PLAN-2026-09.md b/docs/IMPROVEMENT-PLAN-2026-09.md new file mode 100644 index 0000000..7fc9af8 --- /dev/null +++ b/docs/IMPROVEMENT-PLAN-2026-09.md @@ -0,0 +1,152 @@ +# flutter_dot_loader — Improvement Plan (2026-09) + +Written 2026-09-24. Scope: research and planning only. No library code was changed. +The package is small and low-traffic, so this plan is mostly about discoverability +and adoption, plus a few real bugs found while reading the code. + +## 1. Snapshot + +| Metric | Value (2026-09-24) | +|---|---| +| Latest version | 1.0.0 (published 2026-07-11) | +| Pub points | 160 / 160 | +| Likes | 22 | +| Downloads (30d) | ~110 | +| GitHub | 6 stars, 0 forks, live demo at https://hooshyar.github.io/flutter_dot_loader/ | +| Open issues / PRs | 1 issue (#1, per-dot size, from WD-J, 2026-05-31, no reply yet) / 0 PRs | +| CI on `main` | **Red since the 1.0.0 commit.** `dart format --set-exit-if-changed` fails on `test/flutter_dot_loader_test.dart` | +| Analyzer | `flutter analyze`: no issues (Flutter 3.47.5 stable) | +| Tests | 35 widget/unit tests in one file (see section 6 for the run result) | +| Runtime deps | none (Flutter SDK only) | +| Dev deps | `flutter_lints ^6.0.0` (latest 6.0.0). `flutter pub outdated`: all direct deps current. Only transitive SDK-pinned packages lag (`material_color_utilities`, `test_api`), which nobody can fix from here | +| Example app | `flutter_lints ^5.0.0`, one major behind (6.0.0 is latest) | +| SDK constraint | `sdk: ^3.11.4`, `flutter: ">=1.17.0"`. The Dart floor is much higher than the code needs, and the Flutter floor doesn't match it | + +### Search ranking on pub.dev (queried 2026-09-24) + +| Query | Position | +|---|---| +| `typing indicator` | **#1** | +| `dot matrix` | **#1** (direct rival `dot_matrix_loader` at #2) | +| `led loader` | #2 | +| `dot loader` | #3 | +| `thinking indicator` | #6 (behind four "thinking orbs" packages) | +| `loading` / `loader` / `spinner` | not in the top results. These generic queries are owned by spinkit, loading_animation_widget, and loading_indicator | + +Ranking isn't the problem. The package already wins its niche queries. With 110 +downloads a month, the problem is that niche queries have little traffic and the +package has no inbound links from the bigger sibling packages. + +## 2. Competitor matrix + +| Package | Likes | DL/30d | Points | Last release | Variety | Custom frames | Non-square / inline sizing | Reduced motion | Semantics | Live demo | +|---|---|---|---|---|---|---|---|---|---|---| +| **flutter_dot_loader** | 22 | 110 | 160 | 2026-07 | 60 patterns + 16 aliases, marquee text | Yes (`customIntensity`, JSON frames) | **No** (box is always `size × size`) | **No** | Yes (opt-in) | Yes (GH Pages) | +| flutter_spinkit | 4,647 | 493k | 140 | 2025-08 | ~30 spinners | No | Yes | No | No | No (GIFs only) | +| loading_animation_widget | 2,030 | 119k | 150 | 2024-10 | ~20 | No | Yes | No | No | No (GIFs) | +| loading_indicator | 695 | 51k | 160 | 2026-08 | ~30 (loaders.css) | No | Yes | No | No | No | +| dot_matrix_loader | 10 | 69 | 160 | 2026-06 | 61 presets | Yes (`CustomDotAnimation`, `SequenceAnimation`) | **Yes, inline `size` next to text** | No | No | Yes (dots.luisportal.com) | +| thinking_orbs_flutter | 0 | 161 | 150 | 2026-07 | 6 AI "thinking" states | No | 2 fixed sizes | **Yes** | Yes | Yes | +| agent_orbs | 0 | 56 | 160 | 2026-08 | 9 AI states | No | 2 sizes | ? | ? | — | +| simple_typing_indicator | 4 | 142 | 160 | 2025-02 | 1 | No | Yes | No | No | No | + +(Competitor a11y columns come from grepping each package's latest published +`lib/` for `disableAnimations`, `Semantics`, and `RepaintBoundary`.) + +### What competitors offer that we don't + +1. **Inline / non-square sizing.** `dot_matrix_loader` fits a loader next to text in a `Row`. + Our `DotLoader` (a 3×1 row) always takes a 64×64 box, which leaves about 60 px of empty + height inside a chat bubble. This is the biggest DX gap for the AI typing-indicator use case. +2. **Shared controller.** `dot_matrix_loader` can drive N loaders from one `AnimationController`, + which matters for galleries and long chat lists. +3. **Reduced motion.** `thinking_orbs_flutter` honours `MediaQuery.disableAnimations`. + None of the big three do, so this is cheap to add and a real differentiator. +4. **AI-state vocabulary.** The orb packages name states such as thinking, searching, and + generating. We have the patterns but not the names or a preset API for them. +5. **Reach.** spinkit and loading_animation_widget win generic "loading" traffic through age + and back-links, not features. Our lever is cross-promotion from our own packages + (`flutter_gen_ai_chat_ui`: 101 likes, 3.5k DL/30d; `flutter_streaming_text_markdown`: 52 likes, 12.9k DL/30d). + +### What we have that nobody else has + +A marquee text font, JSON/Remote Config frame delivery, `once` + `onComplete` splash +handoff, tap-to-dot interactivity, opt-in semantics, and a `RepaintBoundary`. + +## 3. Bugs found while reading (not yet reported) + +| # | Bug | Evidence | +|---|---|---| +| B1 | **CI red on main since 1.0.0.** The formatter in current stable reformats `test/flutter_dot_loader_test.dart` | GH Actions run 29142043830; reproduced locally with Flutter 3.47.5 | +| B2 | **Grid overflows its box when `rows > columns`.** Auto spacing is `(size - dotSize*columns)/(columns-1)` and is also used vertically. With `columns: 1, rows: 5, size: 64` the spacing is 60 and the grid is 260 px tall, painted outside the 64 px box (CustomPaint doesn't clip). `_handleTap` uses the same math | `matrix_loader.dart` ~L504, L563, L628 | +| B3 | **Triangle mask divides by zero** when `rows == 1` or `columns == 1` (`r/(rows-1)`), so NaN comparisons render no dots | `_isInMask` ~L684 | +| B4 | `MouseRegion` calls `setState` on every enter/exit even when `hoverAnimated: false`, which rebuilds for no reason | `build()` ~L535 | +| B5 | README drift: the features table says "13 semantic aliases", but there are 16 | README L26 | +| B6 | `pubspec.yaml` still carries the ~35-line `flutter create` boilerplate comment block, and `flutter: ">=1.17.0"` contradicts `sdk: ^3.11.4` | pubspec | + +## 4. Prioritized plan + +### P0 — do next (small, and they unblock everything else) + +**P0-1. Fix red CI and pin a formatter-stable workflow** — Effort **S** +- Why: a red badge and a failing `main` hurt trust. Every future PR, including issue #1's, would fail CI. +- Acceptance: `dart format` has been run; CI is green on `main`; the workflow keeps `--set-exit-if-changed`; a CI status badge is added to the README. + +**P0-2. Reduced-motion support** — Effort **S** +- Why: a11y is a stated feature, and only one tiny competitor does this. It matters for OS "reduce motion" users and for WCAG 2.3.3. +- Acceptance: new `respectReducedMotion` (default `true`). When `MediaQuery.maybeDisableAnimationsOf(context) == true`, the loader renders a static mid-intensity frame (or a slow, low-amplitude pulse, whichever the team documents) and the ticker is stopped. Covered by widget tests for both states. README a11y row updated. + +**P0-3. Non-square sizing and fixed grid-overflow math (B2, B3)** — Effort **M** +- Why: this is the biggest DX gap for the AI typing-indicator positioning. A 3×1 `DotLoader` should be about 24×8 inline next to text, not a 64×64 box. It also fixes an actual overflow bug. +- Acceptance: optional `width`/`height` (with `size` kept as the square shorthand, so no breaking change). Auto-spacing is computed per axis and fits both dimensions. `DotLoader` defaults to an intrinsic, text-height-friendly box. Triangle mask is safe for 1-row and 1-column grids. Tap hit-testing uses the same geometry. Tests cover `rows > columns`, `1×N`, `N×1`, and a triangle with 1 row, asserting painted bounds stay inside the widget. Minor version bump (1.1.0). + +**P0-4. Reply to and triage issue #1 (per-dot size)** — Effort **S** to reply, **M** to build +- Why: it's the only external issue, it has been open about four months with no reply, and a quick response costs nothing. +- Acceptance: reply on the issue. Implement `customDotSize: double Function(int row, int col)?` (a scale factor, 0 = hidden, returning to `dotSize` when null), mirroring the `customMask` API. Tests plus a README row. Close #1 citing the release. + +### P1 — adoption and discoverability + +**P1-1. Cross-promotion with our own AI chat packages** — Effort **S–M** +- Why: `flutter_gen_ai_chat_ui` (3.5k DL/30d) and `flutter_streaming_text_markdown` (12.9k DL/30d) have 30–100× our traffic and target the exact same user. +- Acceptance: the `flutter_gen_ai_chat_ui` README and example show `DotLoader` as the recommended custom typing indicator, via an optional builder so the chat UI takes no hard dependency. `flutter_dot_loader`'s README has a "Works great with" section linking both packages, with a 10-line snippet of `DotLoader` inside a chat bubble. Tracked as cross-repo tasks. + +**P1-2. AI-state presets** — Effort **S** +- Why: this competes directly with the orb packages on vocabulary, which is what developers and AI agents search for. +- Acceptance: `DotLoader.thinking()`, `.typing()`, `.searching()`, and `.generating()` named constructors (or a `DotLoaderPreset` enum) that map to curated pattern, grid, and duration combos. Documented in the README, AGENTS.md, and the dartdoc of each preset. Tested. + +**P1-3. README hero refresh** — Effort **S** +- Why: the two hero GIFs are 472 KB and 1 MB, portrait, gallery-oriented, and ship inside the package archive. The first screen should show the chat use case. +- Acceptance: a small GIF of `DotLoader` in a chat bubble goes first. GIFs are referenced by absolute `raw.githubusercontent.com` URLs and `*.gif` is added to `.pubignore` (archive under 300 KB). "13 aliases" becomes "16" (B5). Live demo link gets a badge. + +**P1-4. Shared-controller / `TickerMode` friendliness** — Effort **M** +- Why: galleries and long chat lists create one ticker per loader. `dot_matrix_loader` advertises a shared controller. +- Acceptance: optional `controller: Animation?`. When provided, no internal ticker is created. Driven by `CustomPainter(repaint:)` instead of `AnimatedBuilder` rebuilding the widget each frame. Fix B4. Test: 50 loaders sharing one controller build and pump without error. + +**P1-5. Broaden the SDK floor** — Effort **S** +- Why: `sdk: ^3.11.4` locks out apps on anything older than early-2026 Flutter. The code needs only `Color.withValues` (Flutter 3.27 / Dart 3.6). Lowering the floor means more users can resolve the package. +- Acceptance: `sdk: ">=3.6.0 <4.0.0"`, `flutter: ">=3.27.0"`. CI matrix runs the floor version plus stable. pana is still 160. Remove the pubspec boilerplate (B6). Bump the example to `flutter_lints ^6.0.0`. + +### P2 — nice to have + +| Item | Effort | Why | Acceptance | +|---|---|---|---| +| P2-1. Golden tests for the 16 aliases and the marquee font | M | Catch visual regressions that the "renders without throwing" tests miss | `matchesGoldenFile` per alias at a fixed progress, CI on Linux only | +| P2-2. Coverage report + badge | S | Signal quality | `flutter test --coverage` in CI, uploaded to Codecov, badge in the README | +| P2-3. Split the 670-line test file | S | Maintainability | One test file per widget/helper | +| P2-4. Perf: cache per-dot geometry, avoid `math.pow` in the hover path | S | Large grids (for example 32×8 marquees) | Benchmark at 32×32 shows no regression. Painter allocations are hoisted | +| P2-5. Additional pub.dev topic swap | S | `thinking-indicator` / `typing-indicator` are what AI-chat devs search | Swap `pixel-art` → `typing-indicator` (max 5 topics), then re-check rankings a month later | +| P2-6. Launch post | S | Adoption is driven by being seen | Short post (dev.to, r/FlutterDev, Flutter Discord #showcase) with the chat-bubble GIF plus the live demo link, after 1.1.0 ships | + +## 5. Release sequencing + +1. **1.0.1**: P0-1 (CI), B5, B6. Docs and hygiene only. +2. **1.1.0**: P0-2 reduced motion, P0-3 width/height plus overflow fix, P0-4 `customDotSize`, P1-5 SDK floor. All additive, no breaking changes. +3. **1.2.0**: P1-2 presets, P1-4 shared controller, P1-3 README hero. +4. Cross-repo P1-1 lands in `flutter_gen_ai_chat_ui` right after 1.1.0 (it needs the inline sizing to look right in a bubble). + +## 6. Verification notes (this research pass) + +- `flutter pub outdated`: all direct and dev deps current, and `flutter_lints` 6.0.0 is the latest stable on pub.dev. +- `flutter analyze`: no issues. +- `dart format --set-exit-if-changed .`: **fails** (1 file), matching the red CI run. +- `flutter test --coverage`: see the task summary. The machine was heavily loaded by parallel test runs during this pass. From b9c3839837dd6b49eea80a0b5695141b1a441060 Mon Sep 17 00:00:00 2001 From: hooshyar Date: Thu, 24 Sep 2026 15:53:28 +0300 Subject: [PATCH 2/2] docs(plan): record test + coverage results Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_015kUbfXrmCv9ZQr3ZZFkKQd --- docs/IMPROVEMENT-PLAN-2026-09.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/IMPROVEMENT-PLAN-2026-09.md b/docs/IMPROVEMENT-PLAN-2026-09.md index 7fc9af8..9c85cf4 100644 --- a/docs/IMPROVEMENT-PLAN-2026-09.md +++ b/docs/IMPROVEMENT-PLAN-2026-09.md @@ -16,7 +16,7 @@ and adoption, plus a few real bugs found while reading the code. | Open issues / PRs | 1 issue (#1, per-dot size, from WD-J, 2026-05-31, no reply yet) / 0 PRs | | CI on `main` | **Red since the 1.0.0 commit.** `dart format --set-exit-if-changed` fails on `test/flutter_dot_loader_test.dart` | | Analyzer | `flutter analyze`: no issues (Flutter 3.47.5 stable) | -| Tests | 35 widget/unit tests in one file (see section 6 for the run result) | +| Tests | 39 tests, all passing (one 670-line file). Line coverage 74.3% overall; `matrix_loader.dart` 61% (painter/pattern math, hover and tap paths mostly unexercised); the other files are 92-100% | | Runtime deps | none (Flutter SDK only) | | Dev deps | `flutter_lints ^6.0.0` (latest 6.0.0). `flutter pub outdated`: all direct deps current. Only transitive SDK-pinned packages lag (`material_color_utilities`, `test_api`), which nobody can fix from here | | Example app | `flutter_lints ^5.0.0`, one major behind (6.0.0 is latest) | @@ -149,4 +149,5 @@ handoff, tap-to-dot interactivity, opt-in semantics, and a `RepaintBoundary`. - `flutter pub outdated`: all direct and dev deps current, and `flutter_lints` 6.0.0 is the latest stable on pub.dev. - `flutter analyze`: no issues. - `dart format --set-exit-if-changed .`: **fails** (1 file), matching the red CI run. -- `flutter test --coverage`: see the task summary. The machine was heavily loaded by parallel test runs during this pass. +- `flutter test --coverage`: 39/39 pass; line coverage 74.3% (378/509). The weakest file is `matrix_loader.dart` at 61%. +- P2-2 target: raise `matrix_loader.dart` coverage to 85% or more with tests for the tap hit-test, hover, and per-pattern intensity bounds (0..1).