@dimina-kit/electron-deck 0.1.0-dev.20260711141929 → 0.1.0-dev.20260716153350

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.
@@ -1,4 +1,4 @@
1
- import { a as closePanelForUser, c as extractPanel, d as setActive, g as findGroupById, h as countPanels, m as splitPanel, p as setSizes, u as movePanel, v as findPanelGroupId } from "../layout-D-LYJIpM.js";
1
+ import { _ as countPanels, a as closePanelForUser, b as findPanelGroupId, c as extractPanel, d as setActive, h as splitPanel, p as setSizes, u as movePanel, v as findGroupById } from "../layout-CxEGC10g.js";
2
2
  import { Fragment, createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
3
3
  import { Group, Panel, Separator } from "react-resizable-panels";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -276,20 +276,40 @@ function layoutsEquivalent(a, b, ids, epsilon = LAYOUT_EPSILON) {
276
276
  * Build the panel-ID→PERCENTAGE map for an imperative `setLayout`, given the
277
277
  * model's full-length raw `sizes` (per child) and `constraints`:
278
278
  *
279
- * - FIXED (px-pinned) children keep their CURRENT measured percentage (read
280
- * from the live `getLayout()`); they are NOT derived from weights, so a
281
- * flexible-weights change never disturbs their pixel lock.
279
+ * - FIXED (px-pinned) children normally keep their CURRENT measured
280
+ * percentage (read from the live `getLayout()`) they are NOT derived
281
+ * from weights, so a flexible-weights change never disturbs their pixel
282
+ * lock. When `measured` is supplied, a `fixedPx` child is instead always
283
+ * computed from the real container size (it never legitimately differs
284
+ * from its exact px value), and a `minPx` child is too, but ONLY when
285
+ * `measured.trustLiveForMinPx` is `false` — see `MeasuredContainer`. This
286
+ * matters because right after `<Group>` cold-mounts a `minPx`/`fixedPx`
287
+ * child whose content is itself a NESTED split, rrp's own mount-time
288
+ * px→percentage conversion for that child can land on a degenerate ratio
289
+ * (observed: a pinned child grabbing ~99% while its lone flexible sibling
290
+ * collapses to rrp's floor) — the live layout it reports is simply wrong,
291
+ * and blindly trusting it would perpetuate the collapse forever (there is
292
+ * no subsequent event that would ever correct it).
282
293
  * - The REMAINING percentage (100 − Σ fixed%) is distributed across the
283
294
  * FLEXIBLE children in proportion to their weights.
284
295
  *
285
296
  * Returns `null` when the map can't be built faithfully (e.g. `live` is empty —
286
- * jsdom's stub — or a fixed child's live % is missing), so the caller skips the
287
- * `setLayout` rather than pushing a corrupt total. The result always sums to
288
- * ~100 over all children.
297
+ * jsdom's stub — or a fixed child's live % is missing and no `measured`
298
+ * fallback applies), so the caller skips the `setLayout` rather than pushing a
299
+ * corrupt total. The result always sums to ~100 over all children.
289
300
  */
290
- function buildSetLayoutMap(childIds, sizes, constraints, live) {
301
+ function buildSetLayoutMap(childIds, sizes, constraints, live, measured) {
291
302
  const isFixedAt = (i) => (constraints?.[i] ?? null) !== null;
292
- const fixedTotal = sumFixedLivePercent(childIds, isFixedAt, live);
303
+ const fixedPercentAt = (i) => {
304
+ const constraint = constraints?.[i] ?? null;
305
+ if (measured && measured.containerPx > 0 && constraint) {
306
+ if (constraint.fixedPx != null) return constraint.fixedPx / measured.containerPx * 100;
307
+ if (constraint.minPx != null && !measured.trustLiveForMinPx) return constraint.minPx / measured.containerPx * 100;
308
+ }
309
+ const livePct = live[childIds[i]];
310
+ return typeof livePct === "number" ? livePct : null;
311
+ };
312
+ const fixedTotal = sumFixedPercent(childIds, isFixedAt, fixedPercentAt);
293
313
  if (fixedTotal === null) return null;
294
314
  const remaining = Math.max(0, 100 - fixedTotal);
295
315
  let flexWeightTotal = 0;
@@ -303,22 +323,22 @@ function buildSetLayoutMap(childIds, sizes, constraints, live) {
303
323
  const out = {};
304
324
  for (let i = 0; i < childIds.length; i++) {
305
325
  const id = childIds[i];
306
- out[id] = isFixedAt(i) ? live[id] : flexibleShare(sizes[i] ?? 0, remaining, flexWeightTotal, flexibleCount);
326
+ out[id] = isFixedAt(i) ? fixedPercentAt(i) : flexibleShare(sizes[i] ?? 0, remaining, flexWeightTotal, flexibleCount);
307
327
  }
308
328
  return out;
309
329
  }
310
330
  /**
311
- * Sum the fixed children's CURRENT live percentages (preserving their px
312
- * lock). Null when a fixed child has no measured live % — the caller cannot
313
- * build a faithful map then.
331
+ * Sum the fixed children's target percentages (via `fixedPercentAt`). Null
332
+ * when any fixed child has no usable percentage — the caller cannot build a
333
+ * faithful map then.
314
334
  */
315
- function sumFixedLivePercent(childIds, isFixedAt, live) {
335
+ function sumFixedPercent(childIds, isFixedAt, fixedPercentAt) {
316
336
  let fixedTotal = 0;
317
337
  for (let i = 0; i < childIds.length; i++) {
318
338
  if (!isFixedAt(i)) continue;
319
- const livePct = live[childIds[i]];
320
- if (typeof livePct !== "number") return null;
321
- fixedTotal += livePct;
339
+ const pct = fixedPercentAt(i);
340
+ if (pct === null) return null;
341
+ fixedTotal += pct;
322
342
  }
323
343
  return fixedTotal;
324
344
  }
@@ -370,6 +390,7 @@ function SplitView(props) {
370
390
  const flexCount = node.children.filter((_, i) => (node.constraints?.[i] ?? null) === null).length;
371
391
  const flexMinSize = String(flexibleFloor(flexCount));
372
392
  const groupRef = useRef(null);
393
+ const containerElRef = useRef(null);
373
394
  const nodeRef = useRef(node);
374
395
  nodeRef.current = node;
375
396
  const items = [];
@@ -418,6 +439,7 @@ function SplitView(props) {
418
439
  ctx.onApplyLayout(cur.id, clampFlexibleWeights(weights, cur.constraints));
419
440
  };
420
441
  const setSplitRef = (el) => {
442
+ containerElRef.current = el;
421
443
  if (el) {
422
444
  el.__deckApplyLayout = (weights) => {
423
445
  const cur = nodeRef.current;
@@ -431,22 +453,61 @@ function SplitView(props) {
431
453
  });
432
454
  }
433
455
  };
434
- const runSync = useCallback(() => {
456
+ const runSync = useCallback((isFreshRemount) => {
435
457
  const api = groupRef.current;
436
- if (!api) return false;
458
+ if (!api) return {
459
+ pushed: false,
460
+ stuck: false
461
+ };
437
462
  const cur = nodeRef.current;
438
463
  const curChildIds = cur.children.map((c) => c.id);
439
464
  const live = api.getLayout();
440
- const targetMap = buildSetLayoutMap(curChildIds, cur.sizes, cur.constraints, live);
441
- if (!targetMap) return false;
442
- if (layoutsEquivalent(live, targetMap, curChildIds)) return false;
465
+ const containerRect = containerElRef.current?.getBoundingClientRect();
466
+ const containerPx = containerRect ? cur.orientation === "row" ? containerRect.width : containerRect.height : 0;
467
+ const measured = containerPx > 0 ? {
468
+ containerPx,
469
+ trustLiveForMinPx: !isFreshRemount
470
+ } : void 0;
471
+ const targetMap = buildSetLayoutMap(curChildIds, cur.sizes, cur.constraints, live, measured);
472
+ if (!targetMap) return {
473
+ pushed: false,
474
+ stuck: false
475
+ };
476
+ if (layoutsEquivalent(live, targetMap, curChildIds)) return {
477
+ pushed: false,
478
+ stuck: true
479
+ };
443
480
  api.setLayout(targetMap);
444
- return true;
481
+ return {
482
+ pushed: true,
483
+ stuck: layoutsEquivalent(api.getLayout(), targetMap, curChildIds)
484
+ };
445
485
  }, []);
446
486
  const sizesKey = node.sizes.join(",");
447
487
  const childCountKey = node.children.length;
488
+ const prevChildCountKeyRef = useRef(null);
489
+ const MAX_FRESH_REMOUNT_SYNC_ATTEMPTS = 8;
448
490
  useEffect(() => {
449
- runSync();
491
+ const isFreshRemount = prevChildCountKeyRef.current !== childCountKey;
492
+ prevChildCountKeyRef.current = childCountKey;
493
+ if (!isFreshRemount) {
494
+ runSync(false);
495
+ return;
496
+ }
497
+ let cancelled = false;
498
+ let rafId = 0;
499
+ let attempts = 0;
500
+ const tick = () => {
501
+ if (cancelled) return;
502
+ attempts += 1;
503
+ const { stuck } = runSync(true);
504
+ if (!stuck && attempts < MAX_FRESH_REMOUNT_SYNC_ATTEMPTS) rafId = requestAnimationFrame(tick);
505
+ };
506
+ rafId = requestAnimationFrame(tick);
507
+ return () => {
508
+ cancelled = true;
509
+ cancelAnimationFrame(rafId);
510
+ };
450
511
  }, [
451
512
  sizesKey,
452
513
  childCountKey,