@celestia-island/hikari 0.55.67 → 0.55.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.55.67",
3
+ "version": "0.55.68",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -332,9 +332,12 @@ describe("useSizeMorph clip reveal", () => {
332
332
  await settle();
333
333
  // The pin landed at the new height with no height animation staged.
334
334
  expect(h.frame.style.height).toBe("360px");
335
- // The sweep runs: end-state clip (promotion stays resident).
335
+ // The sweep runs: end-state clip, carried on a DEMOTED frame (see the
336
+ // demotion note in startRevealSweep). The clip contract is unchanged —
337
+ // only the promotion comes off, so the edge travels as a main-thread
338
+ // paint that a phone GPU cannot drop the sheet's tiles out from under.
336
339
  expect(h.frame.style.clipPath).toBe("inset(0px 0 0 0 round 0px 0px 0px 0px)");
337
- expect(h.frame.style.willChange).toBe("clip-path");
340
+ expect(h.frame.style.willChange).toBe("");
338
341
  expect(h.frame.style.transition).toBe("");
339
342
 
340
343
  fireTransitionEnd(h.frame, "clip-path");
@@ -641,7 +644,9 @@ describe("useSizeMorph clip reveal", () => {
641
644
  h.remeasure();
642
645
  expect(h.frame.style.height).toBe("450px");
643
646
  expect(h.frame.style.clipPath).toBe("inset(70px 0 0 0 round 0px 0px 0px 0px)");
644
- expect(h.frame.style.willChange).toBe("clip-path");
647
+ // Re-staging demotes again, and the remember-what-to-restore answer
648
+ // survives the re-stage (see startRevealSweep's idempotence note).
649
+ expect(h.frame.style.willChange).toBe("");
645
650
  });
646
651
 
647
652
  it("cancels a pending warmup when a second growth re-stages mid-warmup", async () => {
@@ -256,6 +256,11 @@ export function useSizeMorph(
256
256
  let morphDemoted = false;
257
257
  let morphSavedFrameWill = "";
258
258
  let morphSavedRiderWills: Array<[HTMLElement, string]> = [];
259
+ /** The frame's promotion as it stood when the live sweep demoted it (see
260
+ * startRevealSweep): put back on landing, dropped outright when the open
261
+ * cycle unwinds first (clearResidentWill) so a sheet that is closing is
262
+ * never re-promoted by a sweep's own settle. */
263
+ let revealSavedWill = "";
259
264
  /** Identity of the sweep currently staged/folding, echoed to consumers. */
260
265
  let sweepSeq = 0;
261
266
  let activeSweep = 0;
@@ -422,6 +427,12 @@ export function useSizeMorph(
422
427
  } else {
423
428
  revealEl.style.clipPath = "";
424
429
  }
430
+ // Put the sweep's demotion back (see startRevealSweep). Empty means
431
+ // the frame carried no promotion to begin with — nothing to restore.
432
+ if (revealSavedWill) {
433
+ revealEl.style.willChange = revealSavedWill;
434
+ revealSavedWill = "";
435
+ }
425
436
  }
426
437
  const hadSweep = revealEl !== null;
427
438
  const sweptId = activeSweep;
@@ -459,6 +470,9 @@ export function useSizeMorph(
459
470
  morphSavedRiderWills = [];
460
471
  morphSavedFrameWill = "";
461
472
  morphDemoted = false;
473
+ // A sweep still in flight must not put its promotion back on a sheet
474
+ // whose open cycle is already unwinding (mirrors morphSavedFrameWill).
475
+ revealSavedWill = "";
462
476
  for (const el of residentRideEls) {
463
477
  el.style.willChange = "";
464
478
  }
@@ -779,6 +793,31 @@ export function useSizeMorph(
779
793
  });
780
794
  const dir = revealDir;
781
795
  const insetPx = Math.abs(delta);
796
+ // Demote the frame for the whole sweep window — staging, warmup and
797
+ // edge travel. A promoted layer runs the per-frame clip re-raster on
798
+ // the GPU, and a phone GPU drops tiles under it, so the sheet's
799
+ // surface goes missing for those frames and the page behind shows
800
+ // through it (round-23 chest report: 变高必闪、变矮几乎不闪, and the
801
+ // same viewport on a desktop engine does not reproduce at all). The
802
+ // height-morph path has always demoted for exactly this reason
803
+ // ("the height animation's per-frame paint happens on the main
804
+ // thread, which is slower but never leaves a frame where the sheet's
805
+ // surface is missing"); the clip sweep kept its promotion and was
806
+ // the last per-frame GPU re-raster of the full-width sheet. The
807
+ // demotion lands BEFORE the warmup on purpose: those two bus frames
808
+ // are the window the demoted layout's raster needs, exactly as they
809
+ // already are for the staged pin. The promotion goes back on landing
810
+ // (stopReveal). Note this is a per-sweep DEMOTION of a promotion that
811
+ // is still resident from arm time — not the per-sweep promotion
812
+ // #620's round-5 finding ruled out.
813
+ // Idempotent across a re-staged dance: the FIRST staging remembers
814
+ // what to put back and later ones keep that answer — the frame is a
815
+ // clip-mode sheet by construction here, so the resident promotion is
816
+ // `clip-path`. clearResidentWill blanks it outright when the open
817
+ // cycle unwinds first, which is what keeps a closing sheet from
818
+ // being re-promoted by its own sweep's settle.
819
+ if (!revealSavedWill) revealSavedWill = f.style.willChange || "clip-path";
820
+ f.style.willChange = "";
782
821
  let framesLeft = REVEAL_WARMUP_FRAMES;
783
822
  const armWarmup = (): void => {
784
823
  revealWarmup = scheduleFrame(() => {