mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 05:36:17 -08:00
Merge remote-tracking branch 'origin/master' into claude/iptvnator-bug-1894-041e7d
This commit is contained in:
commit
5bd1812cba
17 files changed
+587
-20
No files matched your search
@@ -1288,15 +1288,49 @@ measured iterations of the 11 `master` runs from 2026-10-03 08:25 to
|
||||
| `launch/renderer.layoutShiftScoreSettled` | 0 | 0.235 in some iterations of 8 runs up to 2026-10-02 (hero flicker, #1782) |
|
||||
| `open-source/main.mockHttpRequestsToSettled` | 1 | identical |
|
||||
| `open-source/renderer.ipcCallsToFirstPage` | 17 | identical |
|
||||
| `open-source/renderer.layoutShiftScore` | 0.233 | 0.221, then 0.222; 0.233 since #1814, never mixed within a run |
|
||||
| `open-source/renderer.layoutShiftScore` | 0 | 0.221, then 0.222; 0.233 since #1814; 0 since #1895 (see below) |
|
||||
| `playback/renderer.httpRequestsToPlaying` | 2 | identical |
|
||||
| `playback/renderer.layoutShiftScore` | 0.001 | identical |
|
||||
|
||||
`open-source/renderer.layoutShiftScore` read 0.222 in that window and 0.233
|
||||
in every iteration of every `master` run from 84aef83a6 (#1814, page Back
|
||||
buttons moved into the header) on, so its value is 0.233 with `evidenceRun`
|
||||
37372780064 (b78224376). The 0.011 that #1814 added is not explained yet;
|
||||
lowering it back is a separate change.
|
||||
buttons moved into the header) on, so its value was 0.233 with `evidenceRun`
|
||||
37372780064 (b78224376). Since #1895 it is 0 (`evidenceRun` 38147072417:
|
||||
all 5 measured iterations on the runner read 0 with no shift recorded).
|
||||
|
||||
Where the 0.233 came from (`evidence.layoutShift.shifts`, a 1280×720 window;
|
||||
the score of a shift is the area of every node that moved times the largest
|
||||
distance any of them moved, as a fraction of the viewport):
|
||||
|
||||
| Shift, ms after the click | Nodes that moved | Score |
|
||||
| ------------------------- | ---------------------------------------------------------- | ------ |
|
||||
| ~40 (page swap) | `main.workspace-content` x 60 → 383, width −323 | 0.222 |
|
||||
| | switcher, `label.search-field`, `div.header-actions` x +44 | 0.011 |
|
||||
| ~50-70 (categories) | `div.context-header__actions` x −60, width +60 | 0.0001 |
|
||||
|
||||
The first two are one frame: the frame that replaces the dashboard with the
|
||||
portal page also inserts the 323 px context panel the dashboard has no room
|
||||
for, and the history Back that #1814 shows once an in-app previous entry
|
||||
exists. The header items move only 44 px, but a shift is scored with the
|
||||
largest distance in it, the content pane's 323 px, which is why Back added
|
||||
0.011 rather than about 0.0015. The third is the category search and sort
|
||||
buttons appearing when the categories arrive. The skeletons of the category
|
||||
list and the grid are replaced by content without any shift. Nothing was
|
||||
hidden behind the startup overlay: the "Syncing playlist" overlay of the
|
||||
cached catalog load flashes for about two frames after the shift and covers
|
||||
no further one.
|
||||
|
||||
The content frame and the header controls now slide into place instead of
|
||||
jumping ([Layout slides](./workspace-shell.md#layout-slides)): the new layout
|
||||
applies in the same frame and only a transform animates, which Chromium does
|
||||
not record as a layout shift. The category search and sort buttons render
|
||||
disabled while the categories load. Locally (macOS, 2026-10-11) and on the runner
|
||||
J2 then read 0 in every iteration with no shift recorded, J1 0 / 0, J3 0.001
|
||||
and J4 0;
|
||||
`renderer.domMutationsToFirstPage` rose by 22 (the slide's class and style
|
||||
changes) and the wall-clock entries did not move. Under
|
||||
`prefers-reduced-motion` there is no slide, so a user with that preference
|
||||
still sees the jump that the journey no longer measures.
|
||||
|
||||
Being deterministic is not the same as being validated. Principle 3 of the
|
||||
plan promotes a counter to a guardrail once a PR has shown that lowering it
|
||||
|
||||
@@ -127,6 +127,57 @@ synthesized mask, unlike any content layer) may span half the content area,
|
||||
and the backdrop-filtered controls must really be composited, so the check
|
||||
cannot pass on a page with nothing to clip.
|
||||
|
||||
### Layout slides
|
||||
|
||||
When a route change adds or removes the context panel, the content frame
|
||||
moves by the panel's width (323 px on desktop), and when Back or the phone
|
||||
drawer toggle appears or leaves, the header controls after it move by the
|
||||
slot's width (44 px). Both happen in the frame that swaps the page, so they
|
||||
used to be one jump: a layout shift of 0.233 in the open-source journey,
|
||||
0.222 of it the content frame (see
|
||||
[performance journeys](./performance-journeys.md#j2-open-source-open-a-source-to-a-browsable-list)).
|
||||
|
||||
`LayoutSlideDirective` (`workspace-shell/layout-slide.directive.ts`) turns
|
||||
these jumps into a 180 ms slide, the duration of the context panel's own
|
||||
width transition. The new layout applies at once; each target starts
|
||||
translated back to the position it had and animates its transform to none
|
||||
(FLIP), so nothing waits for the animation and Chromium records no layout
|
||||
shift for a transform. The shell's `.workspace-body` slides
|
||||
`.workspace-content-frame` when `showContextPanel` changes; the header slides
|
||||
the drawer toggle, switcher, search and action groups when its leading
|
||||
navigation (Back, drawer toggle) changes.
|
||||
|
||||
1. **Previous positions.** `offsetLeft` values, which ignore transforms, are
|
||||
recorded after every render in which the slide key or the record key
|
||||
changed (the header's record key covers the other inputs that move its
|
||||
controls: settings route, no playlists, parental lock, shortcuts) and
|
||||
when the host or a target resizes (window resize, a context panel folding
|
||||
or being dragged wider). A slide therefore starts where the target was
|
||||
last laid out, and a target that has no recorded position or did not move
|
||||
is left alone.
|
||||
2. **The uncovered strip.** The frame is narrower beside the panel than
|
||||
without it, so the side it moves away from would show the body colour
|
||||
during the slide. While a slide runs the target carries
|
||||
`layout-slide-active` and the uncovered widths as `--layout-slide-start`
|
||||
and `--layout-slide-end`; the frame's stylesheet extends the content
|
||||
surface over that strip with an `::after` below the scroller.
|
||||
3. **Back fades in** over the same 180 ms, so the switcher still sliding away
|
||||
from its slot does not draw over the arrow.
|
||||
4. **Reduced motion.** Under `prefers-reduced-motion` there is no animation:
|
||||
the jump (and its layout shift) remains.
|
||||
5. **Compositing.** While it runs, each slid target is its own composited
|
||||
layer (`ActiveTransformAnimation`): the frame at its width plus the
|
||||
covered strip (1220×664 CSS pixels at 1280×720), the header controls a
|
||||
few small ones. No synthesized clip mask appears, because no ancestor of
|
||||
the frame clips with a radius ([content surface
|
||||
corner](#content-surface-corner)), and the layers go away when the slide
|
||||
ends: `perf:compositing` reads the same layer and mask counts on every
|
||||
route as before the slides (#1895).
|
||||
6. **Not covered.** A slide does not change what the route renders. Going
|
||||
back from a portal to the dashboard still shows one frame with the new
|
||||
dashboard below the old page before the old page leaves; that jump is a
|
||||
routing issue, not a layout slide.
|
||||
|
||||
`WorkspaceShellComponent` binds only to `WorkspaceShellFacade`. The facade is
|
||||
kept as a thin template-facing API and delegates ownership to component-scoped
|
||||
services:
|
||||
@@ -215,9 +266,10 @@ return logic (`backClicked`).
|
||||
|
||||
When there is nowhere to go, the slot is empty rather than a disabled arrow.
|
||||
Sessions often start on a page that never navigates (an M3U playlist or live
|
||||
TV), where a disabled arrow would stay for the whole session. The cost is one
|
||||
shift of the switcher and search when Back first appears or leaves, which
|
||||
happens only at the start of the history and together with a route change.
|
||||
TV), where a disabled arrow would stay for the whole session. When Back first
|
||||
appears or leaves, which happens only at the start of the history and together
|
||||
with a route change, the switcher and search slide by the slot's width instead
|
||||
of jumping (see [Layout slides](#layout-slides)).
|
||||
There is no Forward button: Stalker inline details are store state, not
|
||||
history entries, so Forward would skip them.
|
||||
|
||||
@@ -263,7 +315,10 @@ Xtream and Stalker category panels preserve provider/server category order by
|
||||
default. The panel header exposes a sort menu next to category search with
|
||||
`Server sorting`, `A-Z`, and `Z-A`; when alphabetical sorting is active,
|
||||
synthetic "all categories" entries stay pinned before sorted provider
|
||||
categories.
|
||||
categories. Search and sort render disabled while the categories load (beside
|
||||
the disabled Manage categories button), so the header row has its final width
|
||||
under the skeleton instead of growing by two buttons when the list arrives;
|
||||
they are hidden only once a load ended with no categories.
|
||||
|
||||
On live sections the category panel can be folded away independently of the
|
||||
channels list; the folded panel is reachable as a popover from the channels
|
||||
|
||||
Reference in new issue
Block a user