Repository navigation
feat(nomadnet): inline page images — parser, WebP decode, /media transport, policy + progress UI, render fixes - #98
Merged
Conversation
…sport, LVGL render Cumulative candidate on top of origin/main (7ae47d5). Slices 1-5: compact-model image records, vendored decode-only libwebp, wire contract + loader state machine, owner-loop /media dispatch with retained page-Link reuse, screen render with bounded PSRAM LRU pixel store. Local checkpoint only — not pushed.
…xes, media flow gate Finishing work on the inline page-image feature (929fa2c): - Settings: new Browser card with the page-image loading policy (never/manual/auto/always, upstream image_loading), persisted in NVS (img_load, default auto); takes effect on the next page load. - Loader: strict sequential /media fetch with per-image terminal failure semantics, progress-aware deadline (base window + RTT slack, refreshed by per-part transfer progress), and a manual "Load images" action surfaced as a browser-chrome button (no title overlap). - Mailbox: PROGRESS events carry percent + advertised total (monotonic). - Decoder: LV_COLOR_16_SWAP byte-swap on-device for lv_draw_img_decoded buffers (host tests keep mathematical RGB565). - Render: decoded images blit via lv_draw_img_decoded with a uniform two-axis 8.8 zoom (no upscale) sized from the placement box; coords carries the native source dimensions (LVGL SW reads coords size as src_w/src_h/stride) with the native top-left anchored to center the scaled image. A width-only zoom made height-constrained boxes overflow the content edge; a display-size coords made LVGL stride a wide buffer as narrow, rendering a diagonal offset. - Progress UI: thin full-width bar under the header (overall page fraction) plus status line "Image N of M - % - KB". - Fork pin: microReticulum 921b3aa -> 0487775 (media Resource response fix + jobs()/outbound() self-re-entrancy guard) at all six sites. - Tests: x86 real-peer flow gains the media scenario (raw file-response delivery, 12/12), reference bumped to NomadNet 1.4.3 (e1e8ab8) / RNS 1.5.4 with hash-pinned files; CI installs matching rns/nomadnet and the flow timeout is raised to 300 s. - Diagnostics: T:IMG_STATE serial dump (loader entries + decode slots), test-hook gated and stripped from tdeck-release (audit-verified).
Contributor
|
| int16_t table_index = -1; | ||
| int16_t partial_index = -1; | ||
| int16_t partial_region_index = -1; | ||
| int16_t image_index = -1; |
Contributor
There was a problem hiding this comment.
When a dynamic partial is refreshed, assign_replacing_partial() rebuilds every retained block but does not copy or remap image records. Both images in the fragment and existing images elsewhere on the page therefore lose their image_index association and fall back to placeholders after the refresh. The replacement path needs to preserve image records and remap their indices.
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/tdeck_ui/UI/LXMF/NomadNetCompactPage.h
Line: 149
Comment:
**Partial Refresh Drops Images**
When a dynamic partial is refreshed, `assign_replacing_partial()` rebuilds every retained block but does not copy or remap image records. Both images in the fragment and existing images elsewhere on the page therefore lose their `image_index` association and fall back to placeholders after the refresh. The replacement path needs to preserve image records and remap their indices.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…n new page Greptile P1 fixes on PR #98: 1. Partial refresh dropped images: assign_replacing_partial() cleared _images but never re-copied them, so every image block (retained and in-fragment) lost its image_index and fell back to alt-text after any dynamic refresh. Image records are now preserved: base images are re-copied in original order (stable compact index — decode slots and loader entries are keyed by it), fragment images are appended after the base records with an offset remap and render as placeholders (the reference scans for images only at full page load). 2. Navigation retained media requests: nomad_cancel_images() was defined but never called, so an in-flight /media receipt survived into the next page and blocked its image transport. The cancel is wired at nomad_advance_navigation_generation() — the exact new-page boundary (open/home/back-empty/leave-route). Deliberately not in nomad_supersede_transport/nomad_stop_transport, which same-page partial refreshes also use. Tests: partial-core native test now asserts image record survival and index stability across a refresh (fails on the unpatched code); structural test pins the cancel at the generation boundary and asserts it is NOT in the transport teardown paths.
Round-2 Greptile P1 (Partial Images Accumulate): the round-1 fix appended fragment image records to the record list, so repeated refreshes of an image-bearing partial re-appended on each pass and the list grew until LIMIT_EXCEEDED permanently blocked that partial. Correct semantics (reference 1.4.3: detect_images runs only at full page load, never on partial refresh): copy the record list verbatim from the base — same records, same order, same compact indices, constant count across repeated refreshes. Fragment image blocks carry no record (image_index -1) and render as alt-text placeholders until the next full load, consistent with the loader being configured once per page. Test: native partial-core check now requires a constant image count across two refreshes and a placeholder fragment image; verified to fail against the accumulation-buggy build.
Round-3 Greptile P1 (Partial Images Lose Alt Text): the round-2 fix
dropped fragment image records, so a refreshed-region image could only
render its fixed '[Image: loading]' run instead of its real alt text.
Reference 1.4.3 semantics: a partial refresh never fetches images
(detect_images runs only at full page load), but the refreshed region is
re-parsed and its images show their real alt text. Implementation:
- region-owned base records (referenced by the region's image blocks)
are discarded each refresh, keeping the record list bounded;
- all other base records are copied verbatim (stable compact indices,
so decoded images keep rendering);
- fragment records are appended with an offset remap, so a
refreshed-region image renders its real alt text and is never
fetched (no loader entry exists for it).
Also fixes a latent kept-offset bug the new two-refresh test exposed:
the fragment offset used the base record total instead of the kept
count (wrong whenever a region-owned record was discarded).
Test: native partial-core now asserts real alt text after a refresh,
a bounded count across two refreshes (1 retained + 2 region), and stable
retained indices; verified failing against the prior build.
Greptile round-4 residual: "retained image identity is still not preserved when removing an earlier partial-owned image." Decode slots (NomadNetScreen) and loader entries (NomadNetImageLoader) are both keyed by the compact image index, and the loader is configured once at full page load and never re-run on a partial refresh. The previous discard-and-compact design shifted every surviving record after a removed region image, so a decoded slot kept showing the old image's pixels (or nothing) at the wrong index, and loader state pointed at the wrong record. - assign_replacing_partial now keeps the record list at a stable size: every base record keeps its index (verbatim); a region-owned record is replaced IN PLACE with the fragment's record of the same position (real alt text, same index its decode slot is tagged with); fragment images beyond the region's old count are appended; leftover region slots are blanked (empty URL). Bounded, never accumulates, never shifts a surviving index. - Decode slots now tag the record URL at store time (FNV-1a); the draw path rejects a slot whose record URL no longer matches, so an in-place URL change cannot render stale pixels. - The /media RESPONSE branch discards a response whose record was replaced in place while in flight (per-image terminal failure; the page and remaining images are unaffected). Regression: two-refresh shrink scenario pins in-place replacement, blanked leftover record (empty URL), and retained-record index/URL stability. Falsified against the pre-fix discard-compact code (fails). Co-Authored-By: Claude <noreply@anthropic.com>
Greptile round-5 P1: "Blank Slots Exhaust Capacity." A partial that shrinks a region blanks the removed records but leaves them in _images; the reusable-slot scan only saw image blocks still present, so a later regrow could not reuse the blanked records and appended past them. Repeated shrink/grow cycles consumed the bounded record capacity until current images fell back to placeholder text. The reusable pool is now every record NOT still referenced by a surviving non-region image block: region_slots (old region's records, document order, replaced in place) plus dead_slots (records a prior shrink blanked, ascending index, reused before any append). Retained records stay excluded and keep their index, so decoded slots / loader entries still resolve; overflow beyond the pool appends as before and hits MAX_IMAGES with a run-text fallback. Regression: grow(3)/shrink(1) x2.5 cycle pins the record count at the retained+region bound and that the final grow renders every current image (no blank fallback). Falsified against the append-past-blanked code (fails), passes with the fix. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds NomadNet inline page-image rendering end to end on the T-Deck, conforming to upstream NomadNet e1e8ab8 (v1.4.3). Two commits:
/mediaResource transport over the retained page Link, and LVGL render path (slot LRU in PSRAM +lv_draw_img_decoded).What's in the finishing commit
img_load, default auto), effective on next page load. Manual mode surfaces a "Load images" button (placed just right of home, no title overlap)./mediafetch, per-image terminal failure (one bad image never fails the page), progress-aware deadline (base window + RTT slack, refreshed per part).LV_COLOR_16_SWAPbyte-swap on-device forlv_draw_img_decodedbuffers; host tests keep mathematical RGB565.coordsmade the LVGL SW path stride a wide buffer as narrow (the "diagonal offset rainbow").921b3aa→0487775(media Resource response fix + jobs()/outbound() self-re-entrancy guard) at all six sites.mediascenario (raw file-response delivery, per-part progress; 12/12), reference bumped to NomadNet 1.4.3 (e1e8ab8) / RNS 1.5.4 with hash-pinned files; CI installs matchingrns/nomadnetand the flow timeout is raised to 300 s.T:IMG_STATEserial dump (loader entries + decode slots), test-hook gated and stripped fromtdeck-release(audit-verified).Verification
pio run -e tdeckandtdeck-releaseSUCCESS;tools/audit_release_build.pypasses (pins + stripped test hooks/strings confirmed against the release binary).media(raw file-response,resource_progress=1), manifest + reference hash-pinned.pytest tests/build_scripts— 184 passed.Notes
torlando-tech/microReticulum0487775), not upstream, per standing policy.