mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-02 12:33:56 +08:00
✨ Sync 2026-08-20 17:28:50
This commit is contained in:
1 parent
2a2f7e4252
commit
6b1fd2d7c3
22 files changed
+268
-226
No files matched your search
@@ -338,6 +338,12 @@ let _restAt = null, _restPage = null;
|
||||
function pageStamp() {
|
||||
return (document.body && document.body.getAttribute('data-page')) || '';
|
||||
}
|
||||
/* -> the memo is void: whoever calls this owns the offset now (see the export below) */
|
||||
function forgetRest() {
|
||||
_rest = null;
|
||||
_restAt = null;
|
||||
_restPage = null;
|
||||
}
|
||||
function rememberRest() {
|
||||
if (ENGINE_ANCHORS || scrolling()) return;
|
||||
const ref = anchorRef();
|
||||
@@ -588,6 +594,17 @@ return baseclass.extend({
|
||||
scrolling,
|
||||
deferMeasurement,
|
||||
|
||||
/* "THE OFFSET IS MINE NOW, FORGET WHAT YOU REMEMBERED." Called by fs-router where it resets both
|
||||
* scrollers for an incoming page, and it is not a courtesy — without it the correction below
|
||||
* fights that reset. The router resets synchronously and stamps `body[data-page]` an await later,
|
||||
* so between the two there is a window, as long as the incoming view takes to arrive, in which a
|
||||
* poll tick from the OUTGOING page still fires: the offset is 0, the remembered one is where the
|
||||
* reader was, nobody is scrolling and the stamp still names the page they came from — every term
|
||||
* of "the engine clamped this" is true, and the reader would be dragged back down a page they
|
||||
* have left, with the new page committing mid-scroll. Reported in review on the upstream
|
||||
* proposal. The stamp cannot close it alone, because it is written afterwards. */
|
||||
forgetRest,
|
||||
|
||||
/* Raise the stylesheet's "an unanswered table takes no room" rule. Called by the module that
|
||||
* answers — see armGate above; nothing else may call it. */
|
||||
armGate,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
*
|
||||
* WHAT THAT COST, and why the code below looks the way it does: the old location bought two timing
|
||||
* guarantees for free, because LuCI evaluated the file INSIDE index.load(). Both had to be paid for
|
||||
* explicitly — see ensureOverviewHelpers() and patchOverview() at the bottom. */
|
||||
* explicitly — see patchOverview() below and ensureOverviewHelpers() in menu-footstrap-common.js. */
|
||||
/* section title -> grid role. _() with NO msgctxt on purpose: these must resolve to exactly what
|
||||
* luci-mod-status resolves to, or the titles stop matching. Built once — it used to cost an
|
||||
* allocation plus three _() lookups per poll tick. */
|
||||
@@ -191,44 +191,10 @@ function fillSection(inc, container, res) {
|
||||
container.parentNode.style.display = '';
|
||||
container.parentNode.classList.add('fade-in');
|
||||
if (!inc.hide)
|
||||
swapContent(container, content);
|
||||
dom.content(container, content);
|
||||
}
|
||||
}
|
||||
|
||||
/* WHAT `dom.content()` DOES TO A READER TWO SCREENS DOWN, and the reason a section is filled through
|
||||
* here rather than through it directly.
|
||||
*
|
||||
* `dom.content()` removes every child and then appends the new ones. Between those two halves the
|
||||
* section has NO height, and a document that just lost several hundred pixels is a document the
|
||||
* engine clamps the scroll offset into: the offset drops to whatever length is left, the section
|
||||
* fills again, and nothing puts the offset back. The reader is now somewhere else, once a second,
|
||||
* for as long as the Overview is open. Reported from Safari on macOS and iOS with the offset moving
|
||||
* 200-887px per tick, and reproduced in WebKit with the engine's own scroll anchoring off: a 30-row
|
||||
* section swapped for a 35-row one two screens above the reader moved the page 255px under them —
|
||||
* 0px with the height held across the swap.
|
||||
*
|
||||
* fs-fit's anchoring covers the same fault from the other side (it now recognises a clamped offset
|
||||
* and puts the reader back), and it has to: every OTHER page's poll calls `dom.content()` too and no
|
||||
* theme code is in that path. This is the half that can be prevented rather than corrected, and
|
||||
* prevention is worth the line — a correction is a scroll the reader did not ask for, and on iOS it
|
||||
* lands in the middle of whatever momentum the page still has.
|
||||
*
|
||||
* The cost is one `offsetHeight` per filled section per tick — a forced layout, which this theme
|
||||
* spends carefully (fs-fit.js). It is paid because the alternative is not "no layout" but "a layout
|
||||
* whose result is the page jumping": the swap dirties this container anyway.
|
||||
*
|
||||
* The pin is released in the same call, so nothing is left to a later frame that could take the
|
||||
* minimum with it if the next tick throws — and a section that legitimately got shorter is shorter
|
||||
* again by the time this returns. */
|
||||
function swapContent(container, content) {
|
||||
const hold = container.offsetHeight;
|
||||
const prev = container.style.minHeight;
|
||||
if (hold > 0)
|
||||
container.style.minHeight = hold + 'px';
|
||||
try { dom.content(container, content); }
|
||||
finally { if (hold > 0) container.style.minHeight = prev; }
|
||||
}
|
||||
|
||||
let _inflight = null;
|
||||
/* WHICH containers the in-flight run is filling. The guard below is module-level because the
|
||||
* duplicate load it kills is module-level, but the frames are per RENDER — so joining a run blindly
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
'require fs-menutree as tree';
|
||||
'require fs-chrome as chrome';
|
||||
'require fs-sheets as sheets';
|
||||
'require fs-fit as fit';
|
||||
|
||||
/* ---- SPA client router ----
|
||||
*
|
||||
@@ -1091,6 +1092,12 @@ function navigate(pathname, push, kbd) {
|
||||
* swap and is undone by it (see restoreScroll above), so it neither helps nor hurts, and 'manual' would only take away
|
||||
* the case that does work — a genuine full load. */
|
||||
if (push) {
|
||||
/* BEFORE the two writes, and the order is the point: fs-fit keeps the offset the reader was
|
||||
* last still at, so it can tell an engine's clamp from a reader who moved. This reset is
|
||||
* neither, and it lands a whole require ahead of the `data-page` stamp fs-fit would otherwise
|
||||
* notice it by — so a poll tick from the page being left would read the reset as a clamp and
|
||||
* put the reader back on it. Told rather than inferred. */
|
||||
fit.forgetRest();
|
||||
window.scrollTo(0, 0);
|
||||
const sc = document.getElementById('maincontent');
|
||||
if (sc) sc.scrollTo(0, 0);
|
||||
|
||||
Reference in new issue
Block a user