T-541 DONE: a fast flick on an image-heavy page no longer lands on grey — the GPU displayport at rest is a height each way (JIHAD_GPU_DP_Y 0.25 → 0.75; the A/B on desktop Wikipedia cost nothing: 80 vs 74 express frames over the same five flicks, the main lane 5-8 fps either way) and a fling adds a lead of up to a height in its direction from the card's scroll speed (BrowserPageGoanna's velocity → SetGpuLead → asymmetric margins); about:personas' flicks by finger: the grey lower third and the white upper third are gone, a sliver at the very bottom on the first flick after load

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
NotAlexNoyleandClaude Fable 5.1 committed 2026-10-25 19:30:59 -04:00
1 parent 5f042ccfe0
commit c0aee8d02b
7 files changed
+54 -6

No files matched your search

+1
View File
@@ -80,6 +80,7 @@ Hacker News 36-37, desktop Wikipedia 13-14 (software 9-12.4).
**Dependencies:** R2
### R6: Zoom keeps up (the owner's finger test, 2026-10-18)
*(2026-10-25, T-541: the displayport margin at rest is a height each way (was half); a fling adds up to a height ahead from the card's scroll speed (BrowserPageGoanna's velocity → GoannaRenderPage::SetGpuLead). Measured: about:personas' flicks no longer land on grey; desktop Wikipedia unchanged (80 vs 74 express frames over the same flicks).)*
**Description:** A pinch or a double-tap zoom shows the page at the new zoom promptly and never leaves part of the card grey.
**Acceptance Criteria:**
- [x] A scripted pinch in to about 1.7 and back out to the fit zoom (and a double-tap zoom) on desktop en.wikipedia.org and on `jihad-long-static.html` is reproduced on the bench with the frame timeline logged *(2026-10-18: the card's own gesture calls through its test hook, `g` pinches and `t` double taps; Wikipedia 0.69 → 1.23 → 2.20 → 1.51 → 1.03, double tap 1.26 and back to 0.69; the test page 0.79 → 1.41 → 2.51 → 1.72 → 1.18 and its double taps; each zoom's "the first frame at the new zoom is out" line)*
+1 -1
View File
@@ -547,7 +547,7 @@ Commits are SSH-signed since 2026-10-21 and wait on the owner's passphrase (memo
| T-538 | A field inside a frame by finger: the daemon follows a tapped frame into its document for the edit target (jihadIsInsideFrameOf) and re-raises the keyboard on a tap on the frame of the current target; the followed layout width (T-499) survives the keyboard's height-only resize, so a fit-zoomed page neither re-flows nor re-zooms around the keyboard | cavekit-chrome-look.md, cavekit-input-bridging.md | R4, R2 | T-535 | yes | M | DONE 2026-10-25 |
| T-539 | The slow-script dialog by finger: the card's confirm names the engine's buttons in its text (OK = Continue, Cancel = Stop script); Cancel stops the script and the card stays usable | cavekit-modern-web.md | R2 | T-535 | yes | S | DONE 2026-10-25 |
| T-540 | A dialog whose card closes meanwhile holds the daemon's main thread for the whole 60 s reply wait (the YAP send fails with EPIPE but connected() stays true): make the wait end when the send failed or the client is gone | cavekit-ui-shell.md | R3 | T-539 | yes | S | DONE 2026-10-25 |
| T-541 | A fast flick beyond the displayport shows the uncovered rows grey/white for up to a second on an image-heavy page (about:personas by finger, 2026-10-25); the plain long page does not — grow the displayport in a fling's direction by its distance, or compose a wider margin while a fling runs | cavekit-gpu-rendering.md | R6, R12 | T-527 | yes | M | pending |
| T-541 | A fast flick beyond the displayport showed the uncovered rows grey/white for up to a second on an image-heavy page (about:personas by finger, 2026-10-25). Fixed: the displayport at rest is a height each way (JIHAD_GPU_DP_Y 0.25 → 0.75; the A/B on desktop Wikipedia cost nothing — 80 vs 74 express frames over the same five flicks, the main lane 5-8 fps either way) and a fling adds a lead of up to one height in its direction from the card's scroll speed; the same flicks on about:personas land on painted rows | cavekit-gpu-rendering.md | R6, R12 | T-527 | yes | M | DONE 2026-10-25 |
| T-542 | A Persona installed from a .xpi file showed "Author unknown" and seemed to lack its card picture (Carbon Light from render/goanna/test/personas, by finger 2026-10-25) — NO DEFECT: the fixture's manifest has no author, and its header.png is a flat light grey (3000×200, grey 180), which IS what the card shows; the header file landed in the profile and the card uses it directly (a small header needs no thumb) | cavekit-personas-ui.md | R6, R8 | T-535 | yes | S | DONE 2026-10-25 (no defect) |
| T-521 | The engine's review fixes folded: the signed engine commit (patch 0058), `patch-queue.sh --add`, `update-engine.sh --pins`, ENGINE-SOURCE's row, `check-engine-pins.sh` and `patch-queue.sh --check` agree; the Jihad-Browser commits prepared as one chain for the owner's passphrase | cavekit-gpu-rendering.md | R11 | T-518, T-519, T-520, T-522 | no | S | DONE 2026-10-24 13:49 (engine `7a872e8f`, Jihad-Browser `506c4114`, `3f4e1a27`, `318869f2`; unsigned by the owner's rule — the Gitea key is Gitea's only, `commit.gpgsign false` per repo; the pins check all agree, 58 patches reproduce the pin; the push is the owner's: `git -C third_party/uxp push fork jihad/uxp-RB_20261006`, `git push origin main`. The first chain run's story: the chain `.cavekit/handoff-2026-10-24/t521-chain.sh` — the engine commit, `patch-queue.sh --add`, `update-engine.sh --pins`, the checks, then commits A/B/C with the shared files split by hunk — stopped at its first prompt with "incorrect passphrase supplied", nothing staged or committed; run it again, four prompts, or with the key in an agent, then push both repos) |
+2 -2
View File
@@ -81,8 +81,8 @@ Run each item in all three variants (Enyo, Mochi, Mojo) unless it says otherwise
(mojo-ui R6) *(2026-10-24: one tap selects the whole address, "webos" typed on the keyboard replaces it, Enter opens lite.duckduckgo.com/lite/?q=webos; the results were on screen at the 11 s shot and not at 3 s — the "before the whole page" half needs a finer clock than a 2 s screenshot)*
## Personas and add-ons
- [~] about:personas: no card flashes while the page is open or scrolled (Carbon Light 2.0 was the
example); cards show their pictures sharply. (personas-ui R9) *(2026-10-25, Enyo by finger: at rest and after a slow scroll the cards and their pictures are steady and sharp (four shots); no card blinked. What DOES show: a fast flick far down an image-heavy page leaves the newly uncovered rows GREY (a whole grey card once, the lower 40% once, a white band at the top after a flick up) for up to a second until the main thread paints them — the GPU path's displayport (0.5 heights) does not reach a flick's landing place; the plain long page shows none of it. T-541 (the displayport should grow in a fling's direction). Also seen: Carbon Light reinstalled from its .xpi reads "Author unknown" — the fixture's manifest has no author, and its picture is a flat light grey, which is what its card shows (T-542, no defect).)*
- [x] about:personas: no card flashes while the page is open or scrolled (Carbon Light 2.0 was the
example); cards show their pictures sharply. (personas-ui R9) *(2026-10-25, Enyo by finger: at rest and after a slow scroll the cards and their pictures are steady and sharp (four shots); no card blinked. What DOES show: a fast flick far down an image-heavy page leaves the newly uncovered rows GREY (a whole grey card once, the lower 40% once, a white band at the top after a flick up) for up to a second until the main thread paints them — the GPU path's displayport (0.5 heights) does not reach a flick's landing place; the plain long page shows none of it. T-541, FIXED the same day: the displayport is a height each way at rest (half before) and a fling adds a lead of up to a height in its direction; the same flicks then land on painted rows (three shots at 0 s, no grey, no white band), and desktop Wikipedia's flicks cost the same. Also seen: Carbon Light reinstalled from its .xpi reads "Author unknown" — the fixture's manifest has no author, and its picture is a flat light grey, which is what its card shows (T-542, no defect).)*
- [x] Right after installing a Persona with a large picture, tap its Remove once ("Remove it?") and
wait a few seconds: the question stays until answered while its card picture is made. (personas-ui
R9, round 22) *(2026-10-25, Enyo by finger: Autumn Forest 2.0 (a photo header) worn from the site section of about:personas (the question answered OK), then — two flicks to the top and at once — its Remove: "Remove it?" showed at 1 s and still at 4 s, the card's picture already made beside it; the second tap removed it (theme null, the header file removed); the Default look back.)*
+28
View File
@@ -1282,6 +1282,29 @@ void BrowserPageGoanna::setScrollPosition(int x, int y) {
// T-502: the compositor applies the card's scroll (CSS px = zoomed px over the zoom);
// the engine is scrolled only to re-anchor the displayport (gpuScrollTo).
double zg = (mZoom >= 0.05 && mZoom <= 20.0) ? mZoom : 1.0;
// T-541: the scroll's speed from the card's stream of positions, smoothed; the displayport
// gets a lead in the direction of travel of up to 1 height (what ~0.5 s of this speed
// covers), 0 again once the stream stops (maybePaint).
{
const int64_t now = mLastScrollMs;
const int64_t dt = mVelMs ? now - mVelMs : 0;
if (dt > 0 && dt < 250) {
const double v = (double)(y - mVelY) / (double)dt; // card px per ms
mScrollVelY = 0.5 * mScrollVelY + 0.5 * v;
} else if (dt >= 250) {
mScrollVelY = 0.0;
}
mVelMs = now; mVelY = y;
const double hpx = mLastWinH > 0 ? (double)mLastWinH : (mPage->Height() > 0 ? (double)mPage->Height() : 1024.0);
double lead = fabs(mScrollVelY) * 500.0 / hpx;
if (lead > 1.0) lead = 1.0; // on top of the height at rest: 2 heights ahead while flinging
if (lead < 0.15) lead = 0.0;
const float up = (float)(mScrollVelY < 0 ? lead : 0.0), down = (float)(mScrollVelY > 0 ? lead : 0.0);
if (fabs(up - mGpuLeadUp) > 0.25 || fabs(down - mGpuLeadDown) > 0.25) {
mGpuLeadUp = up; mGpuLeadDown = down; mPage->SetGpuLead(up, down);
fprintf(stderr, "[jihad-bs] fling lead: up %.2f down %.2f heights (%.2f card px/ms)\n", up, down, mScrollVelY);
}
}
mAdapterScrollX = x; mAdapterScrollY = y;
mPage->SetRenderPan(0.0, 0.0);
gpuScrollTo(x, y, zg);
@@ -3549,6 +3572,11 @@ void BrowserPageGoanna::pump(int msBudget, bool drainOnly) {
// not returned the buffer, and frames actually painted.
void BrowserPageGoanna::maybePaint() {
if (!mGpuJobs.empty()) mGpuTrace[6]++;
// T-541: the fling lead goes once the card has stopped streaming positions.
if ((mGpuLeadUp != 0.0f || mGpuLeadDown != 0.0f) && mPage && jihadNowMs() - mLastScrollMs > 400) {
mGpuLeadUp = mGpuLeadDown = 0.0f; mScrollVelY = 0.0; mVelMs = 0; mPage->SetGpuLead(0.0f, 0.0f);
fprintf(stderr, "[jihad-bs] fling lead: off\n");
}
if (jihad_offscreen_late_writes) {
// T-513 (review): a GPU read given up past its bound came back and wrote into a slot the
// software path may have painted (and the card may show): every slot painted whole again
+2
View File
@@ -403,6 +403,8 @@ private:
bool mGpuAnchorValid = false;
double mGpuDpShiftX = 0.0, mGpuDpShiftY = 0.0; // T-524: the shift the displayport was set with
double mGpuDpZoom = 0.0; // T-524: and the zoom
int64_t mVelMs = 0; int mVelY = 0; double mScrollVelY = 0.0; // T-541: the card's scroll speed (card px/ms)
float mGpuLeadUp = 0.0f, mGpuLeadDown = 0.0f; // T-541: the lead last handed to the page
bool mGpuCompositorScroll = false; // the compositor applies the card's scroll (frames at the card's y)
// T-502 round 112: the compositor-side scroll is engaged by a card-driven pan only. A
// setScrollPosition that follows a position this daemon just reported (msgScrolledTo) is the
+15 -3
View File
@@ -4701,7 +4701,12 @@ bool GoannaRenderPage::SetGpuViewport(double zoom) {
// is the anchor distance (JIHAD_GPU_ANCHOR, 0.25 heights) plus JIHAD_GPU_DP_Y (0.25 heights);
// sideways JIHAD_GPU_DP_X widths (0, half a width when zoomed in); layout adds what is shown
// beyond the scroll port (jihad_layout_set_displayport)
static const float sDpY = getenv("JIHAD_GPU_DP_Y") ? (float)atof(getenv("JIHAD_GPU_DP_Y")) : 0.25f;
// T-541: a height of displayport each way at rest (the anchor's 0.25 + 0.75). With 0.25 (half a
// height) a fast flick on an image-heavy page landed past the painted rows and showed grey for
// up to a second (about:personas by finger, 2026-10-25); with 0.75 the same flicks landed on
// painted rows, and desktop Wikipedia's flicks cost the same (A/B: 80 vs 74 express frames over
// the same five flicks, the main lane 5-8 fps either way).
static const float sDpY = getenv("JIHAD_GPU_DP_Y") ? (float)atof(getenv("JIHAD_GPU_DP_Y")) : 0.75f;
static const float sDpX = getenv("JIHAD_GPU_DP_X") ? (float)atof(getenv("JIHAD_GPU_DP_X")) : -1.0f;
static const float sAnchorY = getenv("JIHAD_GPU_ANCHOR") ? (float)atof(getenv("JIHAD_GPU_ANCHOR")) : 0.25f;
const float visW = (float)mWidth / z, visH = (float)mHeight / z;
@@ -4743,8 +4748,15 @@ bool GoannaRenderPage::SetGpuViewport(double zoom) {
// set the displayport; frames built meanwhile kept it blank, and a zoom out left grey areas
if (!sNoDp) {
const float smx = mGpuDpWanted ? mx : 0.0f, smy = mGpuDpWanted ? my : 0.0f;
if (jihad_layout_set_displayport2 && ds) dp = jihad_layout_set_displayport2(ds, visW, visH, (float)mGpuShiftX, (float)mGpuShiftY, smx, smy, smy);
else if (jihad_layout_set_displayport && ds) dp = jihad_layout_set_displayport(ds, visW, visH, smx, smy, smy);
// T-541: a fling lands past the symmetric margin, and what it uncovers stays grey until the
// main thread paints it (about:personas by finger, 2026-10-25: a whole grey card once, the
// lower 40% once, a white band after a flick up). While the card streams a fast scroll the
// daemon adds a lead in the direction of travel, up to 1.5 heights (BrowserPageGoanna's
// velocity); the margin against the motion stays what it was.
const float smyT = mGpuDpWanted ? my + visH * mGpuLeadUp : 0.0f;
const float smyB = mGpuDpWanted ? my + visH * mGpuLeadDown : 0.0f;
if (jihad_layout_set_displayport2 && ds) dp = jihad_layout_set_displayport2(ds, visW, visH, (float)mGpuShiftX, (float)mGpuShiftY, smx, smyT, smyB);
else if (jihad_layout_set_displayport && ds) dp = jihad_layout_set_displayport(ds, visW, visH, smx, smyT > smyB ? smyT : smyB, smyT > smyB ? smyT : smyB);
else if (root && mGpuDpWanted) { r3 = u->SetDisplayPortMarginsForElement(mx, my, mx, my, root, 0); dp = NS_SUCCEEDED(r3) ? 0 : 9; }
}
const bool ok = dp == 0;
+5
View File
@@ -549,6 +549,10 @@ public:
bool SetGpuViewport(double zoom);
// T-524: where what the card shows sits from the engine's scroll (CSS px), for the displayport
void SetGpuShift(double x, double y) { mGpuShiftX = x; mGpuShiftY = y; }
// T-541: extra displayport ahead of a fling, in heights above and below what is shown (0 at rest).
void SetGpuLead(float up, float down) { mGpuLeadUp = up; mGpuLeadDown = down; }
float GpuLeadUp() const { return mGpuLeadUp; }
float GpuLeadDown() const { return mGpuLeadDown; }
// T-502: the engine's scroll position (CSS px) after a ScrollTo, and the compositor's view:
// the zoom and the residual visual offset the engine scroll could not reach (pinch-in pans).
bool GetEngineScroll(int* x, int* y);
@@ -573,6 +577,7 @@ private:
int mWidth;
int mHeight;
double mGpuShiftX = 0.0, mGpuShiftY = 0.0; // T-524: the compositor's shift the displayport is set for
float mGpuLeadUp = 0.0f, mGpuLeadDown = 0.0f; // T-541: the fling lead, in heights
int mLayoutW = 0, mLayoutH = 0; // T-499: the layout viewport when it differs from the card (0 = the card)
double mRenderZoom = 1.0; // pinch/fit zoom applied as a gfxContext scale in the offscreen capture
double mPanX = 0.0; // visual-viewport pan (CSS px) used by the render when zoomed (>1)