@cardstack/boxel-cli 0.12.0 → 0.13.0-unstable.1
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/bundled-realms/base/command.gts +4 -4
- package/bundled-realms/base/realm-api-keys.gts +35 -3
- package/bundled-realms/pretui/components/chip.gts +47 -2
- package/bundled-realms/pretui/components/chip.md +5 -2
- package/bundled-realms/pretui/components/chip.usage.gts +37 -2
- package/bundled-realms/pretui/components/status-chip.gts +12 -2
- package/bundled-realms/pretui/components/status-chip.md +2 -1
- package/bundled-realms/pretui/components/status-chip.usage.gts +2 -0
- package/bundled-test-harness/assets/{ai-module-creation-test-B9lSKNq1.js → ai-module-creation-test-DxxsXNXe.js} +1 -1
- package/bundled-test-harness/assets/{aibot-message-LXV0UlzI.js → aibot-message-Dg5nzE1b.js} +1 -1
- package/bundled-test-harness/assets/{app-BIQcKrVT.js → app-BEvPLshl.js} +8 -8
- package/bundled-test-harness/assets/{assistant-transcript-test-CG_2-bog.js → assistant-transcript-test-kILRv_pf.js} +1 -1
- package/bundled-test-harness/assets/{binary-upload-dedupe-test-Q7e6bzl1.js → binary-upload-dedupe-test-B_gcWqxF.js} +1 -1
- package/bundled-test-harness/assets/{bundled-base-DldokUSJ.js → bundled-base-CYD5iFX3.js} +3 -3
- package/bundled-test-harness/assets/{bundled-base-modules-test-VAlRx4t3.js → bundled-base-modules-test-bO7yZysu.js} +1 -1
- package/bundled-test-harness/assets/{can-invoke-test-BGmUJHxo.js → can-invoke-test-BBTE4_cE.js} +1 -1
- package/bundled-test-harness/assets/{card-chooser-paging-test-CzooKL4I.js → card-chooser-paging-test-14IqQei3.js} +1 -1
- package/bundled-test-harness/assets/{card-chooser-test-C3fjaDYi.js → card-chooser-test-CRg2JrXQ.js} +1 -1
- package/bundled-test-harness/assets/{card-def-field-def-relationships-test-yqv8ir8Y.js → card-def-field-def-relationships-test-BapYGyoB.js} +1 -1
- package/bundled-test-harness/assets/{card-delete-test-BtjqrqKO.js → card-delete-test-CvbDQUUE.js} +1 -1
- package/bundled-test-harness/assets/{chip-D6Xffhjx.css → chip-B2Ns53A6.css} +35 -9
- package/bundled-test-harness/assets/{chip-BydgljDL.js → chip-DVJE_YDh.js} +11 -3
- package/bundled-test-harness/assets/{choreo-D7dLCdGB.js → choreo-CIrop3HM.js} +16 -35
- package/bundled-test-harness/assets/choreo-Ca2Y2sF3.js +2 -0
- package/bundled-test-harness/assets/{choreo-feature-reel-test--x-L3ZKc.js → choreo-feature-reel-test-CJHms7Sf.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-ZO9vXOxd.js → choreo-gallery-DZm1KZ20.js} +3 -3
- package/bundled-test-harness/assets/{choreo-gallery-build-order-test-DG5UTyc9.js → choreo-gallery-build-order-test-QKyMWELN.js} +4 -4
- package/bundled-test-harness/assets/{choreo-gallery-compositor-test-DJP80SNj.js → choreo-gallery-compositor-test-BQDptxfE.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-cover-test-CHYz7sM4.js → choreo-gallery-cover-test-Do-HYF96.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-crossing-stage-test-BuinlINr.js → choreo-gallery-crossing-stage-test-yJVXghRc.js} +10 -10
- package/bundled-test-harness/assets/{choreo-gallery-demos-alive-test-BjNCGfH9.js → choreo-gallery-demos-alive-test-BCeaaZye.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-drift-test-BJ17f3yv.js → choreo-gallery-drift-test-CJ1yq6R0.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-escort-test-Dadih8-S.js → choreo-gallery-escort-test-DypqSgvs.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-films-test-CCXty_WI.js → choreo-gallery-films-test-ClMy5M_p.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-filter-test-BozCoT_M.js → choreo-gallery-filter-test-CzOmgqgl.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-fold-test-Kez2P3_Y.js → choreo-gallery-fold-test-qNnH6wtC.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-guide-pages-test-BV5oxrYA.js → choreo-gallery-guide-pages-test-BtKWMkTv.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-guides-test-DwYBDgKi.js → choreo-gallery-guides-test-CXMsuDYi.js} +4 -3
- package/bundled-test-harness/assets/{choreo-gallery-hang-test-B-5u7cGJ.js → choreo-gallery-hang-test-BD4JQ04e.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-highlight-test-BZ5Lg8V6.js → choreo-gallery-highlight-test-BK280K2W.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-inline-edit-test-wSlSI-VM.js → choreo-gallery-inline-edit-test-DAgUcaaw.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-interruption-test-DBpCEiro.js → choreo-gallery-interruption-test-DY5KUilC.js} +4 -4
- package/bundled-test-harness/assets/{choreo-gallery-lone-stack-test-aMLrHsns.js → choreo-gallery-lone-stack-test-Dxd_Nb0-.js} +4 -4
- package/bundled-test-harness/assets/{choreo-gallery-navigation-test-B8b-2ZTz.js → choreo-gallery-navigation-test-DWJq12PN.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-playhead-transport-test-B95CYBh7.js → choreo-gallery-playhead-transport-test-DlQ6yTKW.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-poplayout-subtree-test-jZAAICV6.js → choreo-gallery-poplayout-subtree-test-D59W8gYt.js} +5 -5
- package/bundled-test-harness/assets/{choreo-gallery-presence-modes-test-BawIKB-a.js → choreo-gallery-presence-modes-test-BAU-dggP.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-presentation-test-CYIb-8ML.js → choreo-gallery-presentation-test-BqGsinE0.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-reduced-motion-test-C6Vi1WYr.js → choreo-gallery-reduced-motion-test-By0rb2Ai.js} +4 -3
- package/bundled-test-harness/assets/{choreo-gallery-sheet-standalone-test-_e4T5KDI.js → choreo-gallery-sheet-standalone-test-Cfw-4ULF.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-sheet-test-Cv51Te3G.js → choreo-gallery-sheet-test-dm53Pdzb.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-site-test-Cm35kHu_.js → choreo-gallery-site-test-SAlqtZgL.js} +3 -2
- package/bundled-test-harness/assets/{choreo-gallery-stage-Bp6QVqlI.js → choreo-gallery-stage-CU8LvQqX.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-stage-keyboard-test-DDX2cuMa.js → choreo-gallery-stage-keyboard-test-DQ4v5qA4.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-stages-test-Dfa_33_4.js → choreo-gallery-stages-test-CRvaS6w6.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-subdivision-test-CgR3WdcT.js → choreo-gallery-subdivision-test-CtOm-U35.js} +3 -3
- package/bundled-test-harness/assets/{choreo-gallery-table-plan-test-DF-cE3J9.js → choreo-gallery-table-plan-test-_0koIm2u.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-theater-test-BuIyb2ke.js → choreo-gallery-theater-test-yedwQVyP.js} +4 -4
- package/bundled-test-harness/assets/{choreo-gallery-tuning-test-BdZ45YBK.js → choreo-gallery-tuning-test-CmMtfJ5a.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-widget-room-test-HcPX75Dg.js → choreo-gallery-widget-room-test-jnYOas5x.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-wires-test-BhFCIjB1.js → choreo-gallery-wires-test-CSyqjVnm.js} +2 -2
- package/bundled-test-harness/assets/clip--wioup_3.js +2 -0
- package/bundled-test-harness/assets/{clip-BmBVqUyU.js → clip-Cm32sLyn.js} +3 -3
- package/bundled-test-harness/assets/{codeblocks-test-DbZYlmG_.js → codeblocks-test-j4ES37-L.js} +1 -1
- package/bundled-test-harness/assets/{compaction-test-D1G3KCSe.js → compaction-test-CyhMODxy.js} +1 -1
- package/bundled-test-harness/assets/{container-BCOmNUou.css → container-CAEYXGQg.css} +179 -155
- package/bundled-test-harness/assets/{container-XSX5809v.js → container-CATqNgYF.js} +20 -19
- package/bundled-test-harness/assets/{create-listing-modal-test--oGT9P_F.js → create-listing-modal-test-QmWcm9hl.js} +1 -1
- package/bundled-test-harness/assets/{debug-message-test-C8UZUtCt.js → debug-message-test-D-4Dcu8y.js} +1 -1
- package/bundled-test-harness/assets/{externals--YMzwHwd.js → externals-Cw0MmkHM.js} +38 -38
- package/bundled-test-harness/assets/{fallback-banner-live-delete-test-DPK_coJY.js → fallback-banner-live-delete-test-Bzqw4uMs.js} +1 -1
- package/bundled-test-harness/assets/{fallback-banner-test-C2wi6Lu6.js → fallback-banner-test-BMQVK4gy.js} +1 -1
- package/bundled-test-harness/assets/{fallback-models-test-SqdXWB1Z.js → fallback-models-test-DkdjPwvA.js} +1 -1
- package/bundled-test-harness/assets/{file-attachment-test-DT0HK6yS.js → file-attachment-test-yvR5gw9_.js} +1 -1
- package/bundled-test-harness/assets/{film-Zt2YoVoG.js → film-9V69qBWw.js} +6 -6
- package/bundled-test-harness/assets/{film-77OfTGL7.js → film-Z87hSVo_.js} +8 -8
- package/bundled-test-harness/assets/film-h1N8vUjG.js +2 -0
- package/bundled-test-harness/assets/{formatted-aibot-message-test-B6i0k9wW.js → formatted-aibot-message-test-DH3WPvU1.js} +1 -1
- package/bundled-test-harness/assets/{general-test-C0Xb2Idr.js → general-test-D2j4JhrD.js} +1 -1
- package/bundled-test-harness/assets/{glimmer-motion-and-choreo-in-cards-test-DFlkZtek.js → glimmer-motion-and-choreo-in-cards-test-Byg3fSOY.js} +32 -32
- package/bundled-test-harness/assets/{glimmer-motion-and-choreo-test-B2ustNm9.js → glimmer-motion-and-choreo-test-BM7uX8k4.js} +10 -10
- package/bundled-test-harness/assets/group-Cgg5RLul.js +2 -0
- package/bundled-test-harness/assets/{group-BNxp6qON.js → group-DL-b_Hzj.js} +2 -2
- package/bundled-test-harness/assets/{index-query-engine-test-BKOqgBMv.js → index-query-engine-test-DFkm037X.js} +1 -1
- package/bundled-test-harness/assets/{instance-filter-matcher-test-CNIzKZvP.js → instance-filter-matcher-test-UqB2Rto8.js} +1 -1
- package/bundled-test-harness/assets/item-Bu9hhPf2.js +2 -0
- package/bundled-test-harness/assets/{item-BDyVqxdA.js → item-CBEoTOyj.js} +2 -2
- package/bundled-test-harness/assets/{main-BiI-FhMc.js → main-CM62YtMN.js} +1 -1
- package/bundled-test-harness/assets/{markdown-preview-BItI_AEl.js → markdown-preview-D_QssEOi.js} +1 -1
- package/bundled-test-harness/assets/{markdown-preview-test-Bntyikh1.js → markdown-preview-test-nljzB8o_.js} +1 -1
- package/bundled-test-harness/assets/memory-B73-H_Rp.js +223 -0
- package/bundled-test-harness/assets/{model-cost-tier-test-Hu2-LpEw.js → model-cost-tier-test-CDXxio0B.js} +1 -1
- package/bundled-test-harness/assets/{motion-DeM7UCtd.js → motion-BiMvMS08.js} +1 -1
- package/bundled-test-harness/assets/{node-BAM535H5.js → node-BWOLCOMI.js} +95 -6
- package/bundled-test-harness/assets/{operation-lowering-test-CG1gatuH.js → operation-lowering-test-C_CaTJ0F.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-card-chooser-test-Bo5ZnF_x.js → operator-mode-card-chooser-test-CU5f7q6r.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-links-test-DVx_LQOU.js → operator-mode-links-test-CoeF3prV.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-test-CCbFrYgN.js → operator-mode-test-B9c6N5FF.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-ui-test-Dm2Bf6Vn.js → operator-mode-ui-test-CJQxypf_.js} +1 -1
- package/bundled-test-harness/assets/{overlay-menu-items-test-Bk5AidFD.js → overlay-menu-items-test-CFWZ-tzq.js} +1 -1
- package/bundled-test-harness/assets/{overlays-CY7IgeOl.js → overlays-DZXjQykn.js} +3 -3
- package/bundled-test-harness/assets/{overlays-DhVKmSP-.js → overlays-hrAO43jG.js} +1 -1
- package/bundled-test-harness/assets/{panel-t9QyU1lY.js → panel-CXmB6GMs.js} +15 -75
- package/bundled-test-harness/assets/{panel-CUYJ8oHS.css → panel-D2zkZoIr.css} +39 -31
- package/bundled-test-harness/assets/{past-sessions-test-Dq-Y9T6W.js → past-sessions-test-7FgLJlog.js} +1 -1
- package/bundled-test-harness/assets/{permission-checking-realm-test-CuImx8ie.js → permission-checking-realm-test-Cm9eB-hE.js} +1 -1
- package/bundled-test-harness/assets/pill-menu-Ctn3JNqG.js +154 -0
- package/bundled-test-harness/assets/{pill-menu-test-kpWBGrX5.js → pill-menu-test-Cf2xO2w6.js} +1 -1
- package/bundled-test-harness/assets/plate-CnatwP3n.js +2 -0
- package/bundled-test-harness/assets/{plate-B4Lz2Ulo.js → plate-Cu5EnXQZ.js} +3 -3
- package/bundled-test-harness/assets/player-Bwn5tEsh.js +2 -0
- package/bundled-test-harness/assets/{player-B965XH82.js → player-CVdsx7Ng.js} +3 -3
- package/bundled-test-harness/assets/presence-BKkR2au7.js +553 -0
- package/bundled-test-harness/assets/presence-CcEXVges.js +2 -0
- package/bundled-test-harness/assets/{quick-look-test-VW5_W7dm.js → quick-look-test-DrqgCLlM.js} +1 -1
- package/bundled-test-harness/assets/{realm-api-keys-Tlj0OK1c.css → realm-api-keys-16eN_yJb.css} +35 -25
- package/bundled-test-harness/assets/{realm-api-keys-sg--fohv.js → realm-api-keys-ZJedocgl.js} +11 -5
- package/bundled-test-harness/assets/realm-api-keys-m-EOSWuV.js +2 -0
- package/bundled-test-harness/assets/{realm-api-keys-test-mrgvnhFJ.js → realm-api-keys-test-Ba_1CKJu.js} +3 -5
- package/bundled-test-harness/assets/{realm-api-keys-test-Cy7qSoAd.js → realm-api-keys-test-jXscHLbS.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-DvpP1xf-.js → realm-config-DIOozzdQ.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-policy-standing-test-DJk-ftgH.js → realm-config-policy-standing-test-DEFQEEvl.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-routing-rule-edit-test-CTPiJrAL.js → realm-config-routing-rule-edit-test-CtLYsxzq.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-settings-test-CUa-PjMC.js → realm-config-settings-test-Dsatuv-T.js} +1 -1
- package/bundled-test-harness/assets/{reasoning-test-CeeGGwvf.js → reasoning-test-3p_PygAV.js} +1 -1
- package/bundled-test-harness/assets/{rendered-markdown-B8TH0yN4.js → rendered-markdown-DPEXS7MQ.js} +1 -1
- package/bundled-test-harness/assets/{rendered-markdown-test-CBRsIBqW.js → rendered-markdown-test-DxbJ1nFX.js} +1 -1
- package/bundled-test-harness/assets/{room-message-test-BTxZ_W74.js → room-message-test-BBE8UIPX.js} +1 -1
- package/bundled-test-harness/assets/{run-BSEzn_PH.js → run-CfyzhBXb.js} +2 -2
- package/bundled-test-harness/assets/{schema-generation-test-De1eamwr.js → schema-generation-test-hkFzUr7-.js} +1 -1
- package/bundled-test-harness/assets/{scrolling-test-C7R0UASi.js → scrolling-test-B8CudFE9.js} +2 -2
- package/bundled-test-harness/assets/{searchable-option-test-BiOqxIIs.js → searchable-option-test-B8lZyJij.js} +1 -1
- package/bundled-test-harness/assets/{sending-test-DOXQ0Tvx.js → sending-test-BtH1z2uE.js} +1 -1
- package/bundled-test-harness/assets/{skills-test-BjCYI4Z_.js → skills-test-DIQvoCLm.js} +1 -1
- package/bundled-test-harness/assets/src-CEw1V0fi.js +11 -0
- package/bundled-test-harness/assets/{src-Bhefkhqj.js → src-CbBcj3xm.js} +11 -5
- package/bundled-test-harness/assets/{src-BDDYD1rs.js → src-D8bpj2tb.js} +2 -2
- package/bundled-test-harness/assets/{src-DySBAkac.js → src-Df4Vutrk.js} +3 -3
- package/bundled-test-harness/assets/{stick-to-bottom-resize-test-hN0AjdXP.js → stick-to-bottom-resize-test-Du4B3w2x.js} +4 -4
- package/bundled-test-harness/assets/{store-test-BD99H9dQ.js → store-test-DoHgyfQA.js} +1 -1
- package/bundled-test-harness/assets/{test-support-BcjhIHQa.js → test-support-BOFeeYuE.js} +25 -2
- package/bundled-test-harness/assets/test-support-CXESl4Wf.js +3 -0
- package/bundled-test-harness/assets/test-support-CgWmpebE.js +3 -0
- package/bundled-test-harness/assets/{test-support-Dl6FChlV.js → test-support-xTaMuaOA.js} +4 -27
- package/bundled-test-harness/assets/{tests-BqLDuQog.js → tests-DXX9TrgY.js} +759 -750
- package/bundled-test-harness/assets/{text-input-validator-test-CW6Ay7bo.js → text-input-validator-test-D56aDP2e.js} +1 -1
- package/bundled-test-harness/assets/{titles-CFt3dEls.js → titles-BUbpcMoe.js} +3 -3
- package/bundled-test-harness/assets/titles-D80DwOQU.js +2 -0
- package/bundled-test-harness/assets/{to-device-streaming-test-DRxNCi7Z.js → to-device-streaming-test-Ug8UnlUl.js} +1 -1
- package/bundled-test-harness/assets/{token-usage-test-Ck9rB3Ie.js → token-usage-test-BolKbZVL.js} +1 -1
- package/bundled-test-harness/assets/{tools-DRCjAZCp.js → tools-CkouX4wE.js} +1 -1
- package/bundled-test-harness/assets/{tools-test-B3zniq10.js → tools-test-CdYgRu5Z.js} +1 -1
- package/bundled-test-harness/assets/{turn-settling-test-C0X6hL7x.js → turn-settling-test-CS0PlpTg.js} +1 -1
- package/bundled-test-harness/assets/{turn-summary-0XOoAv3j.js → turn-summary-CAZVdcBd.js} +1 -1
- package/bundled-test-harness/assets/{unshare-key-Dfy1IK9g.js → unshare-key-W_bNI_MF.js} +0 -1
- package/bundled-test-harness/index.html +29 -28
- package/bundled-test-harness/tests/index.html +29 -28
- package/bundled-types/base/command.d.ts +0 -1
- package/bundled-types/choreo/choreo.gts +21 -42
- package/bundled-types/choreo/test-support/index.ts +9 -30
- package/bundled-types/glimmer-motion/index.ts +11 -3
- package/bundled-types/glimmer-motion/memory.ts +302 -0
- package/bundled-types/glimmer-motion/node.ts +125 -14
- package/bundled-types/glimmer-motion/presence-types.ts +13 -5
- package/bundled-types/glimmer-motion/presence.gts +199 -11
- package/bundled-types/glimmer-motion/snapshot.ts +280 -0
- package/bundled-types/glimmer-motion/test-support/index.ts +36 -4
- package/bundled-types/host-app/components/ai-assistant/llm-select.gts +3 -0
- package/bundled-types/host-app/components/ai-assistant/skill-menu/index.gts +3 -0
- package/bundled-types/host-app/components/matrix/room.gts +15 -3
- package/bundled-types/host-app/components/operator-mode/dashboard/add-workspace.gts +5 -3
- package/bundled-types/host-app/components/operator-mode/dashboard/archived-workspace.gts +3 -5
- package/bundled-types/host-app/components/operator-mode/dashboard/catalog-shelf.gts +24 -7
- package/bundled-types/host-app/components/operator-mode/dashboard/workspace.gts +9 -4
- package/bundled-types/host-app/components/pill-menu/index.gts +74 -23
- package/bundled-types/host-app/lib/key-shares.ts +0 -1
- package/bundled-types/host-app/tools/get-key-share-usage.ts +0 -1
- package/dist/index.js +161 -161
- package/package.json +1 -1
- package/src/commands/keys/manage.ts +22 -6
- package/src/lib/user-keys.ts +18 -5
- package/bundled-test-harness/assets/choreo-Cd9yKlEK.js +0 -2
- package/bundled-test-harness/assets/clip-DnEy6oK5.js +0 -2
- package/bundled-test-harness/assets/film-C4NJTl9U.js +0 -2
- package/bundled-test-harness/assets/group-CPQojb-T.js +0 -2
- package/bundled-test-harness/assets/item-DGzMAZ9K.js +0 -2
- package/bundled-test-harness/assets/pill-menu-DaRpDMUm.js +0 -68
- package/bundled-test-harness/assets/plate-B7JjCVMj.js +0 -2
- package/bundled-test-harness/assets/player-DpGXST02.js +0 -2
- package/bundled-test-harness/assets/presence-CmLmFwhO.js +0 -2
- package/bundled-test-harness/assets/presence-Sv4y4tQy.js +0 -230
- package/bundled-test-harness/assets/realm-api-keys-DkdlXgAg.js +0 -2
- package/bundled-test-harness/assets/src-BpmcuCfH.js +0 -10
- package/bundled-test-harness/assets/test-support-CB3auo8h.js +0 -2
- package/bundled-test-harness/assets/test-support-DfGKwg35.js +0 -3
- package/bundled-test-harness/assets/titles-DVhrJZzS.js +0 -2
|
@@ -23,10 +23,14 @@ import { modifier } from 'ember-modifier';
|
|
|
23
23
|
import {
|
|
24
24
|
closestMotionConfig,
|
|
25
25
|
flushPendingMounts,
|
|
26
|
+
keepAsMemory,
|
|
26
27
|
motionSpeed,
|
|
27
28
|
postRender,
|
|
29
|
+
readAnimations,
|
|
28
30
|
registerBusyProbe,
|
|
31
|
+
type Shown,
|
|
29
32
|
snapshotOnRender,
|
|
33
|
+
trackScrolls,
|
|
30
34
|
} from 'glimmer-motion';
|
|
31
35
|
|
|
32
36
|
import { type BeaconRef, measureBeacons } from './beacons.ts';
|
|
@@ -290,43 +294,6 @@ interface Signature {
|
|
|
290
294
|
|
|
291
295
|
let debugStyle: HTMLStyleElement | undefined;
|
|
292
296
|
|
|
293
|
-
/**
|
|
294
|
-
* Two rules every region shares. An orphaned skin flies with its
|
|
295
|
-
* backdrop-filters OFF: a backdrop-filter can never be cached — it
|
|
296
|
-
* re-samples and re-blurs whatever is behind it on every frame — and
|
|
297
|
-
* inside a scaling, fading leaver that is paid at full price for the
|
|
298
|
-
* whole flight (the camera demo's four glass panels, over its gradient
|
|
299
|
-
* washes, was the return trip's jank). It is also the wrong picture: a
|
|
300
|
-
* skin is a memory of the OLD scene, and its glass sampling the new
|
|
301
|
-
* scene behind it shows the wrong world. The panels keep their own
|
|
302
|
-
* translucent grounds, so they still read as glass in the crossfade.
|
|
303
|
-
*
|
|
304
|
-
* And an orphaned skin does not REPLAY ITS BIRTH: re-inserting a node
|
|
305
|
-
* restarts every CSS animation on it, so a leaver re-parented into the
|
|
306
|
-
* orphan layer re-ran its entrance choreography — rules drawing
|
|
307
|
-
* themselves, letters settling out of a blur — while it was supposed to
|
|
308
|
-
* be dying. A one-frame-old memory acting out its own arrival is the
|
|
309
|
-
* flash the Towers film chased for a night. The run's own exit motion
|
|
310
|
-
* is WAAPI and survives re-parenting untouched; only stylesheet
|
|
311
|
-
* animations restart, and on a memory they are all lies.
|
|
312
|
-
*/
|
|
313
|
-
let orphanStyle: HTMLStyleElement | undefined;
|
|
314
|
-
function ensureOrphanStyle() {
|
|
315
|
-
if (orphanStyle) {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
orphanStyle = document.createElement('style');
|
|
319
|
-
orphanStyle.setAttribute('data-choreo-style', '');
|
|
320
|
-
orphanStyle.textContent =
|
|
321
|
-
'[data-choreo-orphans] *,' +
|
|
322
|
-
'[data-choreo-orphans] *::before,' +
|
|
323
|
-
'[data-choreo-orphans] *::after' +
|
|
324
|
-
'{backdrop-filter:none!important;' +
|
|
325
|
-
'-webkit-backdrop-filter:none!important;' +
|
|
326
|
-
'animation:none!important;}';
|
|
327
|
-
document.head.appendChild(orphanStyle);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
297
|
/**
|
|
331
298
|
* A fingerprint of the collected timeline tree, for telling an EDIT from
|
|
332
299
|
* noise while a run's own orphans are aloft. Mid-flight, compiled cues are
|
|
@@ -457,6 +424,10 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
|
|
|
457
424
|
/** participants whose Presence is letting them go: removed once, not on every pass while they leave */
|
|
458
425
|
private leaving = new Set<ChoreoNode>();
|
|
459
426
|
private snapshots = new Map<ChoreoNode, Snapshot>();
|
|
427
|
+
/** the stylesheet animations the region showed before the render, for the memories of what it removes */
|
|
428
|
+
private shownAnimations: Shown['animations'] = new Map();
|
|
429
|
+
/** every scroll offset inside the region, kept as it scrolls: a removed element has lost its own */
|
|
430
|
+
private scrolls?: Shown['scrolls'];
|
|
460
431
|
/** participants with no layout box before this pass (display: none) */
|
|
461
432
|
private unrendered = new Set<ChoreoNode>();
|
|
462
433
|
/** the whole region had no layout box before this pass (hidden by an ancestor) */
|
|
@@ -736,6 +707,9 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
|
|
|
736
707
|
return;
|
|
737
708
|
}
|
|
738
709
|
this.rootSnapshot = this.element?.getBoundingClientRect();
|
|
710
|
+
this.shownAnimations = this.element
|
|
711
|
+
? readAnimations(this.element)
|
|
712
|
+
: new Map();
|
|
739
713
|
const wired = this.args.inView ? this.nearView : undefined;
|
|
740
714
|
for (const node of this.participants) {
|
|
741
715
|
const el = node.element;
|
|
@@ -1551,10 +1525,13 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
|
|
|
1551
1525
|
el.style.height = `${page.height / sy}px`;
|
|
1552
1526
|
el.style.boxSizing = 'border-box';
|
|
1553
1527
|
el.style.margin = '0';
|
|
1554
|
-
|
|
1555
|
-
//
|
|
1556
|
-
|
|
1557
|
-
|
|
1528
|
+
// a memory of the old scene: inert, held at the frame it showed, and one
|
|
1529
|
+
// texture transformed rather than a subtree re-rastered per frame
|
|
1530
|
+
keepAsMemory(
|
|
1531
|
+
el,
|
|
1532
|
+
{ animations: this.shownAnimations, scrolls: this.scrolls ?? new Map() },
|
|
1533
|
+
() => layer.appendChild(el),
|
|
1534
|
+
);
|
|
1558
1535
|
this.orphans.add(s.node);
|
|
1559
1536
|
this.orphanBounds.set(s.node, s.initial);
|
|
1560
1537
|
}
|
|
@@ -1615,7 +1592,8 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
|
|
|
1615
1592
|
host = modifier((el: HTMLDivElement) => {
|
|
1616
1593
|
this.element = el;
|
|
1617
1594
|
setChoreoHost(el, this);
|
|
1618
|
-
|
|
1595
|
+
const scrolls = trackScrolls(el);
|
|
1596
|
+
this.scrolls = scrolls.scrolls;
|
|
1619
1597
|
if (getComputedStyle(el).position === 'static') {
|
|
1620
1598
|
el.style.position = 'relative';
|
|
1621
1599
|
}
|
|
@@ -1629,6 +1607,7 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
|
|
|
1629
1607
|
}
|
|
1630
1608
|
}
|
|
1631
1609
|
return () => {
|
|
1610
|
+
scrolls.stop();
|
|
1632
1611
|
setChoreoHost(el, undefined);
|
|
1633
1612
|
this.element = undefined;
|
|
1634
1613
|
};
|
|
@@ -90,37 +90,16 @@ export function orphanCount(root: HTMLElement = testRoot()): number {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* where the OLD scene stood. Clicking it does nothing; measuring it measures
|
|
102
|
-
* the past. Neither failure names itself.
|
|
103
|
-
*
|
|
104
|
-
* So any assertion or interaction a test performs while a crossing may be
|
|
105
|
-
* aloft should come through here. A raised sprite (`c.Raise`) is deliberately
|
|
106
|
-
* still live: it is the real element on a different layer, not a copy.
|
|
93
|
+
* `live()` / `liveAll()`: queries that skip memories. Mid-crossing an identity
|
|
94
|
+
* exists twice: the arriving element in the live tree, and the departing one
|
|
95
|
+
* the region keeps as a memory in its orphan layer so it can be flown and
|
|
96
|
+
* faded. The memory's test hooks carry a suffix (`data-test-title-memory`),
|
|
97
|
+
* but a class or role selector matches both, and the orphan layer is the
|
|
98
|
+
* region's FIRST child, so a bare `querySelector` answers with the memory. A
|
|
99
|
+
* raised sprite (`c.Raise`) is deliberately still live: it is the real element
|
|
100
|
+
* on a different layer, not a memory.
|
|
107
101
|
*/
|
|
108
|
-
export
|
|
109
|
-
selector: string,
|
|
110
|
-
root: ParentNode = testRoot(),
|
|
111
|
-
): E | null {
|
|
112
|
-
return liveAll<E>(selector, root)[0] ?? null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** every live match for `selector`, in document order — see `live()` */
|
|
116
|
-
export function liveAll<E extends Element = HTMLElement>(
|
|
117
|
-
selector: string,
|
|
118
|
-
root: ParentNode = testRoot(),
|
|
119
|
-
): E[] {
|
|
120
|
-
return [...root.querySelectorAll<E>(selector)].filter(
|
|
121
|
-
(el) => !el.closest('[data-choreo-orphans]'),
|
|
122
|
-
);
|
|
123
|
-
}
|
|
102
|
+
export { live, liveAll } from 'glimmer-motion/test-support';
|
|
124
103
|
|
|
125
104
|
/**
|
|
126
105
|
* Elements still wearing a transform that nothing is animating.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* glimmer-motion — Motion's React glue re-done for Glimmer, on the unchanged motion-dom engine.
|
|
3
3
|
* Deep imports (`glimmer-motion/motion`, `glimmer-motion/presence`, …) are the same modules.
|
|
4
4
|
*
|
|
5
|
-
* `registerBusyProbe
|
|
6
|
-
* on top (such as `@cardstack/choreo`) uses to take part in
|
|
7
|
-
* motion elements' lifecycles.
|
|
5
|
+
* `registerBusyProbe`, the participant-host API and memories are the seams a
|
|
6
|
+
* layer built on top (such as `@cardstack/choreo`) uses to take part in
|
|
7
|
+
* settling, in motion elements' lifecycles, and in keeping leavers on screen.
|
|
8
8
|
*/
|
|
9
9
|
export { type BusyProbe, registerBusyProbe } from './activity.ts';
|
|
10
10
|
export { createDragControls, DragControls } from './gestures/drag-controls.ts';
|
|
@@ -38,6 +38,14 @@ export {
|
|
|
38
38
|
RENDER_DETECTOR_ATTRIBUTE,
|
|
39
39
|
snapshotOnRender,
|
|
40
40
|
} from './layout-group.gts';
|
|
41
|
+
export type { ScrollPosition, Shown, ShownAnimation } from './memory.ts';
|
|
42
|
+
export {
|
|
43
|
+
keepAsMemory,
|
|
44
|
+
MEMORY_ATTRIBUTE,
|
|
45
|
+
MEMORY_HOOK_SUFFIX,
|
|
46
|
+
readAnimations,
|
|
47
|
+
trackScrolls,
|
|
48
|
+
} from './memory.ts';
|
|
41
49
|
export type { MotionEl, MotionProps } from './motion.ts';
|
|
42
50
|
export { default as motion, MotionModifier } from './motion.ts';
|
|
43
51
|
export type { MotionConfigContext } from './motion-config.gts';
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A memory: an element that has left, kept on screen for as long as its exit
|
|
3
|
+
* plays. `<Presence @mode="snapshot">` keeps a copy of its leaver as one, and
|
|
4
|
+
* a layer that parks removed elements itself (Choreo's orphan layer) keeps
|
|
5
|
+
* each element it parks as one, so a leaver looks and behaves the same
|
|
6
|
+
* whichever layer let it go.
|
|
7
|
+
*
|
|
8
|
+
* A memory shows the frame its element last showed, and nothing in it acts:
|
|
9
|
+
*
|
|
10
|
+
* - Its CSS animations hold still. An element put back in the document
|
|
11
|
+
* restarts every stylesheet animation on it, so an entrance that had
|
|
12
|
+
* finished would play again while the element leaves. Each animation the
|
|
13
|
+
* element was showing pauses at the time it had reached, and any other (one
|
|
14
|
+
* that had finished, or one the re-insertion started) jumps to its end.
|
|
15
|
+
* Animations on a scroll timeline follow the restored scroll offsets. The
|
|
16
|
+
* engine's own animations are not stylesheet animations, and are left
|
|
17
|
+
* alone.
|
|
18
|
+
* - Its scroll offsets are restored: an element taken out of the document
|
|
19
|
+
* loses them.
|
|
20
|
+
* - Its backdrop filters are off. A backdrop filter re-samples whatever is
|
|
21
|
+
* behind it on every frame, at full price inside a moving, fading element,
|
|
22
|
+
* and what is behind a memory is the scene that replaced it.
|
|
23
|
+
* - It is composited as one texture (`will-change: transform, opacity`).
|
|
24
|
+
* That also makes it a stacking context, and the containing block of every
|
|
25
|
+
* positioned descendant, `position: fixed` included: a descendant that was
|
|
26
|
+
* placed against a box outside the element is placed against the memory.
|
|
27
|
+
* - It is inert and hidden from assistive technology.
|
|
28
|
+
* - Its HTML elements carry no ids, `autofocus` or form-control names. A
|
|
29
|
+
* duplicate id that comes first in the document is what `getElementById`
|
|
30
|
+
* and `<label for>` find, and a checked radio button joining the document
|
|
31
|
+
* unchecks the live one in its group. Its SVG elements keep their ids,
|
|
32
|
+
* which its own fills, clips, masks and `<use>` references resolve through.
|
|
33
|
+
* A reference resolves to the first element in the document with its id,
|
|
34
|
+
* so while a memory comes before its replacement (as one in Choreo's orphan
|
|
35
|
+
* layer, its region's first child, does), the replacement's `url(#id)` and
|
|
36
|
+
* `<use>` references resolve to the memory's definitions. Definitions that
|
|
37
|
+
* change as their element is replaced need an id per version. Its iframes
|
|
38
|
+
* are blank, and its media neither plays nor loads.
|
|
39
|
+
* - Its test hooks carry a suffix: `data-test-title` becomes
|
|
40
|
+
* `data-test-title-memory`, with its value. A test's selector finds only the
|
|
41
|
+
* live element, which is what replaced the leaver or nothing, and a test that
|
|
42
|
+
* means the leaver names it with the suffix. `live()` and `liveAll()` in
|
|
43
|
+
* `glimmer-motion/test-support` skip memories for every other selector.
|
|
44
|
+
* - Its classes and its other attributes stay, so it looks as its element
|
|
45
|
+
* did wherever its styles are keyed on them. A rule keyed on an id
|
|
46
|
+
* (`#panel`, `:target`) no longer reaches it, so an element that may leave
|
|
47
|
+
* is styled by class. A memory put back in its parent (a
|
|
48
|
+
* `<Presence @mode="snapshot">` copy) therefore still matches structural
|
|
49
|
+
* selectors that read the live scene: `:has()`, `+`, `~`, `:nth-child()`,
|
|
50
|
+
* `:empty`. A rule that should see only live content excludes it, as in
|
|
51
|
+
* `:has(.menu:not([data-motion-memory], [data-motion-memory] *))`.
|
|
52
|
+
*
|
|
53
|
+
* Custom elements in a memory are connected again, and run their
|
|
54
|
+
* `connectedCallback`.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/** the attribute every memory carries */
|
|
58
|
+
export const MEMORY_ATTRIBUTE = 'data-motion-memory';
|
|
59
|
+
|
|
60
|
+
/** what a memory's `data-test-*` hooks are renamed with */
|
|
61
|
+
export const MEMORY_HOOK_SUFFIX = '-memory';
|
|
62
|
+
|
|
63
|
+
export interface ScrollPosition {
|
|
64
|
+
left: number;
|
|
65
|
+
top: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** a CSS animation on the document timeline that an element was showing */
|
|
69
|
+
export interface ShownAnimation {
|
|
70
|
+
finished: boolean;
|
|
71
|
+
/** its `animation-name` */
|
|
72
|
+
name: string;
|
|
73
|
+
/** the pseudo-element it runs on, or null for the element itself */
|
|
74
|
+
pseudoElement: string | null;
|
|
75
|
+
/** its current time, in milliseconds */
|
|
76
|
+
time: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** what a subtree showed before the render that removed it, for its memory to show */
|
|
80
|
+
export interface Shown {
|
|
81
|
+
/** the CSS animations each element was showing */
|
|
82
|
+
animations: { get(el: Element): readonly ShownAnimation[] | undefined };
|
|
83
|
+
/** each element's scroll offset */
|
|
84
|
+
scrolls: { get(el: Element): ScrollPosition | undefined };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** form controls whose name would join a live radio group or form submission */
|
|
88
|
+
const NAMED_CONTROLS =
|
|
89
|
+
'input, select, textarea, button, fieldset, output, object, form, iframe';
|
|
90
|
+
|
|
91
|
+
/** the element and every element inside it, in document order */
|
|
92
|
+
export function elementsOf(root: Element): Element[] {
|
|
93
|
+
return [root, ...root.querySelectorAll('*')];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** a stylesheet animation on the document timeline: a scroll-driven one follows its scroller instead */
|
|
97
|
+
function onDocumentTimeline(animation: Animation): animation is CSSAnimation {
|
|
98
|
+
return (
|
|
99
|
+
animation instanceof CSSAnimation &&
|
|
100
|
+
animation.timeline === document.timeline
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The CSS animations on the document timeline that `root` and the elements
|
|
106
|
+
* inside it are showing, by the element each runs on. Read it while `root` is
|
|
107
|
+
* still in the document: a removed element has none.
|
|
108
|
+
*/
|
|
109
|
+
export function readAnimations(root: Element): Map<Element, ShownAnimation[]> {
|
|
110
|
+
const shown = new Map<Element, ShownAnimation[]>();
|
|
111
|
+
for (const animation of root.getAnimations({ subtree: true })) {
|
|
112
|
+
const effect = animation.effect;
|
|
113
|
+
if (
|
|
114
|
+
!onDocumentTimeline(animation) ||
|
|
115
|
+
!(effect instanceof KeyframeEffect) ||
|
|
116
|
+
!effect.target
|
|
117
|
+
) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const list = shown.get(effect.target) ?? [];
|
|
121
|
+
list.push({
|
|
122
|
+
finished: animation.playState === 'finished',
|
|
123
|
+
name: animation.animationName,
|
|
124
|
+
pseudoElement: effect.pseudoElement,
|
|
125
|
+
time: Number(animation.currentTime ?? 0),
|
|
126
|
+
});
|
|
127
|
+
shown.set(effect.target, list);
|
|
128
|
+
}
|
|
129
|
+
return shown;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** the scroll offsets of `root` and of every element inside it that is scrolled */
|
|
133
|
+
export function readScrolls(root: Element): Map<Element, ScrollPosition> {
|
|
134
|
+
const scrolls = new Map<Element, ScrollPosition>();
|
|
135
|
+
for (const el of elementsOf(root)) {
|
|
136
|
+
if (el.scrollTop || el.scrollLeft) {
|
|
137
|
+
scrolls.set(el, { top: el.scrollTop, left: el.scrollLeft });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return scrolls;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** what `root` shows now. Read it while `root` is still in the document. */
|
|
144
|
+
export function readShown(root: Element): Shown {
|
|
145
|
+
return { animations: readAnimations(root), scrolls: readScrolls(root) };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** what `original` showed, moved onto the matching elements of `copy`, a deep clone of it */
|
|
149
|
+
export function shownOnCopy(
|
|
150
|
+
shown: Shown,
|
|
151
|
+
original: Element,
|
|
152
|
+
copy: Element,
|
|
153
|
+
): Shown {
|
|
154
|
+
const from = elementsOf(original);
|
|
155
|
+
const to = elementsOf(copy);
|
|
156
|
+
const animations = new Map<Element, readonly ShownAnimation[]>();
|
|
157
|
+
const scrolls = new Map<Element, ScrollPosition>();
|
|
158
|
+
from.forEach((el, i) => {
|
|
159
|
+
const counterpart = to[i];
|
|
160
|
+
if (!counterpart) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const animation = shown.animations.get(el);
|
|
164
|
+
if (animation) {
|
|
165
|
+
animations.set(counterpart, animation);
|
|
166
|
+
}
|
|
167
|
+
const scroll = shown.scrolls.get(el);
|
|
168
|
+
if (scroll) {
|
|
169
|
+
scrolls.set(counterpart, scroll);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
return { animations, scrolls };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Records the scroll offset of every element inside `root` as it scrolls. For
|
|
177
|
+
* a layer that learns which elements left only after the render that removed
|
|
178
|
+
* them, by when their offsets are gone.
|
|
179
|
+
*/
|
|
180
|
+
export function trackScrolls(root: Element): {
|
|
181
|
+
scrolls: Shown['scrolls'];
|
|
182
|
+
stop(): void;
|
|
183
|
+
} {
|
|
184
|
+
const scrolls = new WeakMap<Element, ScrollPosition>();
|
|
185
|
+
const record = (event: Event) => {
|
|
186
|
+
const el = event.target;
|
|
187
|
+
if (el instanceof Element) {
|
|
188
|
+
scrolls.set(el, { top: el.scrollTop, left: el.scrollLeft });
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
// scroll events do not bubble, but they do pass ancestors while capturing
|
|
192
|
+
root.addEventListener('scroll', record, { capture: true, passive: true });
|
|
193
|
+
return {
|
|
194
|
+
scrolls,
|
|
195
|
+
stop: () => root.removeEventListener('scroll', record, { capture: true }),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let memoryStyle: HTMLStyleElement | undefined;
|
|
200
|
+
|
|
201
|
+
function ensureMemoryStyle() {
|
|
202
|
+
if (memoryStyle?.isConnected) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const m = `[${MEMORY_ATTRIBUTE}]`;
|
|
206
|
+
memoryStyle = document.createElement('style');
|
|
207
|
+
memoryStyle.setAttribute('data-motion-memory-style', '');
|
|
208
|
+
memoryStyle.textContent =
|
|
209
|
+
`${m},${m}::before,${m}::after,${m} *,${m} *::before,${m} *::after` +
|
|
210
|
+
'{backdrop-filter:none!important;-webkit-backdrop-filter:none!important}';
|
|
211
|
+
document.head.appendChild(memoryStyle);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** nothing in a memory can be found by id or test hook, submitted, focused, loaded or played */
|
|
215
|
+
function neutralize(el: Element) {
|
|
216
|
+
for (const name of el.getAttributeNames()) {
|
|
217
|
+
if (name === 'data-test' || name.startsWith('data-test-')) {
|
|
218
|
+
el.setAttribute(`${name}${MEMORY_HOOK_SUFFIX}`, el.getAttribute(name)!);
|
|
219
|
+
el.removeAttribute(name);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (!(el instanceof SVGElement)) {
|
|
223
|
+
el.removeAttribute('id');
|
|
224
|
+
}
|
|
225
|
+
el.removeAttribute('autofocus');
|
|
226
|
+
if (el.matches(NAMED_CONTROLS)) {
|
|
227
|
+
el.removeAttribute('name');
|
|
228
|
+
}
|
|
229
|
+
if (el instanceof HTMLIFrameElement) {
|
|
230
|
+
el.removeAttribute('srcdoc');
|
|
231
|
+
el.src = 'about:blank';
|
|
232
|
+
} else if (el instanceof HTMLMediaElement) {
|
|
233
|
+
el.removeAttribute('autoplay');
|
|
234
|
+
el.preload = 'none';
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** each stylesheet animation pauses where it was shown, or jumps to its end */
|
|
239
|
+
function holdAnimations(root: Element, shown: Shown['animations']) {
|
|
240
|
+
for (const animation of root.getAnimations({ subtree: true })) {
|
|
241
|
+
const effect = animation.effect;
|
|
242
|
+
if (!onDocumentTimeline(animation) || !(effect instanceof KeyframeEffect)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const was = effect.target
|
|
246
|
+
? shown
|
|
247
|
+
.get(effect.target)
|
|
248
|
+
?.find(
|
|
249
|
+
(a) =>
|
|
250
|
+
a.name === animation.animationName &&
|
|
251
|
+
a.pseudoElement === effect.pseudoElement,
|
|
252
|
+
)
|
|
253
|
+
: undefined;
|
|
254
|
+
try {
|
|
255
|
+
if (was && !was.finished) {
|
|
256
|
+
animation.pause();
|
|
257
|
+
animation.currentTime = was.time;
|
|
258
|
+
} else {
|
|
259
|
+
animation.finish();
|
|
260
|
+
}
|
|
261
|
+
} catch {
|
|
262
|
+
// an endless animation has no end to jump to
|
|
263
|
+
animation.cancel();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Makes `root` a memory of what `shown` describes, and puts it back in the
|
|
270
|
+
* document with `insert`. Ids, form-control names and test hooks are changed
|
|
271
|
+
* before `insert` runs, so none of them joins the document even for a moment;
|
|
272
|
+
* scroll offsets and animations are restored after it, in the same task, so
|
|
273
|
+
* no frame shows them reset. Whatever is already in a memory (a leaver lifted
|
|
274
|
+
* out of one that left with it, a copy still fading inside a leaver) is put
|
|
275
|
+
* back and restored, and not changed twice.
|
|
276
|
+
*/
|
|
277
|
+
export function keepAsMemory(
|
|
278
|
+
root: HTMLElement | SVGElement,
|
|
279
|
+
shown: Shown,
|
|
280
|
+
insert: () => void,
|
|
281
|
+
) {
|
|
282
|
+
ensureMemoryStyle();
|
|
283
|
+
const elements = elementsOf(root);
|
|
284
|
+
for (const el of elements) {
|
|
285
|
+
if (!el.closest(`[${MEMORY_ATTRIBUTE}]`)) {
|
|
286
|
+
neutralize(el);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
root.setAttribute(MEMORY_ATTRIBUTE, '');
|
|
290
|
+
root.setAttribute('inert', '');
|
|
291
|
+
root.setAttribute('aria-hidden', 'true');
|
|
292
|
+
root.style.willChange = 'transform, opacity';
|
|
293
|
+
insert();
|
|
294
|
+
for (const el of elements) {
|
|
295
|
+
const scroll = shown.scrolls.get(el);
|
|
296
|
+
if (scroll) {
|
|
297
|
+
el.scrollTop = scroll.top;
|
|
298
|
+
el.scrollLeft = scroll.left;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
holdAnimations(root, shown.animations);
|
|
302
|
+
}
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
SVGVisualElement,
|
|
38
38
|
visualElementStore,
|
|
39
39
|
} from 'motion-dom';
|
|
40
|
+
import { MotionGlobalConfig } from 'motion-utils';
|
|
40
41
|
|
|
41
42
|
import { registerBusyProbe } from './activity.ts';
|
|
42
43
|
import { initFeatures } from './features.ts';
|
|
@@ -65,6 +66,19 @@ import { postRender } from './scheduler.ts';
|
|
|
65
66
|
import { motionSpeed, onMotionSpeed, slowed } from './speed.ts';
|
|
66
67
|
import { styleValue } from './unitless.ts';
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* The values motion-dom can hand to the browser to play (its
|
|
71
|
+
* `acceleratedValues`), each with the property its computed style shows it
|
|
72
|
+
* under. While the browser plays one, its motion value does not follow frame
|
|
73
|
+
* by frame.
|
|
74
|
+
*/
|
|
75
|
+
const BROWSER_PLAYED: Record<string, string> = {
|
|
76
|
+
opacity: 'opacity',
|
|
77
|
+
clipPath: 'clip-path',
|
|
78
|
+
filter: 'filter',
|
|
79
|
+
transform: 'transform',
|
|
80
|
+
};
|
|
81
|
+
|
|
68
82
|
/** motion-dom's own `defaultLayoutTransition`, which it does not export */
|
|
69
83
|
const DEFAULT_LAYOUT_TRANSITION = {
|
|
70
84
|
duration: 0.45,
|
|
@@ -272,7 +286,7 @@ const pendingPops: { apply: () => void }[] = [];
|
|
|
272
286
|
let popFlushBooked = false;
|
|
273
287
|
|
|
274
288
|
/** where a popLayout leaver is sitting, and the box it has to be pinned inside */
|
|
275
|
-
interface PopSeat {
|
|
289
|
+
export interface PopSeat {
|
|
276
290
|
h: number;
|
|
277
291
|
isRTL: boolean;
|
|
278
292
|
left: number;
|
|
@@ -282,7 +296,29 @@ interface PopSeat {
|
|
|
282
296
|
w: number;
|
|
283
297
|
}
|
|
284
298
|
|
|
285
|
-
|
|
299
|
+
/**
|
|
300
|
+
* The two insets that pin a box lifted out of flow to its seat: one inline,
|
|
301
|
+
* from the side `anchorX` names (mirrored in right-to-left text), and one
|
|
302
|
+
* block, from the side `anchorY` names.
|
|
303
|
+
*/
|
|
304
|
+
export function seatInsets(
|
|
305
|
+
seat: PopSeat,
|
|
306
|
+
anchorX: 'left' | 'right',
|
|
307
|
+
anchorY: 'top' | 'bottom',
|
|
308
|
+
): {
|
|
309
|
+
block: ['top' | 'bottom', number];
|
|
310
|
+
inline: ['left' | 'right', number];
|
|
311
|
+
} {
|
|
312
|
+
const { w, h, top, left, parentWidth, parentHeight, isRTL } = seat;
|
|
313
|
+
const fromLeft = (anchorX === 'left') !== isRTL;
|
|
314
|
+
return {
|
|
315
|
+
inline: fromLeft ? ['left', left] : ['right', parentWidth - w - left],
|
|
316
|
+
block:
|
|
317
|
+
anchorY === 'bottom' ? ['bottom', parentHeight - h - top] : ['top', top],
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function seatOf(el: MotionEl): PopSeat | undefined {
|
|
286
322
|
const parent = (el as HTMLElement).offsetParent as HTMLElement | null;
|
|
287
323
|
const cs = getComputedStyle(el);
|
|
288
324
|
const w = parseFloat(cs.width),
|
|
@@ -618,6 +654,8 @@ export class MotionNode implements MotionParticipant, PopMeasurable {
|
|
|
618
654
|
private popStyle?: HTMLStyleElement;
|
|
619
655
|
/** the seat measured before the render that removed this element */
|
|
620
656
|
private popSeat?: PopSeat;
|
|
657
|
+
/** snapshot: the seat this element had in the last frame (see recordSeat) */
|
|
658
|
+
private shownSeat?: PopSeat;
|
|
621
659
|
private unregisterPop?: () => void;
|
|
622
660
|
/** participant identity: a participant host above selects this element by these */
|
|
623
661
|
id: string | null = null;
|
|
@@ -1326,6 +1364,87 @@ export class MotionNode implements MotionParticipant, PopMeasurable {
|
|
|
1326
1364
|
}
|
|
1327
1365
|
}
|
|
1328
1366
|
|
|
1367
|
+
/**
|
|
1368
|
+
* `<Presence @mode="snapshot">`: reads the seat this element has now, for
|
|
1369
|
+
* the copy of it to be pinned to if it leaves. <Presence> calls this in each
|
|
1370
|
+
* frame that follows a render, and whenever this element or the box it is
|
|
1371
|
+
* pinned inside resizes. That covers the moves a render or a resize makes;
|
|
1372
|
+
* a move that is neither, such as a transition on a sibling's width that
|
|
1373
|
+
* shifts this element without resizing either box, leaves the seat where it
|
|
1374
|
+
* was. Returns the elements the seat depends on, for <Presence> to watch
|
|
1375
|
+
* for resizes.
|
|
1376
|
+
*/
|
|
1377
|
+
recordSeat(): Element[] {
|
|
1378
|
+
const el = this.element;
|
|
1379
|
+
if (this.destroyed || !el?.isConnected) {
|
|
1380
|
+
return [];
|
|
1381
|
+
}
|
|
1382
|
+
this.shownSeat = seatOf(el);
|
|
1383
|
+
const parent = (el as HTMLElement).offsetParent;
|
|
1384
|
+
return parent ? [el, parent] : [el];
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* `<Presence @mode="snapshot">`: what a static copy of this element needs to
|
|
1389
|
+
* play the exit this element would have played. <Presence> reads it from
|
|
1390
|
+
* its diff, before the render that removes the element.
|
|
1391
|
+
*
|
|
1392
|
+
* `seat` is the one recorded before this render (see recordSeat). By the
|
|
1393
|
+
* time the diff runs, content earlier in the template has already been
|
|
1394
|
+
* patched for this render, and may have moved the element somewhere no
|
|
1395
|
+
* frame showed it.
|
|
1396
|
+
*
|
|
1397
|
+
* The copy starts from every value this element shows now. The browser
|
|
1398
|
+
* plays some of them (opacity, clip-path, filter, a whole transform), and a
|
|
1399
|
+
* motion value does not follow those frame by frame, so those are read from
|
|
1400
|
+
* the computed style. The copy inherits nothing from the variant tree: the
|
|
1401
|
+
* tree it would inherit from has already moved on.
|
|
1402
|
+
*
|
|
1403
|
+
* Undefined when there is no exit to play: the element declares no `exit`
|
|
1404
|
+
* of its own, has not mounted, or animations are skipped.
|
|
1405
|
+
*/
|
|
1406
|
+
exitForCopy():
|
|
1407
|
+
| { element: MotionEl; props: MotionProps; seat: PopSeat | undefined }
|
|
1408
|
+
| undefined {
|
|
1409
|
+
const { element, ve, latest } = this;
|
|
1410
|
+
if (this.destroyed || !element?.isConnected || !ve?.current || !latest) {
|
|
1411
|
+
return undefined;
|
|
1412
|
+
}
|
|
1413
|
+
const props = latest.props as MotionProps;
|
|
1414
|
+
if (
|
|
1415
|
+
props.exit === undefined ||
|
|
1416
|
+
this.config.skipAnimations ||
|
|
1417
|
+
MotionGlobalConfig.skipAnimations ||
|
|
1418
|
+
MotionGlobalConfig.instantAnimations
|
|
1419
|
+
) {
|
|
1420
|
+
return undefined;
|
|
1421
|
+
}
|
|
1422
|
+
const computed = getComputedStyle(element);
|
|
1423
|
+
const initial: Record<string, string | number> = {};
|
|
1424
|
+
ve.values.forEach((value, key) => {
|
|
1425
|
+
const css = BROWSER_PLAYED[key];
|
|
1426
|
+
if (css) {
|
|
1427
|
+
const shown = computed.getPropertyValue(css);
|
|
1428
|
+
initial[key] = key === 'opacity' ? parseFloat(shown) : shown;
|
|
1429
|
+
} else {
|
|
1430
|
+
initial[key] = value.get() as string | number;
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
return {
|
|
1434
|
+
element,
|
|
1435
|
+
seat: this.shownSeat,
|
|
1436
|
+
props: {
|
|
1437
|
+
initial,
|
|
1438
|
+
exit: props.exit,
|
|
1439
|
+
variants: props.variants,
|
|
1440
|
+
transition: props.transition,
|
|
1441
|
+
custom: props.custom,
|
|
1442
|
+
transformTemplate: props.transformTemplate,
|
|
1443
|
+
inherit: false,
|
|
1444
|
+
},
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1329
1448
|
/** popLayout: a leaving element is lifted out of flow at its last box so siblings can close up */
|
|
1330
1449
|
private pop(el: MotionEl, isPresent: boolean, handle: PresenceHandle) {
|
|
1331
1450
|
if (isPresent) {
|
|
@@ -1342,18 +1461,10 @@ export class MotionNode implements MotionParticipant, PopMeasurable {
|
|
|
1342
1461
|
if (!seat) {
|
|
1343
1462
|
return;
|
|
1344
1463
|
}
|
|
1345
|
-
const { w, h
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
const
|
|
1349
|
-
handle.anchorX === 'left'
|
|
1350
|
-
? isRTL
|
|
1351
|
-
? `right:${right}`
|
|
1352
|
-
: `left:${left}`
|
|
1353
|
-
: isRTL
|
|
1354
|
-
? `left:${left}`
|
|
1355
|
-
: `right:${right}`;
|
|
1356
|
-
const y = handle.anchorY === 'bottom' ? `bottom:${bottom}` : `top:${top}`;
|
|
1464
|
+
const { w, h } = seat;
|
|
1465
|
+
const { inline, block } = seatInsets(seat, handle.anchorX, handle.anchorY);
|
|
1466
|
+
const x = `${inline[0]}:${inline[1]}`;
|
|
1467
|
+
const y = `${block[0]}:${block[1]}`;
|
|
1357
1468
|
const id = `pop-${Math.random().toString(36).slice(2, 8)}`;
|
|
1358
1469
|
el.dataset['motionPopId'] = id;
|
|
1359
1470
|
// measured above against the layout as it stands; the lift itself waits
|