@cardstack/boxel-cli 0.13.0-unstable.5 → 0.13.0-unstable.6

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.
Files changed (130) hide show
  1. package/bundled-test-harness/assets/{ai-module-creation-test-EsOoJpQ1.js → ai-module-creation-test-C4zhhxaZ.js} +1 -1
  2. package/bundled-test-harness/assets/{aibot-message-uGaCPn5o.js → aibot-message-BShcljQ9.js} +1 -1
  3. package/bundled-test-harness/assets/{app-pIzBYfiZ.js → app-DiEYqT6T.js} +4 -4
  4. package/bundled-test-harness/assets/{binary-upload-dedupe-test-Dnxlbbu6.js → binary-upload-dedupe-test-C0RFmHma.js} +1 -1
  5. package/bundled-test-harness/assets/{can-invoke-test-DJi7jMaU.js → can-invoke-test-CVfE1l1V.js} +1 -1
  6. package/bundled-test-harness/assets/{card-chooser-paging-test-BoUyFMEw.js → card-chooser-paging-test-BN21sDpP.js} +1 -1
  7. package/bundled-test-harness/assets/{card-chooser-test-7ZwhxxkH.js → card-chooser-test-C9jDiIgM.js} +1 -1
  8. package/bundled-test-harness/assets/{card-def-field-def-relationships-test-ByPl5n55.js → card-def-field-def-relationships-test-D_30xak6.js} +1 -1
  9. package/bundled-test-harness/assets/{card-delete-test-Be971b-V.js → card-delete-test-BlsD9Xw4.js} +1 -1
  10. package/bundled-test-harness/assets/{choreo-D1K9RyjM.js → choreo-CZUXQKjf.js} +81 -2
  11. package/bundled-test-harness/assets/choreo-D8RcLuoq.js +2 -0
  12. package/bundled-test-harness/assets/{choreo-feature-reel-test-vzmW-SFd.js → choreo-feature-reel-test-DRGloCAJ.js} +1 -1
  13. package/bundled-test-harness/assets/{choreo-gallery-build-order-test-J6n0_PKm.js → choreo-gallery-build-order-test-BR-r7gaw.js} +1 -1
  14. package/bundled-test-harness/assets/{choreo-gallery-compositor-test-DSi1N9bi.js → choreo-gallery-compositor-test-CWRLfcNT.js} +1 -1
  15. package/bundled-test-harness/assets/{choreo-gallery-crossing-stage-test-BIuLOZH6.js → choreo-gallery-crossing-stage-test-WJ7FmjYi.js} +2 -2
  16. package/bundled-test-harness/assets/{choreo-gallery-demos-alive-test-ClGjpnj6.js → choreo-gallery-demos-alive-test-C-yxdVT_.js} +1 -1
  17. package/bundled-test-harness/assets/{choreo-gallery-drift-test-OjxMXV0Y.js → choreo-gallery-drift-test-HX1mK3od.js} +1 -1
  18. package/bundled-test-harness/assets/{choreo-gallery-escort-test-D6VL4FfT.js → choreo-gallery-escort-test-jZEVk_V4.js} +1 -1
  19. package/bundled-test-harness/assets/{choreo-gallery-films-test-DBcDHGx8.js → choreo-gallery-films-test-DLDQzsxb.js} +1 -1
  20. package/bundled-test-harness/assets/{choreo-gallery-filter-test-BUghtJZe.js → choreo-gallery-filter-test-BJv8KEeP.js} +1 -1
  21. package/bundled-test-harness/assets/{choreo-gallery-fold-test-0D9-_h_I.js → choreo-gallery-fold-test-BssKC8A8.js} +1 -1
  22. package/bundled-test-harness/assets/{choreo-gallery-guides-test-CZnaxUNJ.js → choreo-gallery-guides-test-DTj7yEKp.js} +2 -2
  23. package/bundled-test-harness/assets/{choreo-gallery-hang-test-WFvsJ-MJ.js → choreo-gallery-hang-test-BqJLmeFF.js} +1 -1
  24. package/bundled-test-harness/assets/{choreo-gallery-highlight-test-Dn0hYPLC.js → choreo-gallery-highlight-test-B1Q7-ICm.js} +1 -1
  25. package/bundled-test-harness/assets/{choreo-gallery-inline-edit-test-C5WvS67s.js → choreo-gallery-inline-edit-test-7My9lVam.js} +1 -1
  26. package/bundled-test-harness/assets/{choreo-gallery-interruption-test-CgT9RKTe.js → choreo-gallery-interruption-test-BlIAaGrw.js} +2 -2
  27. package/bundled-test-harness/assets/{choreo-gallery-lone-stack-test-BZgqbTJd.js → choreo-gallery-lone-stack-test-C9NnYIP8.js} +1 -1
  28. package/bundled-test-harness/assets/{choreo-gallery-playhead-transport-test-Dt71t4CU.js → choreo-gallery-playhead-transport-test-CwdiZWeI.js} +1 -1
  29. package/bundled-test-harness/assets/{choreo-gallery-poplayout-subtree-test-B_rDq4em.js → choreo-gallery-poplayout-subtree-test-D55fxwTn.js} +1 -1
  30. package/bundled-test-harness/assets/{choreo-gallery-presence-modes-test-j_cZ9cAM.js → choreo-gallery-presence-modes-test-D8Wxv0lb.js} +1 -1
  31. package/bundled-test-harness/assets/{choreo-gallery-presentation-test-CIFKR65J.js → choreo-gallery-presentation-test-BLoWfp6G.js} +1 -1
  32. package/bundled-test-harness/assets/{choreo-gallery-reduced-motion-test-BvRCiCvB.js → choreo-gallery-reduced-motion-test-D5XxTBDS.js} +2 -2
  33. package/bundled-test-harness/assets/{choreo-gallery-sheet-standalone-test-DgO2MBzU.js → choreo-gallery-sheet-standalone-test-DrHTTvss.js} +1 -1
  34. package/bundled-test-harness/assets/{choreo-gallery-sheet-test-BntUKT_P.js → choreo-gallery-sheet-test-BFVdnM_b.js} +1 -1
  35. package/bundled-test-harness/assets/{choreo-gallery-site-test-CvSE6Jyq.js → choreo-gallery-site-test-BhEBHdvI.js} +2 -2
  36. package/bundled-test-harness/assets/{choreo-gallery-stage-Dj6tFzon.js → choreo-gallery-stage-DcKKsdcu.js} +1 -1
  37. package/bundled-test-harness/assets/{choreo-gallery-stage-keyboard-test-CkDrAPA8.js → choreo-gallery-stage-keyboard-test-Fcc6SC3F.js} +1 -1
  38. package/bundled-test-harness/assets/{choreo-gallery-stages-test-DHUmzoRW.js → choreo-gallery-stages-test-Cb-ADpxK.js} +1 -1
  39. package/bundled-test-harness/assets/{choreo-gallery-subdivision-test-BZ9-cDFh.js → choreo-gallery-subdivision-test-DMJ6C9FW.js} +1 -1
  40. package/bundled-test-harness/assets/{choreo-gallery-table-plan-test-7uP6mZpo.js → choreo-gallery-table-plan-test-CAjSUdLJ.js} +1 -1
  41. package/bundled-test-harness/assets/{choreo-gallery-theater-test-DgEue9FD.js → choreo-gallery-theater-test-B-3IZMj9.js} +2 -2
  42. package/bundled-test-harness/assets/{choreo-gallery-tuning-test-CfPx5iCp.js → choreo-gallery-tuning-test-BVxK5Ry_.js} +1 -1
  43. package/bundled-test-harness/assets/{choreo-gallery-widget-room-test-o9T2EFKq.js → choreo-gallery-widget-room-test-uWBH1EKE.js} +1 -1
  44. package/bundled-test-harness/assets/{choreo-gallery-wires-test-73JLkxn6.js → choreo-gallery-wires-test-Bo4Sd1Vn.js} +1 -1
  45. package/bundled-test-harness/assets/{clip-CsRtHZiN.js → clip-DY6N6K67.js} +1 -1
  46. package/bundled-test-harness/assets/clip-DzXoPdp-.js +2 -0
  47. package/bundled-test-harness/assets/{codeblocks-test-D9pbJMK8.js → codeblocks-test-D6ijVvEL.js} +1 -1
  48. package/bundled-test-harness/assets/{compaction-test-BSqAGv4g.js → compaction-test-CjEsY0uG.js} +1 -1
  49. package/bundled-test-harness/assets/{container-h3Wc26qn.css → container-BYGZImLU.css} +73 -69
  50. package/bundled-test-harness/assets/{container-CEslVSTe.js → container-CBZZ8uFw.js} +73 -32
  51. package/bundled-test-harness/assets/{create-listing-modal-test-DyGlGMWu.js → create-listing-modal-test-De7b4vu3.js} +1 -1
  52. package/bundled-test-harness/assets/{debug-message-test-0hv0RwE_.js → debug-message-test-Ck3BfFv3.js} +1 -1
  53. package/bundled-test-harness/assets/{externals-CDQm-Lm7.js → externals-k-2ZBepK.js} +11 -11
  54. package/bundled-test-harness/assets/{fallback-banner-live-delete-test-DQSiTzvj.js → fallback-banner-live-delete-test-CuFiH1D8.js} +1 -1
  55. package/bundled-test-harness/assets/{fallback-banner-test-CJFKLpOy.js → fallback-banner-test-CqSRN7D8.js} +1 -1
  56. package/bundled-test-harness/assets/{fallback-models-test-CiWfmxaB.js → fallback-models-test-BWekNxC0.js} +1 -1
  57. package/bundled-test-harness/assets/{file-attachment-test-m72-VerW.js → file-attachment-test-LwZf-BSY.js} +1 -1
  58. package/bundled-test-harness/assets/{film-D97U-ydx.js → film-Bf73ypzi.js} +6 -6
  59. package/bundled-test-harness/assets/film-C54lhaiU.js +2 -0
  60. package/bundled-test-harness/assets/{film-D1E-odYG.js → film-DPcMEHYu.js} +6 -6
  61. package/bundled-test-harness/assets/{formatted-aibot-message-test-Bdx_-Ktn.js → formatted-aibot-message-test-BahBj3x8.js} +1 -1
  62. package/bundled-test-harness/assets/{general-test-DQuxDvXK.js → general-test-DKZPBtSb.js} +1 -1
  63. package/bundled-test-harness/assets/{glimmer-motion-and-choreo-in-cards-test-DzPKH76g.js → glimmer-motion-and-choreo-in-cards-test-BkUDjiTs.js} +14 -14
  64. package/bundled-test-harness/assets/{glimmer-motion-and-choreo-test-D9mlcQuN.js → glimmer-motion-and-choreo-test-DsFWCzTs.js} +3 -3
  65. package/bundled-test-harness/assets/{index-query-engine-test-BOvxkx07.js → index-query-engine-test-BETfoYOK.js} +1 -1
  66. package/bundled-test-harness/assets/{instance-filter-matcher-test-COAlkUed.js → instance-filter-matcher-test-BSVCg59b.js} +1 -1
  67. package/bundled-test-harness/assets/{main-DUKPWRts.js → main-bXHwJ0CN.js} +1 -1
  68. package/bundled-test-harness/assets/{markdown-preview-Df5fp1SY.js → markdown-preview-lOh-MyqF.js} +1 -1
  69. package/bundled-test-harness/assets/{markdown-preview-test-DDaPzsVs.js → markdown-preview-test-B8w0fFIy.js} +1 -1
  70. package/bundled-test-harness/assets/{model-cost-tier-test-CZlu1zww.js → model-cost-tier-test-BGg-sJML.js} +1 -1
  71. package/bundled-test-harness/assets/{operation-lowering-test-CKkamOQt.js → operation-lowering-test-omU66bkJ.js} +1 -1
  72. package/bundled-test-harness/assets/{operator-mode-card-chooser-test-CQ1v8med.js → operator-mode-card-chooser-test-DjTQgizt.js} +1 -1
  73. package/bundled-test-harness/assets/{operator-mode-links-test-BMHEauS3.js → operator-mode-links-test-DSEqy5Db.js} +5 -5
  74. package/bundled-test-harness/assets/{operator-mode-test-blL_JVF8.js → operator-mode-test-CB7BNDNE.js} +1 -1
  75. package/bundled-test-harness/assets/{operator-mode-ui-test-BG6I7-Hc.js → operator-mode-ui-test-C1OhF1il.js} +1 -1
  76. package/bundled-test-harness/assets/{overlay-menu-items-test-D-zmRQQ0.js → overlay-menu-items-test-CPN31yBJ.js} +1 -1
  77. package/bundled-test-harness/assets/{overlays-COinwHCP.js → overlays-B-qMEPhY.js} +1 -1
  78. package/bundled-test-harness/assets/{overlays-nTqcu69K.js → overlays-DSf2je0c.js} +1 -1
  79. package/bundled-test-harness/assets/{panel-D9SEMehG.js → panel-DjrC7DxA.js} +4 -4
  80. package/bundled-test-harness/assets/{past-sessions-test-De9N4wVX.js → past-sessions-test-BSIS2Hlq.js} +1 -1
  81. package/bundled-test-harness/assets/{permission-checking-realm-test-OBzvhDB_.js → permission-checking-realm-test-BD4O0nen.js} +1 -1
  82. package/bundled-test-harness/assets/{plate-DAr_pvwC.js → plate-CJq1veVA.js} +1 -1
  83. package/bundled-test-harness/assets/plate-CwAvOUfL.js +2 -0
  84. package/bundled-test-harness/assets/player-Digk7omr.js +2 -0
  85. package/bundled-test-harness/assets/{player-CAVQUyKk.js → player-vsjosoN_.js} +1 -1
  86. package/bundled-test-harness/assets/{quick-look-test-icQwb1NJ.js → quick-look-test-CE0o6lJ4.js} +1 -1
  87. package/bundled-test-harness/assets/{realm-api-keys-test-gtJ8XZCE.js → realm-api-keys-test-BVs7f5Dz.js} +1 -1
  88. package/bundled-test-harness/assets/{realm-config-policy-standing-test-CXJ4ZMde.js → realm-config-policy-standing-test-Ber-IXpc.js} +1 -1
  89. package/bundled-test-harness/assets/{realm-config-routing-rule-edit-test-DoEunShH.js → realm-config-routing-rule-edit-test-3Xqt5VlM.js} +1 -1
  90. package/bundled-test-harness/assets/{realm-config-settings-test-BYi9i4O6.js → realm-config-settings-test-CkalhbDF.js} +1 -1
  91. package/bundled-test-harness/assets/{reasoning-test-WUx-4agu.js → reasoning-test-BV49AYg2.js} +1 -1
  92. package/bundled-test-harness/assets/{rendered-markdown-B_jWmDrK.js → rendered-markdown-DXiDN91F.js} +1 -1
  93. package/bundled-test-harness/assets/{rendered-markdown-test-sdWhU0NA.js → rendered-markdown-test-n3MlXmmu.js} +1 -1
  94. package/bundled-test-harness/assets/{room-message-test-BYNhZ39n.js → room-message-test-BrjJ2wMs.js} +1 -1
  95. package/bundled-test-harness/assets/{run-FzaqmBEl.js → run-P_7TsreC.js} +65 -7
  96. package/bundled-test-harness/assets/{scrolling-test-Dd4gv_fj.js → scrolling-test-B0GNYGYc.js} +2 -2
  97. package/bundled-test-harness/assets/{searchable-option-test-eJaIPH_L.js → searchable-option-test-CcbfMqIL.js} +1 -1
  98. package/bundled-test-harness/assets/{sending-test-Dt9Dh2wW.js → sending-test-H0AaBSgi.js} +1 -1
  99. package/bundled-test-harness/assets/{skills-test-BP8QNesE.js → skills-test-ChmKhADX.js} +1 -1
  100. package/bundled-test-harness/assets/{src-a4d10Hra.js → src-C1hYFgVl.js} +2 -2
  101. package/bundled-test-harness/assets/{src-LqENIikj.js → src-CIlLx7Lu.js} +3 -3
  102. package/bundled-test-harness/assets/{stick-to-bottom-resize-test-BBLiCR3X.js → stick-to-bottom-resize-test-DMHsAG0v.js} +4 -4
  103. package/bundled-test-harness/assets/{store-test-Dd4WQp7L.js → store-test-BiKKDqqW.js} +1 -1
  104. package/bundled-test-harness/assets/{test-support-QTh6wGxu.js → test-support-BGl3rthw.js} +1 -1
  105. package/bundled-test-harness/assets/{test-support-Tr-PYFgc.js → test-support-BcBv__98.js} +1 -1
  106. package/bundled-test-harness/assets/{tests-zLlwPutr.js → tests-CmM9imwB.js} +84 -84
  107. package/bundled-test-harness/assets/{text-input-validator-test-C1uDVZU7.js → text-input-validator-test-DBj7TaM_.js} +1 -1
  108. package/bundled-test-harness/assets/{titles-0a1luoTq.js → titles-DCvAhECF.js} +1 -1
  109. package/bundled-test-harness/assets/titles-ZPeArtS1.js +2 -0
  110. package/bundled-test-harness/assets/{to-device-streaming-test-3A6jI0_r.js → to-device-streaming-test-BJ4J1cXe.js} +1 -1
  111. package/bundled-test-harness/assets/{token-usage-test-33ovcqPk.js → token-usage-test-WUf4r4fQ.js} +1 -1
  112. package/bundled-test-harness/assets/{tools-test-DtUQp5ez.js → tools-test-Cvau5219.js} +1 -1
  113. package/bundled-test-harness/assets/{turn-settling-test-DqYk312r.js → turn-settling-test-DZoL2mRT.js} +1 -1
  114. package/bundled-test-harness/index.html +13 -13
  115. package/bundled-test-harness/tests/index.html +13 -13
  116. package/bundled-types/choreo/choreo.gts +45 -1
  117. package/bundled-types/choreo/compile.ts +5 -0
  118. package/bundled-types/choreo/layout-probe.ts +100 -0
  119. package/bundled-types/choreo/run.ts +80 -4
  120. package/bundled-types/host-app/components/operator-mode/motion/stack-motion.gts +92 -33
  121. package/bundled-types/host-app/components/operator-mode/stack-item.gts +2 -2
  122. package/bundled-types/host-app/components/operator-mode/stack.gts +36 -25
  123. package/bundled-types/host-app/components/operator-mode/submode-layout.gts +2 -2
  124. package/package.json +1 -1
  125. package/bundled-test-harness/assets/choreo-Ci5dG5fv.js +0 -2
  126. package/bundled-test-harness/assets/clip-CycGr0Lf.js +0 -2
  127. package/bundled-test-harness/assets/film-D25Y0ED6.js +0 -2
  128. package/bundled-test-harness/assets/plate-DeoQd99m.js +0 -2
  129. package/bundled-test-harness/assets/player-BvXGBItd.js +0 -2
  130. package/bundled-test-harness/assets/titles-DKHNLVwC.js +0 -2
