Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ list every change.
of a `TabBarView`, get no outline or marker. Their position read as NaN,
so stepping through a card's widgets could start on one, say "Showing",
and count every tab's widgets.
- A widget that a scroll view clips away, such as a list row scrolled
under the app bar, gets no outline or marker, and an outline covers only
the part of a widget that shows. A list builds rows past its edge, so a
card's outline could start on a hidden row and count rows the screen did
not show. A flipped or scaled widget is outlined where it shows.

## 0.38.0

Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Runtime performance diagnostics package for Flutter mobile apps. 21 detectors ac

```bash
# Always use fvm for all Flutter/Dart commands
fvm flutter test --exclude-tags benchmark # Default run (~5,102 tests; wall-clock benchmarks excluded)
fvm flutter test --exclude-tags benchmark # Default run (~5,109 tests; wall-clock benchmarks excluded)
fvm flutter test --tags benchmark --concurrency=1 # Wall-clock benchmarks, serial (44 tests)
fvm flutter test test/detectors/ # Run detector tests only
fvm flutter analyze # Static analysis (must be 0 issues)
Expand Down Expand Up @@ -81,7 +81,7 @@ test/

**v1.0.0 API**: `lib/sleuth.dart` exports with `show` lists only (62 names + `Sleuth`); `lib/validation.dart` holds `EvidenceTier`, `DetectorMetadata`(+Provider), `BracketSpec`, `ComponentMetadata`(+Provider), `ProfileCaptureSchema`, `PollTimings`, `StreamResourcePollResult`, `kSleuthPackageVersion` and Repaint/MemoryPressure/Startup/TrackedResource/ImageMemory/GpuPressure/FrameTiming detectors, all `@experimental` (example `analysis_options.yaml` ignores `experimental_member_use`); 15 internal names hidden (tests import `src/`). `StartupTracker` (`lib/src/startup/`) owns startup state (`metrics`, `dartEntryMonotonicUs`, `enrichWithVmData`); `ActiveController.current` / `release` replace `Sleuth._controller` / `notifyControllerDisposed`. Capture statics on `Sleuth` `@experimental`, test statics `@visibleForTesting`. `RouteSession.end()` / `countScanCycle()` `@internal`. Theme: 29 unread tokens + `confidenceColor`/`effortColor`/`sourceAccentColor` removed. Growing enums documented. `test/validation/public_api_test.dart` pins names against `test/validation/_fixtures/public_api_1_0_0.txt` (80 names); CI `api-diff` job: `dart pub unpack` newest 1.x, drop `lib/validation.dart` from both trees, `dart_apitool` 0.23.2 `--version-check-mode onlyBreakingChanges` (fails only a breaking change without a major bump; `fully` also failed minor changes) (added enum value = minor `CF02`; removing an `@experimental` top-level is still breaking), skips before 1.0.0 is on pub.dev, fails on a pub.dev error. API table `doc/api_1_0_0.md`.

**v1.0.0 overlay**: header run label ("Profile · VM", dot filled with VM / amber ring without) + `MoreMenu` (Startup, Rebuild stats, Hidden issues, Encyclopedia, Guide, Copy snapshot, Theme Auto/Light/Dark); severity chips lead the status row (turning one off is refused when no card would remain; the frame rate and the chipless row label are semantics containers); Rebuild stats reads "Off" unless `SleuthController.countsRouteRebuilds` (profile + `enableDeepDebugInstrumentation`); 320 px min width; no footer. `IssueCard`: `SleuthSeverityIcon` (light presets outline warning/OK via `edgeOf`), meta line (category · confidence or Debug timing tag · "seen X of Y scans, label" · effects, Jank/New tags), crosshair 48 px. Expanded: Measured row with `MeasurementGauge` (scale 1.25 × the higher line, 100 for a percent; a value past it fills the track with two slanted cuts; a debug rate critical on its latest window, `HeldRate.latestRate` → `extraTraceArgs.latestRatePerSec`, reads "Critical: the latest second went over the line" instead of the held note), Where (`file:line` per element) / Route / When / Evidence ("... Sleuth rates it possible, since ...") / Check in rows, Copy details and the Ask AI prompt add `measuredSentence` (`copyDetailsText`), Fix with code blocks, buttons Show on screen / Learn more / Ask AI / Copy details / Hide / Not useful, Why it rebuilds (`DebugSnapshot.forcedRebuildsOf`). `status_lines.dart`: Starting / Warming up / No VM link / No issues found / VM link lost / stopped retrying (no VM-lost toast), one debug banner. Highlight flow: crosshair → `selectHighlightForIssue` (`HighlightLookup` found/notFound/pending, no category fallback) → card shrinks to `SleuthHighlightBar` ("Showing X" / "X is off screen", Back to issues, close; edge away from the widget, shield for a double tap, `highlightBarAtTopNotifier` for the label); outline only (markers = "Mark flagged widgets" toggle), in the target issue's severity (`highlightTargetSeverity`), not painted while the widget is off screen; an issue with an `occurrenceId` takes only its element's highlight, a name match skips list-tagged highlights; with the bar up Escape leaves an app dialog or sheet to the app; controller collects highlights while `_highlightTarget` / pending. List cards (`SleuthController.listsWidgets`: always_repaint_painter, missing_repaint_boundary, layout_bottleneck, uncached_images, expensive_gpu_nodes) step through highlights tagged `WidgetHighlight.stableId` + element `occurrenceId`: first on screen, `highlightStepNotifier` (shown place frozen between Next/lookup, refreshed on 0/1/many count change), `nextHighlightStep` (off screen → widget now at its place), `canStepNext`; bar shows "· 1 of 3" + Next and a second row only with Next (GlobalKey keeps Back to issues), shields its old rect when it moves or shrinks. Card list: each card sits in `_CardSlot`, which keeps its `IssueCard` widget while `IssueCard.sameInputsAs` holds (every constructor field; issues by `utils/issue_fingerprint.dart` `issueFingerprint` + `occurrenceId`, callbacks by presence; `issue_card_test` reads the constructor to pin the list) and `_cardEpoch` stays (moves on severity filter / hidden-key change, a change of the shown `(listKey, occurrenceId)` set, `didUpdateWidget`, `didChangeDependencies`, `reassemble`: what `_relatedLinkOf` reads while a card builds); kept cards' callbacks act on the row's latest report (`latest()`). `OverlayUiState.schemaVersion` 2: `loadJson` maps a version 1 hide key without a widget name to an any-widget key (`legacyHideKey`, `base|*` / `base|*!critical`), which `isHiddenBy` / `hideKeysOf` / the Hidden page match for every widget of that id. The Hidden page labels keys with `hideKeyLabel` (`id · Widget`, `id · every widget`, critical suffix left out). Jank tag: `SleuthController.heldJankCauses()` (issues a verdict named in the last `FrameTimingDetector.jankWindow`, recorded by a `verdictNotifier` listener on the controller's monotonic clock or `clockOverrideForTest`, cleared on a route change beside `markRouteEpoch`, so the hold survives closing the dashboard), shown only while a jank symptom is reported; the card arms one timer for `nextExpiry`. Toasts in inverted theme colours (`toastColorsOf`). Example `ext.sleuthDemo.overlay` actions `menu` / `notUseful`. `getGlobalRect` returns null for a non-finite rect (a kept-alive page that is not painted, such as another `TabBarView` tab, maps to NaN), so those widgets get no highlight; `SimpleStructuralDetector` uses it too. The 1.0 card replaced 0.37 internals named in the v0.37.0 paragraphs below: the frozen zone is `_frozen` (`List<FrozenCard>`, built by `applyFreezeZone` and `_expandInZone`) in place of `_expandedIndices` / `_orderSnapshot` / `_repointExpansions`; the status row's `_SeverityChip`s replace `_IssuesSummaryBar` / `_SeverityChipPill`; the meta line replaces `_badgeLine` / `_inlineBadgesWidth`; `_LeadTrailLine` is gone. Example `FileSleuthStateStore` writes under `overlayStateDirectory` (Android `files` beside `code_cache`, iOS `Library/Application Support` beside `tmp`; installs empty `code_cache`).
**v1.0.0 overlay**: header run label ("Profile · VM", dot filled with VM / amber ring without) + `MoreMenu` (Startup, Rebuild stats, Hidden issues, Encyclopedia, Guide, Copy snapshot, Theme Auto/Light/Dark); severity chips lead the status row (turning one off is refused when no card would remain; the frame rate and the chipless row label are semantics containers); Rebuild stats reads "Off" unless `SleuthController.countsRouteRebuilds` (profile + `enableDeepDebugInstrumentation`); 320 px min width; no footer. `IssueCard`: `SleuthSeverityIcon` (light presets outline warning/OK via `edgeOf`), meta line (category · confidence or Debug timing tag · "seen X of Y scans, label" · effects, Jank/New tags), crosshair 48 px. Expanded: Measured row with `MeasurementGauge` (scale 1.25 × the higher line, 100 for a percent; a value past it fills the track with two slanted cuts; a debug rate critical on its latest window, `HeldRate.latestRate` → `extraTraceArgs.latestRatePerSec`, reads "Critical: the latest second went over the line" instead of the held note), Where (`file:line` per element) / Route / When / Evidence ("... Sleuth rates it possible, since ...") / Check in rows, Copy details and the Ask AI prompt add `measuredSentence` (`copyDetailsText`), Fix with code blocks, buttons Show on screen / Learn more / Ask AI / Copy details / Hide / Not useful, Why it rebuilds (`DebugSnapshot.forcedRebuildsOf`). `status_lines.dart`: Starting / Warming up / No VM link / No issues found / VM link lost / stopped retrying (no VM-lost toast), one debug banner. Highlight flow: crosshair → `selectHighlightForIssue` (`HighlightLookup` found/notFound/pending, no category fallback) → card shrinks to `SleuthHighlightBar` ("Showing X" / "X is off screen", Back to issues, close; edge away from the widget, shield for a double tap, `highlightBarAtTopNotifier` for the label); outline only (markers = "Mark flagged widgets" toggle), in the target issue's severity (`highlightTargetSeverity`), not painted while the widget is off screen; an issue with an `occurrenceId` takes only its element's highlight, a name match skips list-tagged highlights; with the bar up Escape leaves an app dialog or sheet to the app; controller collects highlights while `_highlightTarget` / pending. List cards (`SleuthController.listsWidgets`: always_repaint_painter, missing_repaint_boundary, layout_bottleneck, uncached_images, expensive_gpu_nodes) step through highlights tagged `WidgetHighlight.stableId` + element `occurrenceId`: first on screen, `highlightStepNotifier` (shown place frozen between Next/lookup, refreshed on 0/1/many count change), `nextHighlightStep` (off screen → widget now at its place), `canStepNext`; bar shows "· 1 of 3" + Next and a second row only with Next (GlobalKey keeps Back to issues), shields its old rect when it moves or shrinks. Card list: each card sits in `_CardSlot`, which keeps its `IssueCard` widget while `IssueCard.sameInputsAs` holds (every constructor field; issues by `utils/issue_fingerprint.dart` `issueFingerprint` + `occurrenceId`, callbacks by presence; `issue_card_test` reads the constructor to pin the list) and `_cardEpoch` stays (moves on severity filter / hidden-key change, a change of the shown `(listKey, occurrenceId)` set, `didUpdateWidget`, `didChangeDependencies`, `reassemble`: what `_relatedLinkOf` reads while a card builds); kept cards' callbacks act on the row's latest report (`latest()`). `OverlayUiState.schemaVersion` 2: `loadJson` maps a version 1 hide key without a widget name to an any-widget key (`legacyHideKey`, `base|*` / `base|*!critical`), which `isHiddenBy` / `hideKeysOf` / the Hidden page match for every widget of that id. The Hidden page labels keys with `hideKeyLabel` (`id · Widget`, `id · every widget`, critical suffix left out). Jank tag: `SleuthController.heldJankCauses()` (issues a verdict named in the last `FrameTimingDetector.jankWindow`, recorded by a `verdictNotifier` listener on the controller's monotonic clock or `clockOverrideForTest`, cleared on a route change beside `markRouteEpoch`, so the hold survives closing the dashboard), shown only while a jank symptom is reported; the card arms one timer for `nextExpiry`. Toasts in inverted theme colours (`toastColorsOf`). Example `ext.sleuthDemo.overlay` actions `menu` / `notUseful`. `getGlobalRect` returns null for a non-finite rect (a kept-alive page that is not painted, such as another `TabBarView` tab, maps to NaN), so those widgets get no highlight; `SimpleStructuralDetector` uses it too. `_publishHighlights` publishes what shows: for a highlight measured from a box, `getVisibleGlobalRect` (transformed corners via `MatrixUtils.transformRect`, cut to the ancestors' `describeApproximatePaintClip` in one walk to the root; null when detached, not finite or wholly clipped, such as a list row in the cache extent); `_builtHighlights` keeps the scan's candidates in scan order, hidden ones included (reset by a scan, navigation or collection off), and `refreshHighlightRects` republishes the attached ones, so a row scrolled into view gets its highlight before the next scan. The 1.0 card replaced 0.37 internals named in the v0.37.0 paragraphs below: the frozen zone is `_frozen` (`List<FrozenCard>`, built by `applyFreezeZone` and `_expandInZone`) in place of `_expandedIndices` / `_orderSnapshot` / `_repointExpansions`; the status row's `_SeverityChip`s replace `_IssuesSummaryBar` / `_SeverityChipPill`; the meta line replaces `_badgeLine` / `_inlineBadgesWidth`; `_LeadTrailLine` is gone. Example `FileSleuthStateStore` writes under `overlayStateDirectory` (Android `files` beside `code_cache`, iOS `Library/Application Support` beside `tmp`; installs empty `code_cache`).

**v0.38.0**: detectors-only release on top of 0.37.0. Jank is a missed deadline with Sleuth's own start delay excluded, the VM timeline comes from the `Timeline` stream outside capture mode, new `event_loop_stall`, `material_ui` / `cupertino_ui` support, GC counted once per collection and young-only, `heap_growing` floor leg, 12 more runtimeVerified pairs (27 across 19 stable ids). Sidecar `sleuth_mcp` 0.9.0. The paragraphs below hold the detail per area.

Expand Down
15 changes: 15 additions & 0 deletions doc/release_notes/1.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,21 @@ The full list of changes in sleuth 1.0.0. [CHANGELOG.md](../../CHANGELOG.md) has
tabbed screen could start on another tab's image, say "Showing Image"
and count the images of every tab. `getGlobalRect` now returns null
for a rect that is not finite, as for one it cannot measure.
- A widget that a clip hides gets no outline, marker or step, and an
outline covers only the part of a widget that shows. A list builds rows
past each edge (its cache extent, 250 px by default), and those rows
passed the on-screen check wherever the screen covered them. On a chat
screen, stepping through Oversized Images started on an avatar scrolled
under the app bar, drew its outline over the header and counted the
hidden rows. The overlay now draws only the part of each highlight that
shows. The new `getVisibleGlobalRect` cuts the box to each ancestor's
paint clip (`describeApproximatePaintClip`, which a scroll view answers
with its edge), and a highlight with no part showing is left out. The
controller keeps those hidden highlights while their widget stays
built, so a row that scrolls into view gets its outline while the list
scrolls, without waiting for the next scan. The rect comes from the
box's transformed corners instead of its transformed origin plus its
size, so a flipped or scaled widget is outlined where it shows.

### Theme

Expand Down
Loading
Loading