@@ -78,6 +78,15 @@ const loopKey = (cue: Cue) =>
78
78
  .sort()
79
79
  .join(',');
80
80
 
81
+ /** where releaseForMeasure stands a transform a tween is carrying */
82
+ const TRANSFORM_REST: Record<string, PropValue> = {
83
+ scale: 1,
84
+ scaleX: 1,
85
+ scaleY: 1,
86
+ x: 0,
87
+ y: 0,
88
+ };
89
+
81
90
  /** a tween a replacement pass carries on: see Run.midfade */
82
91
  export interface Fade {
83
92
  /** score ms until it starts (0 when it is already running) */
@@ -147,6 +156,8 @@ export interface Run {
147
156
  reassert(): void;
148
157
  /** put every element this run has touched back to its resting layout NOW */
149
158
  releaseForMeasure(): void;
159
+ /** stand back up rested tween values the replacement drives itself (see ChoreoRun.restoreRested) */
160
+ restoreRested(drives: (ve: VisualElement, key: string) => boolean): void;
150
161
  /**
151
162
  * One build backwards — Keynote's rule. Land PARKED at the previous
152
163
  * gate with everything ahead re-closed, and HOLD: a retreat never
@@ -495,7 +506,9 @@ export class ChoreoRun implements Run {
495
506
  value: unknown;
496
507
  ve: VisualElement;
497
508
  }[];
498
- } = { borrowed: [], moved: [] };
509
+ /** transforms in-flight tweens carry, rested for the measure */
510
+ tweened: { key: string; value: unknown; ve: VisualElement }[];
511
+ } = { borrowed: [], moved: [], tweened: [] };
499
512
  private owned: { el: HTMLElement; key: string; ve: VisualElement }[] = [];
500
513
  private cancelled = false;
501
514
  private ended = false;
@@ -2965,7 +2978,7 @@ export class ChoreoRun implements Run {
2965
2978
  this.dropCameraAnimation(t);
2966
2979
  }
2967
2980
  const touched = new Set<VisualElement>();
2968
- this.retired = { borrowed: [], moved: [] };
2981
+ this.retired = { borrowed: [], moved: [], tweened: [] };
2969
2982
  for (const { ve, el, key, prior } of this.borrowedValues.splice(0)) {
2970
2983
  this.retired.borrowed.push({
2971
2984
  el,
@@ -2992,6 +3005,63 @@ export class ChoreoRun implements Run {
2992
3005
  ve.getValue(key)?.jump(rest ?? 0);
2993
3006
  touched.add(ve);
2994
3007
  }
3008
+ // A transform a tween or spring is still carrying (an entrance's scale,
3009
+ // a slide's y) is part of the picture, not of the layout: measured with
3010
+ // it on, the final box would be the one it paints mid-way, and a move
3011
+ // the next score plays from it would land short and snap. So it rests
3012
+ // for the measurement too. A kept run stands it back up (reassert); a
3013
+ // replacement either moves the sprite from its painted box, carries it
3014
+ // on to rest (the region's continuity cues), or, when a new tween drives
3015
+ // the same value, stands it back up for that tween to carry on from
3016
+ // (restoreRested). A cancelled run is past measuring: its values stay
3017
+ // where they are painted for whatever takes them over.
3018
+ const now = this.master;
3019
+ const rested = new Set<string>();
3020
+ for (const t of this.cancelled ? [] : this.tracks) {
3021
+ const { cue } = t;
3022
+ const ve = cue.sprite.node.visualElement;
3023
+ if (
3024
+ !ve ||
3025
+ t.passed ||
3026
+ t.end <= now ||
3027
+ cue.loop ||
3028
+ (cue.kind !== 'tween' && cue.kind !== 'spring') ||
3029
+ !cue.target
3030
+ ) {
3031
+ continue;
3032
+ }
3033
+ for (const key of Object.keys(cue.target)) {
3034
+ const rest = TRANSFORM_REST[key];
3035
+ const seen = `${cue.sprite.node.layoutKey}:${key}`;
3036
+ if (rest === undefined || rested.has(seen) || !ve.hasValue(key)) {
3037
+ continue;
3038
+ }
3039
+ rested.add(seen);
3040
+ const value = ve.getValue(key)!;
3041
+ this.retired.tweened.push({ key, value: value.get(), ve });
3042
+ value.jump(rest);
3043
+ touched.add(ve);
3044
+ }
3045
+ }
3046
+ touched.forEach((ve) => ve.render());
3047
+ }
3048
+
3049
+ /**
3050
+ * Stand back up the in-flight tween values the last measure rested, where
3051
+ * the replacement score drives the same value with a tween or spring of
3052
+ * its own: that tween starts from the value on the element, which has to
3053
+ * be the one painted, not its rest. Values nothing new drives stay
3054
+ * rested, for a continuation to carry on (restoring those too would
3055
+ * compound a continuation's scaleX/scaleY with a restored scale).
3056
+ */
3057
+ restoreRested(drives: (ve: VisualElement, key: string) => boolean) {
3058
+ const touched = new Set<VisualElement>();
3059
+ for (const { key, value, ve } of this.retired.tweened) {
3060
+ if (value !== undefined && drives(ve, key)) {
3061
+ ve.getValue(key)?.jump(value as PropValue);
3062
+ touched.add(ve);
3063
+ }
3064
+ }
2995
3065
  touched.forEach((ve) => ve.render());
2996
3066
  }
2997
3067
 
@@ -3011,8 +3081,14 @@ export class ChoreoRun implements Run {
3011
3081
  * run's own clock, so a keep is invisible rather than a restart.
3012
3082
  */
3013
3083
  reassert() {
3014
- const { borrowed, moved } = this.retired;
3015
- this.retired = { borrowed: [], moved: [] };
3084
+ const { borrowed, moved, tweened } = this.retired;
3085
+ this.retired = { borrowed: [], moved: [], tweened: [] };
3086
+ // first, so a value a move also writes ends on the move's own
3087
+ for (const { key, value, ve } of tweened) {
3088
+ if (value !== undefined) {
3089
+ ve.getValue(key)?.jump(value as PropValue);
3090
+ }
3091
+ }
3016
3092
  for (const { el, key, prior, value, ve } of borrowed) {
3017
3093
  if (value !== undefined) {
3018
3094
  ve.getValue(key, value as PropValue)!.jump(value as PropValue);
@@ -1,4 +1,3 @@
1
- import { array } from '@ember/helper';
2
1
  import { htmlSafe } from '@ember/template';
3
2
  import Component from '@glimmer/component';
4
3
 
@@ -72,28 +71,85 @@ const reducesMotion = () =>
72
71
  typeof matchMedia === 'function' &&
73
72
  matchMedia('(prefers-reduced-motion: reduce)').matches;
74
73
 
75
- // How small a card starts as it scales in. Text and card content should not
76
- // scale in motion (§1.5), so it starts near its full size and the fade
77
- // carries the entrance. The slight growth is what says the card arrives on
78
- // top of the stack, over the cards under it, where a rise would read as
79
- // arriving from below.
80
- const SCALE_FROM = 0.96;
74
+ // Every positional value here is ONE transform string, never separate x, y
75
+ // or scale values and never a size: a whole transform keyframe pair rides
76
+ // the platform's own animation (WAAPI, off the main thread), where separate
77
+ // values and sizes are written by script every frame and a size re-lays out
78
+ // the card's content each time. That is what keeps the stack smooth while
79
+ // the card under the motion is still rendering.
81
80
 
82
- const scaleInOnTop: PropSource = (sprite, changeset) =>
83
- isTopCard(sprite, changeset) ? [SCALE_FROM, 1] : [1, 1];
81
+ /** the translation a card is painted with right now, mid-flight or at rest */
82
+ const paintedTranslation = (sprite: Sprite) => {
83
+ const matrix = new DOMMatrixReadOnly(
84
+ getComputedStyle(sprite.element).transform,
85
+ );
86
+ return { x: matrix.m41, y: matrix.m42 };
87
+ };
88
+
89
+ const STILL = ['none', 'none'];
90
+
91
+ // An opened card lifts into place from just below it as it fades in. Text
92
+ // and card content should not scale in motion (§1.5), so the entrance moves
93
+ // it, a short distance, and the fade carries the arrival.
94
+ const LIFT = 24;
95
+ const liftInOnTop: PropSource = (sprite, changeset) =>
96
+ isTopCard(sprite, changeset) && !reducesMotion()
97
+ ? [`translate(0px, ${LIFT}px)`, 'none']
98
+ : STILL;
84
99
  const fadeInOnTop: PropSource = (sprite, changeset) =>
85
100
  isTopCard(sprite, changeset) ? [0, 1] : [1, 1];
86
- // A phone card slides up from below the viewport: its own height.
101
+ // A phone card slides up from below the viewport: its own height. Under
102
+ // reduced motion it fades in where it stands instead.
87
103
  const slideUpOnTop: PropSource = (sprite, changeset) =>
88
- isTopCard(sprite, changeset) ? [sprite.final?.page.height ?? 0, 0] : [0, 0];
89
- // Under reduced motion the slide lands at once, so a phone card fades in
90
- // where it stands instead.
104
+ isTopCard(sprite, changeset) && !reducesMotion()
105
+ ? [`translate(0px, ${sprite.final?.page.height ?? 0}px)`, 'none']
106
+ : STILL;
91
107
  const fadeInOnTopWhenReduced: PropSource = (sprite, changeset) =>
92
108
  reducesMotion() ? fadeInOnTop(sprite, changeset) : [1, 1];
93
- // A leaving card sinks by its own height as it fades; under reduced motion
94
- // it fades where it stood.
95
- const sinkOut: PropSource = (sprite) =>
96
- reducesMotion() ? [0, 0] : [0, sprite.initial?.page.height ?? 0];
109
+
110
+ // A leaving card drops a little below where it is painted as it fades, so
111
+ // a card closed mid-glide drops from where it is, not to a fixed place. It
112
+ // eases in, as a leaving element should (ADR 007 §1.5). Under reduced
113
+ // motion it fades where it stood.
114
+ const DROP = 18;
115
+ const dropOut: PropSource = (sprite) => {
116
+ const { x, y } = paintedTranslation(sprite);
117
+ const from = `translate(${x}px, ${y}px)`;
118
+ return reducesMotion()
119
+ ? [from, from]
120
+ : [from, `translate(${x}px, ${y + DROP}px)`];
121
+ };
122
+
123
+ /**
124
+ * A card whose place changed (covered, uncovered, expanded) glides from the
125
+ * center it was painted at to its new one, by transform alone. Its size
126
+ * lands at once: the card is laid out once at its new width, and only
127
+ * travels, so its content never re-lays out frame by frame. Center to
128
+ * center, because the stack centers its cards: a card that narrows stays
129
+ * under its own middle. Measured against the painted translation, so a
130
+ * card caught mid-flight (or measured with its transform still on) carries
131
+ * on from where it is seen.
132
+ *
133
+ * Under reduced motion it lands at once. The region's reduced motion lands
134
+ * only the separate positional values (x, y, scale, width, height); a whole
135
+ * transform string is not one of them, so this value has to stand still
136
+ * itself, as the entrance and the exit do.
137
+ */
138
+ const reflow: PropSource = (sprite, changeset) => {
139
+ const from = sprite.initial?.page;
140
+ const to = sprite.final?.page;
141
+ if (!from || !to || reducesMotion()) {
142
+ return STILL;
143
+ }
144
+ const painted = paintedTranslation(sprite);
145
+ const zoom = changeset.measureZoom || 1;
146
+ const x =
147
+ painted.x + (from.x + from.width / 2 - (to.x + to.width / 2)) / zoom;
148
+ const y = painted.y + (from.y - to.y) / zoom;
149
+ return Math.abs(x) < 0.5 && Math.abs(y) < 0.5
150
+ ? STILL
151
+ : [`translate(${x}px, ${y}px)`, 'none'];
152
+ };
97
153
 
98
154
  // How long a phone card stays in view under a card opening over it, read by
99
155
  // the stylesheet as a transition delay (see stack-item.gts). The slide runs
@@ -120,17 +176,19 @@ interface Signature {
120
176
  * 300–500 ms), and the cards under it move on the same clock, so the stack
121
177
  * reads as one change.
122
178
  *
123
- * - the inserted top card scales in and fades in, easing out; on a phone it
124
- * slides up from below the viewport instead;
125
- * - a departing card fades and sinks, easing in over the exit duration;
126
- * - a card whose box changed (uncovered, covered, or expanded) moves by
127
- * transform to its new place. Its width and height animate as layout, not
128
- * by scale, so its text is never scaled (§1.5); the frame reflows under
129
- * the card's content, the way a width transition does.
179
+ * - the inserted top card lifts into place and fades in, easing out; on a
180
+ * phone it slides up from below the viewport instead;
181
+ * - a departing card fades and drops a little, over the exit duration;
182
+ * - a card whose box changed (uncovered, covered, or expanded) takes its
183
+ * new size at once and glides by transform from the center it was painted
184
+ * at to its new one, so its content is laid out once and only travels.
185
+ * A fade the card was still playing (an entrance cut short) is carried on
186
+ * by the region itself, so this step names only the transform.
130
187
  *
131
- * Under reduced motion the region's MotionConfig ('user') lands every move
132
- * at once, and a card fades in or out where it stands, on a phone as on a
133
- * wide screen (§1.5).
188
+ * Under reduced motion every move lands at once, and a card fades in or out
189
+ * where it stands, on a phone as on a wide screen (§1.5). Each transform
190
+ * value here checks for it itself: the region's MotionConfig ('user') lands
191
+ * separate positional values, never a whole transform string.
134
192
  */
135
193
  export default class StackMotion extends Component<Signature> {
136
194
  private enabled = motionEnabled('boxel-stack-motion');
@@ -148,7 +206,7 @@ export default class StackMotion extends Component<Signature> {
148
206
  {{#if @phone}}
149
207
  <c.Tween
150
208
  @of={{c.inserted STACK_ITEM_ROLE}}
151
- @y={{slideUpOnTop}}
209
+ @transform={{slideUpOnTop}}
152
210
  @opacity={{fadeInOnTopWhenReduced}}
153
211
  @duration={{DURATION.space}}
154
212
  @ease={{EASE}}
@@ -156,7 +214,7 @@ export default class StackMotion extends Component<Signature> {
156
214
  {{else}}
157
215
  <c.Tween
158
216
  @of={{c.inserted STACK_ITEM_ROLE}}
159
- @scale={{scaleInOnTop}}
217
+ @transform={{liftInOnTop}}
160
218
  @opacity={{fadeInOnTop}}
161
219
  @duration={{DURATION.space}}
162
220
  @ease={{EASE}}
@@ -164,13 +222,14 @@ export default class StackMotion extends Component<Signature> {
164
222
  {{/if}}
165
223
  <c.Tween
166
224
  @of={{DEPARTED}}
167
- @opacity={{array 1 0}}
168
- @y={{sinkOut}}
225
+ @opacity={{0}}
226
+ @transform={{dropOut}}
169
227
  @duration={{DURATION.exit}}
170
228
  @ease={{EASE_IN}}
171
229
  />
172
- <c.Move
173
- @of={{c.moved STACK_ITEM_ROLE}}
230
+ <c.Tween
231
+ @of={{c.kept STACK_ITEM_ROLE}}
232
+ @transform={{reflow}}
174
233
  @duration={{DURATION.space}}
175
234
  @ease={{EASE}}
176
235
  />
@@ -1033,7 +1033,7 @@ export default class OperatorModeStackItem extends Component<Signature> {
1033
1033
  justify-self: center;
1034
1034
  position: absolute;
1035
1035
  width: 89%;
1036
- height: inherit;
1036
+ height: 100%;
1037
1037
  z-index: 0;
1038
1038
  pointer-events: none;
1039
1039
  }
@@ -1114,7 +1114,7 @@ export default class OperatorModeStackItem extends Component<Signature> {
1114
1114
  /* When the top card is expanded, fade out the underlying
1115
1115
  buried cards so the user isn't visually distracted by the
1116
1116
  stack history behind the expanded surface. Sibling selector
1117
- within .operator-mode-stack > .inner; uses :has() to match
1117
+ within the stack's cards wrapper (.stack-cards); uses :has() to match
1118
1118
  buried items that have an expanded sibling AFTER them in the
1119
1119
  DOM (top card = last in DOM order). */
1120
1120
  .item:not(.expanded, .phone-layout):has(~ .item.expanded) {
@@ -77,31 +77,38 @@ export default class OperatorModeStack extends Component<Signature> {
77
77
  };
78
78
 
79
79
  <template>
80
- <div
81
- class={{cn
82
- 'operator-mode-stack'
83
- stack-medium-padding-top=(eq @stackItems.length 2)
84
- stack-small-padding-top=(gte @stackItems.length 3)
85
- }}
86
- ...attributes
87
- >
80
+ <div class='operator-mode-stack' ...attributes>
88
81
  {{! the stack's motion region: its cards are the participants (motion/stack-motion.gts) }}
89
82
  <StackMotion
90
83
  class='inner'
91
84
  @phone={{this.operatorModeStateService.isMobileViewport}}
92
85
  >
93
- {{#each @stackItems as |item i|}}
94
- <OperatorModeStackItem
95
- @item={{item}}
96
- @index={{i}}
97
- @stackItems={{@stackItems}}
98
- @toolContext={{@toolContext}}
99
- @dismissStackedCardsAbove={{this.dismissStackedCardsAbove}}
100
- @requestDeleteCard={{@deleteCard}}
101
- @close={{@close}}
102
- @setupStackItem={{@setupStackItem}}
103
- />
104
- {{/each}}
86
+ {{! The cards' top padding follows how many cards stand in the stack,
87
+ so it is set here, inside the region: the region measures the cards
88
+ before a render patches anything inside it, and a padding set
89
+ outside it would already have changed by then, so a card would jump
90
+ by the difference instead of moving. }}
91
+ <div
92
+ class={{cn
93
+ 'stack-cards'
94
+ stack-medium-padding-top=(eq @stackItems.length 2)
95
+ stack-small-padding-top=(gte @stackItems.length 3)
96
+ }}
97
+ data-test-stack-cards
98
+ >
99
+ {{#each @stackItems as |item i|}}
100
+ <OperatorModeStackItem
101
+ @item={{item}}
102
+ @index={{i}}
103
+ @stackItems={{@stackItems}}
104
+ @toolContext={{@toolContext}}
105
+ @dismissStackedCardsAbove={{this.dismissStackedCardsAbove}}
106
+ @requestDeleteCard={{@deleteCard}}
107
+ @close={{@close}}
108
+ @setupStackItem={{@setupStackItem}}
109
+ />
110
+ {{/each}}
111
+ </div>
105
112
  </StackMotion>
106
113
  </div>
107
114
 
@@ -124,7 +131,6 @@ export default class OperatorModeStack extends Component<Signature> {
124
131
  width: 100%;
125
132
  background-position: center;
126
133
  background-size: cover;
127
- padding-top: var(--stack-padding-top);
128
134
  padding-inline: var(--stack-padding-inline);
129
135
  padding-bottom: var(--stack-padding-bottom);
130
136
  z-index: 0;
@@ -132,13 +138,13 @@ export default class OperatorModeStack extends Component<Signature> {
132
138
  /* The padding follows the cards standing in the stack: a leaving card
133
139
  is kept in the region's orphan layer while it fades, and must not
134
140
  hold the padding it had */
135
- .stack-medium-padding-top:not(:has(> .inner > .item.expanded)) {
141
+ .stack-medium-padding-top:not(:has(> .item.expanded)) {
136
142
  --stack-padding-top: var(--stack-md-padding-top);
137
143
  }
138
- .stack-small-padding-top:not(:has(> .inner > .item.expanded)) {
144
+ .stack-small-padding-top:not(:has(> .item.expanded)) {
139
145
  --stack-padding-top: var(--stack-sm-padding-top);
140
146
  }
141
- .operator-mode-stack:has(> .inner > .item.expanded) {
147
+ .operator-mode-stack:has(> .inner > .stack-cards > .item.expanded) {
142
148
  --stack-padding-inline: 0;
143
149
  --stack-padding-bottom: 0;
144
150
  }
@@ -153,9 +159,14 @@ export default class OperatorModeStack extends Component<Signature> {
153
159
  width: 100%;
154
160
  height: 100%;
155
161
  position: relative;
162
+ margin: 0 auto;
163
+ }
164
+ .stack-cards {
165
+ position: relative;
166
+ height: calc(100% - var(--stack-padding-top));
167
+ margin-top: var(--stack-padding-top);
156
168
  display: flex;
157
169
  justify-content: center;
158
- margin: 0 auto;
159
170
  border-bottom-left-radius: var(--boxel-border-radius);
160
171
  border-bottom-right-radius: var(--boxel-border-radius);
161
172
  }
@@ -710,8 +710,8 @@ export default class SubmodeLayout extends Component<Signature> {
710
710
  max-width: 100%;
711
711
  container-type: inline-size;
712
712
  /* Lock outer box to exactly var(--stack-padding-top) — the
713
- same value .operator-mode-stack uses for its padding-top
714
- offset. Any content the bar contains (workspace button,
713
+ same value the stack's cards wrapper (.stack-cards in
714
+ stack.gts) uses for its top margin. Any content the bar contains (workspace button,
715
715
  submode switcher, portaled expanded-card-header pill,
716
716
  etc.) renders WITHIN this fixed height; nothing the slot
717
717
  contents do can push the bar taller. This is what lets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardstack/boxel-cli",
3
- "version": "0.13.0-unstable.5",
3
+ "version": "0.13.0-unstable.6",
4
4
  "license": "MIT",
5
5
  "description": "CLI tools for Boxel workspace management",
6
6
  "main": "./dist/index.js",
@@ -1,2 +0,0 @@
1
- import { t as Choreo } from "./choreo-D1K9RyjM.js";
2
- export { Choreo, Choreo as default };
@@ -1,2 +0,0 @@
1
- import { t as Clip } from "./clip-CsRtHZiN.js";
2
- export { Clip };
@@ -1,2 +0,0 @@
1
- import { r as TICK, t as Film } from "./film-D97U-ydx.js";
2
- export { Film, TICK };
@@ -1,2 +0,0 @@
1
- import { t as Plate } from "./plate-DAr_pvwC.js";
2
- export { Plate };
@@ -1,2 +0,0 @@
1
- import { n as Menu, r as Player, t as Burst } from "./player-CAVQUyKk.js";
2
- export { Burst, Menu, Player };
@@ -1,2 +0,0 @@
1
- import { n as Gate, t as EndCard } from "./titles-0a1luoTq.js";
2
- export { EndCard, Gate };