From 2e2a6223bc4131ebce37dae2970070e3562721cd Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Sun, 11 Oct 2026 22:50:26 -0400 Subject: [PATCH 1/3] Move the Choreo docs into the package READMEs and the gallery realm Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018tqpxGBVBUCe7nACwttGag --- .claude/skills/choreo-create/SKILL.md | 4 +- .../references/composition-recipes.md | 2 +- .../references/spatial-and-film.md | 24 +- .claude/skills/choreo-regions/SKILL.md | 2 +- .claude/skills/choreo-scene/SKILL.md | 4 +- .claude/skills/motion-pattern/SKILL.md | 6 +- .../app/components/films/sagrada-score.gts | 2 +- .../app/components/films/towers-score.gts | 2 +- .../choreo-film-app/app/lib/films/sagrada.ts | 4 +- .../choreo-film-app/app/lib/films/towers.ts | 4 +- .../tests/integration/film/graph-test.gts | 2 +- packages/choreo-film-app/tools/tograph.mjs | 398 ++++++++++ .../choreo-film-app/vendor/sylva-scene.ts | 4 +- packages/choreo-gallery/.prettierignore | 5 +- packages/choreo-gallery/.template-lintrc.cjs | 3 - packages/choreo-gallery/README.md | 50 +- packages/choreo-gallery/docs/choreo-player.md | 151 ---- .../docs/film-graph/tograph.mjs | 219 ------ .../docs/gallery/BOXEL-DEPLOYMENT.md | 80 -- .../choreo-gallery/docs/source-repo/AGENTS.md | 90 --- .../choreo-gallery/docs/source-repo/README.md | 678 ---------------- packages/choreo-gallery/eslint.config.mjs | 4 - .../realm/asset/towers-model.html | 2 +- .../{notes => realm/design}/README.md | 27 +- .../design}/camera-api-wishlist.md | 0 .../design}/choreo-composition.md | 2 +- .../design}/demos-as-applications.md | 0 .../{notes => realm/design}/dialkit.md | 2 +- .../design}/docs-studies-plan.md | 0 .../design}/documentation-site.md | 9 +- .../{notes => realm/design}/drift.md | 2 +- .../external-clock-camera-seek-handoff.md | 0 .../{notes => realm/design}/film-construct.md | 2 +- .../design}/film-graph/CONSTRUCTS.md | 0 .../{docs => realm/design}/film-graph/GAPS.md | 0 .../{docs => realm/design}/film-graph/PLAN.md | 8 +- .../design}/film-graph/README.md | 28 +- .../design}/film-graph/REVISIONS.md | 2 +- .../design}/film-graph/SPIKES.md | 0 .../design}/film-graph/TRANSITIONS.md | 0 .../design/film-graph/long-take.gts.md} | 6 + .../design/film-graph/mockup.gts.md} | 6 + .../design/film-graph/replate.gts.md} | 6 + .../design/film-graph/sagrada.gts.md} | 6 + .../design/film-graph/sylva.gts.md} | 6 + .../design/film-graph/towers.gts.md} | 6 + .../design}/gallery/PERFORMANCE.md | 0 .../design}/gallery/QUICK-TOUR.md | 0 .../{docs => realm/design}/gallery/README.md | 2 +- .../design}/gallery/ROOM-DESIGN.md | 0 .../design}/gallery/TILE-CAPACITY.md | 0 .../design}/gallery/TOUR-PACING.md | 0 .../design}/gallery/TOUR-SCRIPT.md | 0 .../design}/hyperframes-choreo-foundation.md | 0 .../{notes => realm/design}/sagrada-film.md | 4 +- .../{notes => realm/design}/sagrada-vo.md | 0 .../design}/sagrada-web-model.md | 0 .../subdivision-browser-timing-test.md | 0 .../design}/sylva-one-world.md | 0 .../{notes => realm/design}/towers-about.md | 4 +- .../design}/towers-porting-delta.md | 2 +- .../{notes => realm/design}/towers-quality.md | 2 +- .../design}/towers-vo-files.md | 4 +- .../design}/towers-vo-pronunciation.md | 6 +- .../{notes => realm/design}/towers-vo.md | 2 +- .../design}/towers-wall-text.md | 0 packages/choreo-gallery/realm/docs/README.md | 53 ++ .../{ => realm}/docs/choreo-constructs.md | 8 +- .../{ => realm}/docs/choreo-splices.md | 2 +- .../{ => realm}/docs/choreography.md | 8 +- .../{ => realm}/docs/demo-recording.md | 2 +- .../{ => realm}/docs/dom-in-3d.md | 4 +- .../choreo-gallery/{ => realm}/docs/film.md | 12 +- .../choreo-gallery/{ => realm}/docs/guide.md | 15 +- .../{ => realm}/docs/nested-choreo.md | 20 +- .../{ => realm}/docs/planes-and-cameras.md | 4 +- .../{ => realm}/docs/postmortem-follow.md | 0 .../{ => realm}/docs/step-vocabulary.md | 4 +- .../{docs => realm}/licenses/lucide-icons.txt | 0 .../choreo-gallery/realm/notes/towers.gts | 2 +- .../choreo-gallery/realm/reel/compositor.ts | 6 +- .../realm/reel/feature-reel.gts | 6 +- packages/choreo-gallery/realm/stages/fold.gts | 2 +- .../{docs => tools}/api-inventory.json | 0 packages/choreo-gallery/tools/check-realm.mjs | 260 +++++- packages/choreo-player/README.md | 32 +- .../app/components/dial-controls.gts | 2 +- .../app/components/examples/drift.gts | 4 +- .../app/components/examples/escort.gts | 2 +- .../app/components/examples/fold.gts | 2 +- .../app/components/examples/hang.gts | 2 +- .../app/components/feature-reel.gts | 8 +- .../app/components/films/sagrada-score.gts | 2 +- .../app/components/films/towers-score.gts | 2 +- .../app/components/notes/towers.gts | 6 +- .../app/components/sylva-stage.gts | 2 +- .../app/content}/guide-topics.json | 0 .../app/content/guides/core-api-inventory.md | 740 +++++++++--------- .../app/content/guides/core-config.md | 2 +- .../app/content/guides/core-drag-handles.md | 2 +- .../app/content/guides/core-drag.md | 2 +- .../app/content/guides/core-elements.md | 2 +- .../app/content/guides/core-first-app.md | 2 +- .../app/content/guides/core-host.md | 2 +- .../app/content/guides/core-layout.md | 2 +- .../content/guides/core-page-transitions.md | 2 +- .../app/content/guides/core-presence.md | 2 +- .../app/content/guides/core-reorder.md | 2 +- .../app/content/guides/core-scroll.md | 2 +- .../app/content/guides/core-springs.md | 2 +- .../app/content/guides/core-targets.md | 2 +- .../app/content/guides/core-tempo.md | 2 +- .../app/content/guides/core-test-geometry.md | 2 +- .../app/content/guides/core-test-timing.md | 2 +- .../app/content/guides/core-testing.md | 2 +- .../app/content/guides/core-tweens.md | 2 +- .../app/content/guides/core-variants.md | 2 +- .../app/content/guides/film-adjustments.md | 2 +- .../app/content/guides/film-attachments.md | 2 +- .../app/content/guides/film-audio-mix.md | 2 +- .../app/content/guides/film-audio.md | 2 +- .../app/content/guides/film-clips.md | 2 +- .../app/content/guides/film-clock.md | 2 +- .../app/content/guides/film-first-export.md | 2 +- .../content/guides/film-graph-extension.md | 2 +- .../app/content/guides/film-graph.md | 2 +- .../app/content/guides/film-interface.md | 2 +- .../app/content/guides/film-joins.md | 2 +- .../app/content/guides/film-media-graph.md | 2 +- .../app/content/guides/film-picture.md | 2 +- .../app/content/guides/film-player.md | 2 +- .../app/content/guides/film-poses.md | 2 +- .../app/content/guides/film-schedule.md | 2 +- .../app/content/guides/film-seam-readiness.md | 2 +- .../app/content/guides/film-shots.md | 2 +- .../app/content/guides/film-start.md | 2 +- .../app/content/guides/film-transport.md | 2 +- .../app/content/guides/film-typography.md | 2 +- .../app/content/guides/film-utilities.md | 2 +- .../content/guides/film-world-annotations.md | 2 +- .../app/content/guides/interactive-anchors.md | 2 +- .../app/content/guides/interactive-arming.md | 2 +- .../app/content/guides/interactive-beacons.md | 4 +- .../app/content/guides/interactive-blocks.md | 2 +- .../content/guides/interactive-commands.md | 2 +- .../content/guides/interactive-composites.md | 2 +- .../content/guides/interactive-crossings.md | 2 +- .../content/guides/interactive-delivery.md | 2 +- .../app/content/guides/interactive-follow.md | 2 +- .../app/content/guides/interactive-gates.md | 2 +- .../app/content/guides/interactive-holds.md | 2 +- .../app/content/guides/interactive-move.md | 2 +- .../guides/interactive-property-steps.md | 2 +- .../app/content/guides/interactive-queries.md | 2 +- .../guides/interactive-raise-scroll.md | 2 +- .../content/guides/interactive-registry.md | 2 +- .../app/content/guides/interactive-run.md | 2 +- .../app/content/guides/interactive-start.md | 2 +- .../app/content/guides/interactive-tethers.md | 2 +- .../content/guides/interactive-timelines.md | 2 +- .../app/content/guides/spatial-camera3d.md | 2 +- .../app/content/guides/spatial-coordinates.md | 2 +- .../app/content/guides/spatial-dom.md | 2 +- .../app/content/guides/spatial-first-scene.md | 2 +- .../app/content/guides/spatial-frame.md | 2 +- .../app/content/guides/spatial-paths.md | 2 +- .../app/content/guides/spatial-relative.md | 2 +- .../app/content/guides/spatial-start.md | 2 +- .../choreo-test-app/app/lib/compositor.ts | 6 +- packages/choreo-test-app/app/lib/dial.ts | 2 +- packages/choreo-test-app/app/lib/drift.ts | 6 +- .../choreo-test-app/app/lib/films/sagrada.ts | 4 +- .../choreo-test-app/app/lib/films/towers.ts | 4 +- .../choreo-test-app/app/lib/sylva/scene.ts | 4 +- .../choreo-test-app/app/routes/application.ts | 2 +- packages/choreo-test-app/app/styles/app.css | 2 +- .../public/asset/sagrada/README.md | 4 +- .../public/asset/towers-model.html | 2 +- .../public/asset/towers/vo/README.md | 2 +- .../choreo-test-app/tests/unit/drift-test.ts | 2 +- packages/choreo-test-app/vite.config.mjs | 2 +- packages/choreo/README.md | 579 +++++++++++++- packages/choreo/src/anchors.ts | 2 +- packages/choreo/src/beacons.ts | 2 +- packages/choreo/src/choreo.gts | 2 +- packages/choreo/src/film/clips.ts | 2 +- packages/choreo/src/film/film.gts | 10 +- packages/choreo/src/film/graph/nodes.gts | 2 +- packages/choreo/src/film/index.ts | 2 +- packages/choreo/src/film/types.ts | 2 +- packages/choreo/src/path.ts | 2 +- packages/choreo/src/run.ts | 4 +- packages/choreo/src/space.ts | 2 +- packages/choreo/src/steps.gts | 4 +- packages/choreo/src/types.ts | 8 +- .../tests/integration/choreo/attach-test.gts | 2 +- .../integration/choreo/camera-preset-test.gts | 2 +- .../integration/choreo/camera-tether-test.gts | 2 +- .../choreo/camera-transport-test.gts | 2 +- .../tests/integration/choreo/choreo-test.gts | 4 +- .../integration/choreo/crossing-test.gts | 2 +- .../integration/choreo/delivery-test.gts | 2 +- .../tests/integration/choreo/gates-test.gts | 2 +- .../integration/choreo/lane-spike-test.gts | 2 +- .../integration/choreo/move-extras-test.gts | 2 +- .../tests/integration/choreo/perform-test.gts | 2 +- .../integration/choreo/plane-flight-test.gts | 2 +- .../integration/choreo/raise-scroll-test.gts | 2 +- .../tests/integration/choreo/space-test.ts | 2 +- .../integration/choreo/world-dock-test.gts | 2 +- .../film/join-presentation-test.gts | 2 +- packages/glimmer-motion/README.md | 116 ++- .../choreo-gallery-crossing-test.gts | 2 +- .../choreo-gallery-escort-test.gts | 2 +- .../choreo-gallery-presentation-test.gts | 2 +- .../integration/choreo-gallery-wires-test.gts | 2 +- 216 files changed, 2129 insertions(+), 1932 deletions(-) create mode 100644 packages/choreo-film-app/tools/tograph.mjs delete mode 100644 packages/choreo-gallery/docs/choreo-player.md delete mode 100644 packages/choreo-gallery/docs/film-graph/tograph.mjs delete mode 100644 packages/choreo-gallery/docs/gallery/BOXEL-DEPLOYMENT.md delete mode 100644 packages/choreo-gallery/docs/source-repo/AGENTS.md delete mode 100644 packages/choreo-gallery/docs/source-repo/README.md rename packages/choreo-gallery/{notes => realm/design}/README.md (65%) rename packages/choreo-gallery/{notes => realm/design}/camera-api-wishlist.md (100%) rename packages/choreo-gallery/{notes => realm/design}/choreo-composition.md (99%) rename packages/choreo-gallery/{notes => realm/design}/demos-as-applications.md (100%) rename packages/choreo-gallery/{notes => realm/design}/dialkit.md (99%) rename packages/choreo-gallery/{docs => realm/design}/docs-studies-plan.md (100%) rename packages/choreo-gallery/{docs => realm/design}/documentation-site.md (89%) rename packages/choreo-gallery/{notes => realm/design}/drift.md (99%) rename packages/choreo-gallery/{notes => realm/design}/external-clock-camera-seek-handoff.md (100%) rename packages/choreo-gallery/{notes => realm/design}/film-construct.md (99%) rename packages/choreo-gallery/{docs => realm/design}/film-graph/CONSTRUCTS.md (100%) rename packages/choreo-gallery/{docs => realm/design}/film-graph/GAPS.md (100%) rename packages/choreo-gallery/{docs => realm/design}/film-graph/PLAN.md (98%) rename packages/choreo-gallery/{docs => realm/design}/film-graph/README.md (78%) rename packages/choreo-gallery/{docs => realm/design}/film-graph/REVISIONS.md (99%) rename packages/choreo-gallery/{docs => realm/design}/film-graph/SPIKES.md (100%) rename packages/choreo-gallery/{docs => realm/design}/film-graph/TRANSITIONS.md (100%) rename packages/choreo-gallery/{docs/film-graph/long-take.gts => realm/design/film-graph/long-take.gts.md} (98%) rename packages/choreo-gallery/{docs/film-graph/mockup.gts => realm/design/film-graph/mockup.gts.md} (98%) rename packages/choreo-gallery/{docs/film-graph/replate.gts => realm/design/film-graph/replate.gts.md} (98%) rename packages/choreo-gallery/{docs/film-graph/sagrada.gts => realm/design/film-graph/sagrada.gts.md} (99%) rename packages/choreo-gallery/{docs/film-graph/sylva.gts => realm/design/film-graph/sylva.gts.md} (98%) rename packages/choreo-gallery/{docs/film-graph/towers.gts => realm/design/film-graph/towers.gts.md} (99%) rename packages/choreo-gallery/{docs => realm/design}/gallery/PERFORMANCE.md (100%) rename packages/choreo-gallery/{docs => realm/design}/gallery/QUICK-TOUR.md (100%) rename packages/choreo-gallery/{docs => realm/design}/gallery/README.md (98%) rename packages/choreo-gallery/{docs => realm/design}/gallery/ROOM-DESIGN.md (100%) rename packages/choreo-gallery/{docs => realm/design}/gallery/TILE-CAPACITY.md (100%) rename packages/choreo-gallery/{docs => realm/design}/gallery/TOUR-PACING.md (100%) rename packages/choreo-gallery/{docs => realm/design}/gallery/TOUR-SCRIPT.md (100%) rename packages/choreo-gallery/{notes => realm/design}/hyperframes-choreo-foundation.md (100%) rename packages/choreo-gallery/{notes => realm/design}/sagrada-film.md (98%) rename packages/choreo-gallery/{notes => realm/design}/sagrada-vo.md (100%) rename packages/choreo-gallery/{docs => realm/design}/sagrada-web-model.md (100%) rename packages/choreo-gallery/{notes => realm/design}/subdivision-browser-timing-test.md (100%) rename packages/choreo-gallery/{notes => realm/design}/sylva-one-world.md (100%) rename packages/choreo-gallery/{notes => realm/design}/towers-about.md (95%) rename packages/choreo-gallery/{notes => realm/design}/towers-porting-delta.md (99%) rename packages/choreo-gallery/{notes => realm/design}/towers-quality.md (99%) rename packages/choreo-gallery/{notes => realm/design}/towers-vo-files.md (97%) rename packages/choreo-gallery/{notes => realm/design}/towers-vo-pronunciation.md (97%) rename packages/choreo-gallery/{notes => realm/design}/towers-vo.md (99%) rename packages/choreo-gallery/{notes => realm/design}/towers-wall-text.md (100%) create mode 100644 packages/choreo-gallery/realm/docs/README.md rename packages/choreo-gallery/{ => realm}/docs/choreo-constructs.md (99%) rename packages/choreo-gallery/{ => realm}/docs/choreo-splices.md (99%) rename packages/choreo-gallery/{ => realm}/docs/choreography.md (97%) rename packages/choreo-gallery/{ => realm}/docs/demo-recording.md (99%) rename packages/choreo-gallery/{ => realm}/docs/dom-in-3d.md (99%) rename packages/choreo-gallery/{ => realm}/docs/film.md (99%) rename packages/choreo-gallery/{ => realm}/docs/guide.md (93%) rename packages/choreo-gallery/{ => realm}/docs/nested-choreo.md (93%) rename packages/choreo-gallery/{ => realm}/docs/planes-and-cameras.md (98%) rename packages/choreo-gallery/{ => realm}/docs/postmortem-follow.md (100%) rename packages/choreo-gallery/{ => realm}/docs/step-vocabulary.md (98%) rename packages/choreo-gallery/{docs => realm}/licenses/lucide-icons.txt (100%) rename packages/choreo-gallery/{docs => tools}/api-inventory.json (100%) rename packages/{choreo-gallery/docs => choreo-test-app/app/content}/guide-topics.json (100%) diff --git a/.claude/skills/choreo-create/SKILL.md b/.claude/skills/choreo-create/SKILL.md index 6a37ca3776d..82f08919a04 100644 --- a/.claude/skills/choreo-create/SKILL.md +++ b/.claude/skills/choreo-create/SKILL.md @@ -42,11 +42,11 @@ composition idea) in `packages/choreo-gallery/realm/demos/.json`. The test app keeps a copy in `packages/choreo-test-app/app/content/demo-lessons.json` for its guide pages; change both, since the realm check fails when they differ. The concept guides are `packages/choreo-test-app/app/content/guides/*.md`. -`packages/choreo-gallery/docs/api-inventory.json` maps public APIs to concept +`packages/choreo-gallery/tools/api-inventory.json` maps public APIs to concept guides; look up the relevant entries rather than loading it whole. For a tutorial, isolate one observable relationship and predict the result of a parameter change. The docs-only spatial/film studies in -`packages/choreo-gallery/docs/docs-studies-plan.md` are planned, not shipped. +`packages/choreo-gallery/realm/design/docs-studies-plan.md` are planned, not shipped. Every control must supply a named variable consumed by that example. Show units, use meaningful ranges, and retain source defaults. The test app's diff --git a/.claude/skills/choreo-create/references/composition-recipes.md b/.claude/skills/choreo-create/references/composition-recipes.md index 2d9020a08eb..d631119c1b6 100644 --- a/.claude/skills/choreo-create/references/composition-recipes.md +++ b/.claude/skills/choreo-create/references/composition-recipes.md @@ -24,4 +24,4 @@ Use the catalog to resolve the components named below. These are starting points - **Architectural film:** the `towers` and `sagrada` film routes and their film/score components. Reuse the Film graph: spine, chapters, shots, picture actors, type, voice, and joins. Distinguish exact seeking from the chased camera's cut mode. - **Guided gallery:** `packages/choreo-test-app/app/components/widget-room.gts`, and `widget-quick-tour.ts`, `widget-tour.ts` and `widget-narration.ts` in `packages/choreo-test-app/app/lib/`. Organize chapters around capabilities, score the camera and actions together, and budget live demos to the active view. Preserve a preview until the replacement is visibly ready. -Read `packages/choreo-gallery/docs/film.md` for as-built Film contracts, `packages/choreo-gallery/docs/demo-recording.md` for ownership, and `packages/choreo-gallery/docs/dom-in-3d.md` for the DOM projection boundary. Design documents sometimes describe proposed capabilities; use exported source to confirm availability. +Read `packages/choreo-gallery/realm/docs/film.md` for as-built Film contracts, `packages/choreo-gallery/realm/docs/demo-recording.md` for ownership, and `packages/choreo-gallery/realm/docs/dom-in-3d.md` for the DOM projection boundary. Design documents sometimes describe proposed capabilities; use exported source to confirm availability. diff --git a/.claude/skills/choreo-create/references/spatial-and-film.md b/.claude/skills/choreo-create/references/spatial-and-film.md index 73598aad219..9929dc9349b 100644 --- a/.claude/skills/choreo-create/references/spatial-and-film.md +++ b/.claude/skills/choreo-create/references/spatial-and-film.md @@ -4,17 +4,17 @@ Choose the boundary before choosing a demo. Read only the row needed for the tas Guide paths below are under `packages/choreo-test-app/app/content/guides/`; implementation exports remain the authority for accepted arguments. -| Intent | Read | Minimal source reference | -| -------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------- | -| Frame or pan across DOM | spatial-frame.md | examples/camera.gts; long-take/board.gts | -| Relative pose or coordinate conversion | spatial-relative.md; spatial-coordinates.md | `packages/choreo/src/choreo.gts` vocabulary; `app/lib/css3d.ts` | -| 3D camera path and look target | spatial-camera3d.md; spatial-paths.md | examples/mockup.gts; examples/long-take.gts | -| Place live DOM on a mesh plane | spatial-dom.md; spatial-compositing.md | examples/mockup.gts; `packages/choreo-gallery/docs/dom-in-3d.md` | -| Film graph, shots and picture actor | film-graph.md; film-picture.md | tower-film.gts; sagrada-film.gts | -| Seek, pause or capture | film-transport.md; film-schedule.md | `packages/choreo-gallery/docs/demo-recording.md`; `packages/choreo-player/src/` | -| Joins and readiness | film-joins.md; film-seam-readiness.md | `packages/choreo/src/film/` | -| Separate clips, audio and captions | film-audio.md; film-audio-mix.md; film-typography.md | `app/lib/widget-narration.ts`; `@cardstack/choreo/film` exports | -| World labels and adjustments | film-world-annotations.md; film-adjustments.md | picture capability implementation and its Film score | +| Intent | Read | Minimal source reference | +| -------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Frame or pan across DOM | spatial-frame.md | examples/camera.gts; long-take/board.gts | +| Relative pose or coordinate conversion | spatial-relative.md; spatial-coordinates.md | `packages/choreo/src/choreo.gts` vocabulary; `app/lib/css3d.ts` | +| 3D camera path and look target | spatial-camera3d.md; spatial-paths.md | examples/mockup.gts; examples/long-take.gts | +| Place live DOM on a mesh plane | spatial-dom.md; spatial-compositing.md | examples/mockup.gts; `packages/choreo-gallery/realm/docs/dom-in-3d.md` | +| Film graph, shots and picture actor | film-graph.md; film-picture.md | tower-film.gts; sagrada-film.gts | +| Seek, pause or capture | film-transport.md; film-schedule.md | `packages/choreo-gallery/realm/docs/demo-recording.md`; `packages/choreo-player/src/` | +| Joins and readiness | film-joins.md; film-seam-readiness.md | `packages/choreo/src/film/` | +| Separate clips, audio and captions | film-audio.md; film-audio-mix.md; film-typography.md | `app/lib/widget-narration.ts`; `@cardstack/choreo/film` exports | +| World labels and adjustments | film-world-annotations.md; film-adjustments.md | picture capability implementation and its Film score | Component paths are relative to `packages/choreo-test-app/app/components/`; `app/` paths are relative to `packages/choreo-test-app`. Other paths are repository-relative. Use the catalog @@ -31,6 +31,6 @@ nested cameras; independent hard-coded durations drift. A render barrier means the requested picture is ready, not merely that an arbitrary delay expired. For a tutorial, prefer a tiny procedural scene that isolates one relationship. -Use `packages/choreo-gallery/docs/docs-studies-plan.md` for the planned docs-only studies. They are not +Use `packages/choreo-gallery/realm/design/docs-studies-plan.md` for the planned docs-only studies. They are not shipped examples. Keep full museum films as composition references rather than forcing their assets, narration and lighting into every camera exercise. diff --git a/.claude/skills/choreo-regions/SKILL.md b/.claude/skills/choreo-regions/SKILL.md index d3919bfd578..0ff366f0f65 100644 --- a/.claude/skills/choreo-regions/SKILL.md +++ b/.claude/skills/choreo-regions/SKILL.md @@ -108,4 +108,4 @@ barrier between tests — required, or passes leak across tests. Ground truth: `packages/choreo-test-app/app/components/examples/far-match.gts` (three bays, the on/off switch is just id scoping) and its Deep Dive note `packages/choreo-test-app/app/components/notes/far.gts`; design in -`packages/choreo-gallery/docs/nested-choreo.md`. +`packages/choreo-gallery/realm/docs/nested-choreo.md`. diff --git a/.claude/skills/choreo-scene/SKILL.md b/.claude/skills/choreo-scene/SKILL.md index a041c4f17b1..716f48e1476 100644 --- a/.claude/skills/choreo-scene/SKILL.md +++ b/.claude/skills/choreo-scene/SKILL.md @@ -102,7 +102,7 @@ document-global on purpose (chrome and outlet are separate regions). An inner `` is a separate scene — the outer region does not see its participants. Regions, far matching (an id leaving one region and appearing in another), and cross-region measurement: `choreo-regions`, and -`packages/choreo-gallery/docs/nested-choreo.md`. +`packages/choreo-gallery/realm/docs/nested-choreo.md`. ## When NOT @@ -113,4 +113,4 @@ One element, no ordering, no cross-element measurement → `motion-element` / Canonical demos, in `packages/choreo-test-app/app/components/examples/`: `inbox.gts` (beacons), `sequence.gts`, `interrupt.gts`, `slides.gts`, `far-match.gts`, `split-view.gts` (function values), -`lists.gts`. Design doc: `packages/choreo-gallery/docs/choreography.md`. +`lists.gts`. Design doc: `packages/choreo-gallery/realm/docs/choreography.md`. diff --git a/.claude/skills/motion-pattern/SKILL.md b/.claude/skills/motion-pattern/SKILL.md index ee976744c5b..f0328963cd8 100644 --- a/.claude/skills/motion-pattern/SKILL.md +++ b/.claude/skills/motion-pattern/SKILL.md @@ -68,10 +68,10 @@ Dives — the _reasoning_ behind each pattern choice (why the inbox uses beacons, how the interruption model holds, what the playhead samples). Read the note when modifying its demo. -Docs: `packages/choreo-gallery/docs/guide.md` (tutorial), the package READMEs +Docs: `packages/choreo-gallery/realm/docs/guide.md` (tutorial), the package READMEs `packages/glimmer-motion/README.md` and `packages/choreo/README.md` (API -reference), `packages/choreo-gallery/docs/choreography.md` + -`packages/choreo-gallery/docs/nested-choreo.md` (Choreo design). +reference), `packages/choreo-gallery/realm/docs/choreography.md` + +`packages/choreo-gallery/realm/docs/nested-choreo.md` (Choreo design). A card in a realm imports the same libraries through host's realm shims, under different rules (no direct motion-dom imports, state shared with host). For diff --git a/packages/choreo-film-app/app/components/films/sagrada-score.gts b/packages/choreo-film-app/app/components/films/sagrada-score.gts index 7745e8a209f..133da9c442e 100644 --- a/packages/choreo-film-app/app/components/films/sagrada-score.gts +++ b/packages/choreo-film-app/app/components/films/sagrada-score.gts @@ -2,7 +2,7 @@ * SAGRADA FAMÍLIA, AS A GRAPH — the film's score in the film vocabulary. * * Generated from the beat table in `lib/films/sagrada.ts` by - * `docs/film-graph/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, + * `tools/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, * stamp and trace is the table's; only the shape differs. It takes the * vocabulary as an argument so it can be rendered inside `` * and, in `tests/integration/film/graph-test.gts`, inside a bare diff --git a/packages/choreo-film-app/app/components/films/towers-score.gts b/packages/choreo-film-app/app/components/films/towers-score.gts index 28a1b620f14..097cbe7d6ad 100644 --- a/packages/choreo-film-app/app/components/films/towers-score.gts +++ b/packages/choreo-film-app/app/components/films/towers-score.gts @@ -2,7 +2,7 @@ * TOWERS, AS A GRAPH — the film's score in the film vocabulary. * * Generated from the beat table in `lib/films/towers.ts` by - * `docs/film-graph/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, + * `tools/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, * stamp and trace is the table's; only the shape differs. It takes the * vocabulary as an argument so it can be rendered inside `` * and, in `tests/integration/film/graph-test.gts`, inside a bare diff --git a/packages/choreo-film-app/app/lib/films/sagrada.ts b/packages/choreo-film-app/app/lib/films/sagrada.ts index cff09890b7e..94c2037466d 100644 --- a/packages/choreo-film-app/app/lib/films/sagrada.ts +++ b/packages/choreo-film-app/app/lib/films/sagrada.ts @@ -17,7 +17,7 @@ import { hex, RAD } from '@cardstack/choreo/film/math'; /** * SAGRADA — a film, at `/sagrada`. * - * The second reference film for the film construct (notes/film-construct.md), + * The second reference film for the film construct (packages/choreo-gallery/realm/design/film-construct.md), * and the first one cut ON it: everything here is the shot list, the * script, the chapters, the year clock, the traces sampled off the model, * and the front and back matter. The engine — the chased lens, the joins, @@ -167,7 +167,7 @@ export const VO_GAIN: Record = { * files in `public/sagrada/vo/`. The kinetic type is paced against the VOICE, * not against the beat: the last cue should land as the line is finishing, * whether the read is four seconds or thirteen. Estimating this went wrong - * once already (see notes/towers-vo.md) — so it is measured, and a re-record + * once already (see packages/choreo-gallery/realm/design/towers-vo.md) — so it is measured, and a re-record * means re-measuring. A beat missing from here paces against its own length. */ export const VO_SECS: Record = { diff --git a/packages/choreo-film-app/app/lib/films/towers.ts b/packages/choreo-film-app/app/lib/films/towers.ts index 510ee9d07cf..9aea6378bdd 100644 --- a/packages/choreo-film-app/app/lib/films/towers.ts +++ b/packages/choreo-film-app/app/lib/films/towers.ts @@ -10,7 +10,7 @@ import { hex, RAD } from '@cardstack/choreo/film/math'; /** * TOWERS — a film, at `/towers`. * - * The first reference film for the film construct (notes/film-construct.md), + * The first reference film for the film construct (packages/choreo-gallery/realm/design/film-construct.md), * now cut ON it: what is left here is the shot list, the script, the * chapters, the six climates, the traces sampled off the keep, and the * front and back matter in the film's own Japanese voice. The engine — @@ -226,7 +226,7 @@ export const VO_GAIN: Record = {}; * files in `public/towers/vo/`. The kinetic type is paced against the VOICE, * not against the beat: the last cue should land as the line is finishing, * whether the read is four seconds or thirteen. Estimating this went wrong - * once already (see notes/towers-vo.md) — so it is measured, and a re-record + * once already (see packages/choreo-gallery/realm/design/towers-vo.md) — so it is measured, and a re-record * means re-measuring. A beat missing from here paces against its own length. */ export const VO_SECS: Record = { diff --git a/packages/choreo-film-app/tests/integration/film/graph-test.gts b/packages/choreo-film-app/tests/integration/film/graph-test.gts index 244b101690e..16f359182a4 100644 --- a/packages/choreo-film-app/tests/integration/film/graph-test.gts +++ b/packages/choreo-film-app/tests/integration/film/graph-test.gts @@ -4,7 +4,7 @@ * Both reference films exist twice on this branch: as the beat tables the * engine has always run (`lib/films/*.ts`) and as graphs written in the * film vocabulary (`components/films/*-score.gts`, generated from those - * tables by `docs/film-graph/tograph.mjs`). These render each graph + * tables by `tools/tograph.mjs`). These render each graph * inside a bare `` and assert that what it compiles to is the * table — every row, every key — and the same measured reads. That is * the whole of Phase 2's first claim, and it is what lets the films diff --git a/packages/choreo-film-app/tools/tograph.mjs b/packages/choreo-film-app/tools/tograph.mjs new file mode 100644 index 00000000000..af89dc5227d --- /dev/null +++ b/packages/choreo-film-app/tools/tograph.mjs @@ -0,0 +1,398 @@ +// Turn a film's BEATS table (as written in the .gts) into the proposed +// graph syntax: chapters as blocks, shots as named steps, joins as +// siblings, everything else attached. Values are carried verbatim. +import fs from 'node:fs'; + +const [, , file] = process.argv; +// the prefix the vocabulary is reached by: `f.` inside , `@f.` in a component that takes it as an argument +const F = process.env.PREFIX ?? 'f.'; +const src = fs.readFileSync(file, 'utf8'); + +/* ---- a tiny tokenizer for the object literal ----------------------- */ +function skipWs(s, i) { + for (;;) { + while (i < s.length && /\s/.test(s[i])) { + i++; + } + if (s.startsWith('/*', i)) { + i = s.indexOf('*/', i) + 2; + continue; + } + if (s.startsWith('//', i)) { + i = s.indexOf('\n', i); + continue; + } + return i; + } +} +function readValue(s, i) { + i = skipWs(s, i); + const c = s[i]; + if (c === "'" || c === '"' || c === '`') { + let j = i + 1; + while (s[j] !== c) { + if (s[j] === '\\') { + j++; + } + j++; + } + return [s.slice(i, j + 1), j + 1]; + } + if (c === '{' || c === '[' || c === '(') { + const close = { '{': '}', '[': ']', '(': ')' }[c]; + let depth = 0, + j = i; + for (; j < s.length; j++) { + const d = s[j]; + if (d === "'" || d === '"' || d === '`') { + const [, k] = readValue(s, j); + j = k - 1; + continue; + } + if (s.startsWith('/*', j)) { + j = s.indexOf('*/', j) + 1; + continue; + } + if (d === c) { + depth++; + } else if (d === close) { + depth--; + if (depth === 0) { + break; + } + } + } + return [s.slice(i, j + 1), j + 1]; + } + let j = i; + while (j < s.length && !/[,}\]\n]/.test(s[j])) { + if (s[j] === '(') { + const [, k] = readValue(s, j); + j = k; + continue; + } + j++; + } + return [s.slice(i, j).trim(), j]; +} +function readObject(s) { + // s is '{ ... }' + const out = []; + let i = skipWs(s, 1); + while (i < s.length - 1) { + i = skipWs(s, i); + if (s[i] === '}') { + break; + } + const m = /^([A-Za-z_][A-Za-z0-9_]*)\s*:/.exec(s.slice(i)); + if (!m) { + i++; + continue; + } + i += m[0].length; + const [v, j] = readValue(s, i); + out.push([m[1], v]); + i = skipWs(s, j); + if (s[i] === ',') { + i++; + } + } + return out; +} +function readArray(s) { + const out = []; + let i = skipWs(s, 1); + while (i < s.length - 1) { + i = skipWs(s, i); + if (s[i] === ']') { + break; + } + const [v, j] = readValue(s, i); + out.push(v); + i = skipWs(s, j); + if (s[i] === ',') { + i++; + } + } + return out; +} + +/* ---- TS value → Glimmer expression --------------------------------- */ +function str(v) { + const inner = v.slice(1, -1).replace(/\\'/g, "'").replace(/"/g, '\\"'); + return `"${inner}"`; +} +function expr(v) { + v = v.trim(); + if (/^['"`]/.test(v)) { + return str(v); + } + if (v[0] === '{') { + return `(hash ${readObject(v) + .map(([k, x]) => `${k}=${expr(x)}`) + .join(' ')})`; + } + if (v[0] === '[') { + return `(array ${readArray(v).map(expr).join(' ')})`; + } + const call = /^([A-Za-z_][A-Za-z0-9_.]*)\((.*)\)$/s.exec(v); + if (call) { + const args = readArray('[' + call[2] + ']'); + return `(${call[1]} ${args.map(expr).join(' ')})`; + } + if (/^-?\d/.test(v) || v === 'true' || v === 'false' || v === 'null') { + return v; + } + if (/^[A-Z][A-Z0-9_]*$/.test(v)) { + return v; + } // a constant + if (/^-\s*[A-Z]/.test(v)) { + return `(neg ${v.slice(1).trim()})`; + } + if (/[-+*/]/.test(v)) { + return `(calc "${v}")`; + } // arithmetic: honest about it + return v; +} +function attr(k, v) { + return `@${k}={{${expr(v)}}}`; +} + +/* ---- the grouping: which key goes where ----------------------------- */ +// the shot's own facts; the head pose is flattened onto the shot, the +// tail pose is a child, an eye-level walk is a child +const SHOT = [ + 'id', + 'ticks', + 'cut', + 'lead', + 'lift', + 'bob', + 'hold', + 'follow', + 'to', + 'dissolve', + 'quality', +]; +const TYPE = ['mode', 'kicker', 'kanji', 'romaji', 'gloss', 'says', 'bare']; +// ADJUSTMENTS: parameter sets the PICTURE declares, yielded as contextual +// components under f.picture — the package knows none of these names. +// Look is a FILTER (it processes the frame); the rest hold values. +const FILTERS = [ + [F + 'picture.Look', ['grade', 'look', 'lut']], + [ + F + 'picture.Weather', + ['theme', 'hours', 'hoursOver', 'haze', 'rain', 'wx', 'wxCut', 'lightning'], + ], + [F + 'picture.Winter', ['winter']], + [F + 'picture.Sun', ['sun']], + [F + 'picture.Light', ['rim']], + [F + 'picture.Set', ['city', 'grass']], + [F + 'picture.Build', ['build', 'buildBy', 'settle', 'style']], +]; +const RENAME = { + kanji: 'word', + romaji: 'reading', + build: 'clock', + buildBy: 'by', + style: 'subject', + hoursOver: 'over', +}; +function pose(tag, v) { + return `${tag} ${readObject(v) + .map(([k, x]) => attr(k, x)) + .join(' ')}`; +} + +/* ---- walk the table ------------------------------------------------- */ +const start = src.indexOf('const BEATS: Beat[] = ['); +const body = readValue(src, start + 'const BEATS: Beat[] = '.length)[0]; +// beats plus the banner comments between them +const items = []; +{ + let i = 1; + while (i < body.length - 1) { + const ws = skipWs(body, i); + const gap = body.slice(i, ws); + const banner = /\/\*\s*-+\s*\*\n\s*\*\s*(.+?)\n/.exec(gap); + if (banner) { + items.push({ banner: banner[1].trim() }); + } + i = ws; + if (body[i] === ']' || i >= body.length - 1) { + break; + } + const [v, j] = readValue(body, i); + items.push({ beat: readObject(v) }); + i = skipWs(body, j); + if (body[i] === ',') { + i++; + } + } +} + +const chapters = JSON.parse(process.env.CHAPTERS); +const out = []; +const P = (s) => out.push(s); +let openCh = -1; +const ind = (n) => ' '.repeat(n); + +for (const it of items) { + if (it.banner) { + P(''); + P(`${ind(3)}{{! ${it.banner} }}`); + continue; + } + const b = Object.fromEntries(it.beat); + const ch = Number(b.ch); + if (ch !== openCh) { + if (openCh >= 0) { + P(`${ind(3)}`); + } + const c = chapters[ch]; + const tints = + (c.tint ? ` @tint="${c.tint}"` : '') + + (c.tintD ? ` @tintD="${c.tintD}"` : ''); + P( + `${ind(3)}<${F}Chapter @n="${c.n}" @title="${c.title}"${c.grade ? ` @grade="${c.grade}"` : ''}${c.lut ? ` @lut="${c.lut}"` : ''}${tints}>`, + ); + openCh = ch; + } + const id = b.id.slice(1, -1); + // the seam INTO this shot, as a sibling before it + if (b.join) { + const j = [`@presentation=${str(b.join)}`]; + if (b.dipTo) { + j.push(`@to=${str(b.dipTo)}`); + } + P(`${ind(4)}<${F}Join ${j.join(' ')} />`); + } + const shotArgs = SHOT.filter((k) => k in b).map((k) => + k === 'id' ? `@name="${id}"` : attr(k, b[k]), + ); + const head = b.cam ? readObject(b.cam).map(([k, x]) => attr(k, x)) : []; + P(`${ind(4)}<${F}Shot ${[...shotArgs, ...head].join(' ')}>`); + if (b.toCam) { + P(`${ind(5)}<${pose(F + 'To', b.toCam)} />`); + } + if (b.eye) { + const e = Object.fromEntries(readObject(b.eye)); + P( + `${ind(5)}<${F}Eye @from={{${expr(e.at)}}}${e.to ? ` @to={{${expr(e.to)}}}` : ''} @fov={{${e.fov}}} />`, + ); + } + const type = TYPE.filter((k) => k in b); + if (type.length) { + P( + `${ind(5)}<${F}Type ${type.map((k) => attr(RENAME[k] ?? k, b[k])).join(' ')} />`, + ); + } + if (b.vo) { + P( + `${ind(5)}<${F}Voice @line=${str(b.vo)} @read={{get VO_SECS "${id}"}}${b.hush ? ' @hush={{true}}' : ''} />`, + ); + } else if (b.hush) { + P(`${ind(5)}<${F}Voice @hush={{true}} />`); + } + for (const [tag, keys] of FILTERS) { + const have = keys.filter((k) => k in b); + if (!have.length) { + continue; + } + // an object-valued knob (sun: {az, el}; winter: {gust, pack}) is its own + // filter with the object's keys as flat, typed arguments; null clears it + if ( + have.length === 1 && + (b[have[0]].startsWith('{') || b[have[0]] === 'null') && + ['sun', 'winter'].includes(have[0]) + ) { + const v = b[have[0]]; + P( + `${ind(5)}<${tag} ${ + v === 'null' + ? '@off={{true}}' + : readObject(v) + .map(([k, x]) => attr(k, x)) + .join(' ') + } />`, + ); + continue; + } + P( + `${ind(5)}<${tag} ${have.map((k) => attr(RENAME[k] ?? k, b[k])).join(' ')} />`, + ); + } + if (b.mix) { + P( + `${ind(5)}<${F}sound.Mix ${readObject(b.mix) + .map(([k, v]) => attr(k, v)) + .join(' ')} />`, + ); + } + if (b.stamp) { + const s = Object.fromEntries(readObject(b.stamp)); + P( + `${ind(5)}<${F}Stamp @year={{${s.y}}}${s.at ? ` @at={{${s.at}}}` : ''} />`, + ); + } + if (b.sky) { + P( + `${ind(5)}<${F}Sky ${readObject(b.sky) + .map(([k, v]) => attr(k, v)) + .join(' ')} />`, + ); + } + if (b.mark) { + P( + `${ind(5)}<${F}Mark ${readObject(b.mark) + .map(([k, v]) => attr(k, v)) + .join(' ')} />`, + ); + } + if (b.trace) { + for (const t of readArray(b.trace)) { + P( + `${ind(5)}<${F}Trace ${readObject(t) + .map(([k, v]) => attr(k, v)) + .join(' ')} />`, + ); + } + } + if (b.cycle) { + P(`${ind(5)}<${F}Lineup @steps={{${expr(b.cycle)}}} />`); + } + if (b.photo) { + const p = Object.fromEntries(readObject(b.photo)); + P(`${ind(5)}<${F}Attach @lane={{1}}>`); + P( + `${ind(6)}<${F}Insert @src=${str(p.src)} @caption=${str(p.caption)} @credit=${str(p.credit)} />`, + ); + P(`${ind(5)}`); + } + if (b.clip) { + const c = Object.fromEntries(readObject(b.clip)); + const win = []; + if (c.at) { + win.push(`@at={{${c.at}}}`); + } + if (c.for) { + win.push(`@for={{${c.for}}}`); + } + if (c.end) { + win.push(`@end=${str(c.end)}`); + } + const kind = c.kind ? c.kind.slice(1, -1) : 'video'; + const comp = { freeze: 'Freeze', image: 'Still', video: 'Video' }[kind]; + const rest = Object.entries(c) + .filter(([k]) => !['at', 'for', 'end', 'kind'].includes(k)) + .map(([k, v]) => attr(k, v)); + P(`${ind(5)}<${F}Attach @lane={{2}} ${win.join(' ')}>`); + P(`${ind(6)}<${F}${comp} ${rest.join(' ')} />`); + P(`${ind(5)}`); + } + P(`${ind(4)}`); +} +if (openCh >= 0) { + P(`${ind(3)}`); +} +fs.writeFileSync(`/dev/stdout`, out.join('\n') + '\n'); diff --git a/packages/choreo-film-app/vendor/sylva-scene.ts b/packages/choreo-film-app/vendor/sylva-scene.ts index 26550261d42..7654f9a790f 100644 --- a/packages/choreo-film-app/vendor/sylva-scene.ts +++ b/packages/choreo-film-app/vendor/sylva-scene.ts @@ -19,7 +19,7 @@ * * WHY THIS SCENE. It was picked for its geometry, not its looks: a moss root * with an arch and a hollow under it is a shape that can stand in FRONT of a - * DOM card, which is exactly the case `docs/dom-in-3d.md` lists as unproven. + * DOM card, which is exactly the case `packages/choreo-gallery/realm/docs/dom-in-3d.md` lists as unproven. * A flat backdrop could not test occlusion at all. * * The one lucky alignment: its camera is already authored in pixels — @@ -3170,7 +3170,7 @@ export function createSylva(hosts: SylvaHosts): Sylva { * branch it is naming the moment the composition reflows. * * The proxy is the whole occlusion answer, and it is the piece - * `docs/dom-in-3d.md` calls unproven: a plane at the card's pose with + * `packages/choreo-gallery/realm/docs/dom-in-3d.md` calls unproven: a plane at the card's pose with * `NoBlending`, which writes its own alpha straight into the framebuffer and * so cuts a card-shaped hole for the DOM underneath. Being ordinary * geometry, it is depth-tested like everything else — so moss standing diff --git a/packages/choreo-gallery/.prettierignore b/packages/choreo-gallery/.prettierignore index 17c4b0670ef..362735eafbf 100644 --- a/packages/choreo-gallery/.prettierignore +++ b/packages/choreo-gallery/.prettierignore @@ -1,9 +1,8 @@ node_modules/ tmp/ out/ -# the film-graph sketches: proposals in a syntax that does not exist yet -docs/film-graph/*.gts -docs/film-graph/*.mjs +# the film-graph sketches, kept as written: proposals in a syntax that does not exist yet +realm/design/film-graph/*.gts.md # the film app's build (choreo-film-app), and the films' vendored and generated media realm/film-app/ realm/asset/ diff --git a/packages/choreo-gallery/.template-lintrc.cjs b/packages/choreo-gallery/.template-lintrc.cjs index eb34d497271..70c1a07e87e 100644 --- a/packages/choreo-gallery/.template-lintrc.cjs +++ b/packages/choreo-gallery/.template-lintrc.cjs @@ -3,9 +3,6 @@ module.exports = { extends: ['recommended', '@cardstack/template-lint:recommended'], plugins: ['../template-lint/plugin'], - // the film-graph sketches are proposals written in a syntax that does not - // exist yet: read them, don't lint them - ignore: ['docs/film-graph/**'], rules: { // Only a
element's is interactive, so buttons in its // open panel are not nested controls. diff --git a/packages/choreo-gallery/README.md b/packages/choreo-gallery/README.md index 32b96c88983..745cab9eb6b 100644 --- a/packages/choreo-gallery/README.md +++ b/packages/choreo-gallery/README.md @@ -5,30 +5,32 @@ Boxel realm content. Everything in [`realm/`](realm/) is the realm: cards, their data, and the modules they import. Cards import `glimmer-motion` and `@cardstack/choreo` like any other card; the host serves both to realms. -The rest of the package is tooling and source material that is not served: -`tools/` (the realm check and the film and widget tools), `scripts/`, -`docs/` and `notes/`. The agent skills for working on the libraries and the +The rest of the package is tooling that is not served: `tools/` (the realm +check and the film and widget tools) and `scripts/`. The agent skills for working on the libraries and the gallery are in the monorepo's `.claude/skills/` (`motion-pattern` routes to the rest). ## The realm -| Path | What it is | -| ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| `realm.json` | The realm's config. | -| `index.json` | The gallery: a `ChoreoGallery` card linking every demo, in the order the gallery shows them. | -| `gallery.gts` | `ChoreoGallery`. Its isolated format is the whole site; embedded and fitted are a summary. | -| `demo.gts` | `GalleryDemo`, one gallery entry: its title, group, lede, API pills, usage example, walkthrough and teaching lesson. | -| `demos/.json` | One `GalleryDemo` per demo. The catalog is this data. | -| `shell/` | The site's components: the frame, the grid, the demo page, the How panel and the pickers. | -| `stages/.gts` | A demo's own `GalleryDemo` subclass, supplying its live stage and its notes. | -| `notes/` | Each demo's long-form notes (its Deep Dive). | -| `asset/` | Media the demos load: the films' narration, looks, textures, picture pages, vendored three.js and posters. | -| `film-app/` | The films' own app, built from `packages/choreo-film-app` by `pnpm push`. Not committed. | -| `reel/` | The feature reel: three demos, live, in one camera'd world under a title, brand and timecode plane. `feature-reel.json`. | -| `lib/` | The crossing between the grid and a demo page, tempo and theme preferences, syntax highlighting, and `onstage`/`restWhenOff`. | -| `theme.json` | The fonts the gallery is set in. The palette is declared by the gallery's root element, `shell/choreo-root.gts`. | -| `*.test.gts` | The gallery's tests, run by `boxel test`. | +| Path | What it is | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| `realm.json` | The realm's config. | +| `index.json` | The gallery: a `ChoreoGallery` card linking every demo, in the order the gallery shows them. | +| `gallery.gts` | `ChoreoGallery`. Its isolated format is the whole site; embedded and fitted are a summary. | +| `demo.gts` | `GalleryDemo`, one gallery entry: its title, group, lede, API pills, usage example, walkthrough and teaching lesson. | +| `demos/.json` | One `GalleryDemo` per demo. The catalog is this data. | +| `shell/` | The site's components: the frame, the grid, the demo page, the How panel and the pickers. | +| `stages/.gts` | A demo's own `GalleryDemo` subclass, supplying its live stage and its notes. | +| `notes/` | Each demo's long-form notes (its Deep Dive). | +| `docs/` | The long-form documentation, as Markdown: the guide, the choreography model, the construct reference, Film. `docs/README.md` indexes it. | +| `design/` | Design history: design records, handoffs, measurements, and the films' scripts. Drafts by definition. | +| `licenses/` | Attribution for third-party material the demos use. | +| `asset/` | Media the demos load: the films' narration, looks, textures, picture pages, vendored three.js and posters. | +| `film-app/` | The films' own app, built from `packages/choreo-film-app` by `pnpm push`. Not committed. | +| `reel/` | The feature reel: three demos, live, in one camera'd world under a title, brand and timecode plane. `feature-reel.json`. | +| `lib/` | The crossing between the grid and a demo page, tempo and theme preferences, syntax highlighting, and `onstage`/`restWhenOff`. | +| `theme.json` | The fonts the gallery is set in. The palette is declared by the gallery's root element, `shell/choreo-root.gts`. | +| `*.test.gts` | The gallery's tests, run by `boxel test`. | A demo's stage and its long-form notes are components, not data. A demo that has them adopts from its own subclass of `GalleryDemo`, in @@ -105,13 +107,19 @@ pnpm test # boxel test realm `pnpm lint:realm` (`tools/check-realm.mjs`) checks the realm's contents against the libraries it documents: -- `docs/api-inventory.json` names every public export of glimmer-motion, +- `tools/api-inventory.json` names every public export of glimmer-motion, Choreo and choreo-player, and every member of the `ChoreoContext` and `FilmVocabulary` vocabularies, and maps each to a guide; - every `demos/*.json` is well formed, `index.json` links exactly that set, and each demo has a complete teaching lesson naming a known guide; - every guide meets the prose floor, has a preamble, and links only to guides - that exist. + that exist; +- `docs/` and `design/` link only to realm files and headings that exist; +- every import written in the docs, the guides and the package READMEs names + an export its package has, so none still reaches Choreo or Film through + `glimmer-motion`; +- every `choreo.boxel.site` URL names a file in the realm, and every link into + the monorepo names a file that exists. The guides themselves, and the guide pages that embed the demos, are still `choreo-test-app` sources; the check reads them from there. diff --git a/packages/choreo-gallery/docs/choreo-player.md b/packages/choreo-gallery/docs/choreo-player.md deleted file mode 100644 index 7a88aeafa0f..00000000000 --- a/packages/choreo-gallery/docs/choreo-player.md +++ /dev/null @@ -1,151 +0,0 @@ -# Shipping `choreo-player` 1.0 - -`choreo-player` is the small boundary between a public Choreo run and an -external clock. It lets preview tools and deterministic renderers control -Choreo without changing `glimmer-motion` or Choreo core. - -## Package boundary - -The npm package has two exports: - -- `choreo-player` — dependency-free play, pause, absolute render, rate, and - replacement-run synchronization; -- `choreo-player/hyperframes` — a dependency-free binding to HyperFrames' - native `hf-seek`/`waitUntil()` browser protocol. - -It does not ship UI, demo state reducers, DOM discovery, generated media, a -GSAP facade, or HyperFrames itself. - -The player accepts a structural run instead of importing the -`glimmer-motion` runtime: - -```ts -interface ChoreoRun { - readonly duration: number; - time: number; - speed: number; - play(): void; - pause(): void; -} -``` - -That is the complete surface it uses. A public `ChoreoRun` yielded by -`` satisfies the interface without an adapter. - -## Explicit ownership - -Every player requires a `runs()` provider: - -```ts -const player = createChoreoPlayer({ - duration: 7, - prepare: () => { - recording = true; - mountScoredPass(); - }, - runs: () => (recording && scoreRun === currentRun ? [scoreRun] : []), -}); -``` - -This is intentionally not a global registry. A recorder must not touch an -interactive run merely because both exist on the same page. The external -clock arms ownership through `prepare()` on its first call and retains it for -the page's recording session. - -Read [demo-recording.md](demo-recording.md) before making a demo recordable. -Its interactive-test requirement is part of the release contract. - -## A frame is an application transaction - -`player.renderAt(t)` controls Choreo runs. The application still owns the -tracked state that decides what Glimmer renders. A complete recording method -therefore reconstructs both: - -```ts -async function renderAt(time: number) { - await player.renderAt(time, { settle: false }); - - state = foldEventsThrough(time); - await glimmerRenderFinished(); - - await player.renderAt(time, { settle: false }); - paintImperativeReadouts(time); -} -``` - -The first seek makes a newly mounted score and its compiled cues available. -The state fold makes the application a function of time. The awaited Glimmer -pass commits that state, and the final seek reasserts the Choreo still on the -DOM that will be captured. - -## HyperFrames is optional - -Applications that only need an external transport install `choreo-player` -alone: - -```bash -pnpm add @cardstack/choreo-player -``` - -A video project installs its own exact HyperFrames CLI as a development tool: - -```bash -pnpm add --save-dev hyperframes@0.8.14 -``` - -The local scripts then use the pinned binary: - -```json -{ - "scripts": { - "preview": "hyperframes preview build", - "check": "hyperframes check build", - "render": "hyperframes render build --workers 10" - } -} -``` - -Do not install HyperFrames globally, bundle it into `choreo-player`, declare it -as a peer dependency, or use an unpinned `npx --yes hyperframes` in a durable -composition. - -Connect the complete application transaction to capture: - -```ts -import { bindHyperframes } from '@cardstack/choreo-player/hyperframes'; - -const disconnect = bindHyperframes({ renderAt }); -``` - -Parallel safety belongs to this `renderAt()` implementation. Ten workers are -a verified result for the Playhead fixture, not a package-wide guarantee. - -## Reference proof - -`videos/playhead-recording` is the installation and render fixture. It: - -- builds the real Glimmer gallery application; -- copies the published-shape `choreo-player/hyperframes` module into the - composition; -- mounts the real Playhead Glimmer component with no added border; -- binds native `hf-seek` to its awaited frame transaction; -- pins HyperFrames 0.8.14 locally; and -- renders 1920×1080, 30fps, with ten workers. - -The proof was compared with a same-build one-worker render. FFmpeg measured -54.15 dB average PSNR and 0.999583 SSIM, and found no post-opening frame that -matched the opening frame at any worker boundary. - -## 1.0 release gate - -Before publishing: - -1. Build and run the package contract tests. -2. Run `pnpm pack` and inspect the tarball contents. -3. Install the tarball in the reference fixture. -4. Run the complete interactive Ember suite. -5. Run HyperFrames check and render with one and ten workers. -6. Scan every worker boundary for an opening-frame insertion. - -The npm artifact should contain only `dist`, `README.md`, `LICENSE`, and -`package.json`. diff --git a/packages/choreo-gallery/docs/film-graph/tograph.mjs b/packages/choreo-gallery/docs/film-graph/tograph.mjs deleted file mode 100644 index 4837f056a8d..00000000000 --- a/packages/choreo-gallery/docs/film-graph/tograph.mjs +++ /dev/null @@ -1,219 +0,0 @@ -// Turn a film's BEATS table (as written in the .gts) into the proposed -// graph syntax: chapters as blocks, shots as named steps, joins as -// siblings, everything else attached. Values are carried verbatim. -import fs from 'node:fs'; - -const [, , file, name] = process.argv; -// the prefix the vocabulary is reached by: `f.` inside , `@f.` in a component that takes it as an argument -const F = process.env.PREFIX ?? 'f.'; -const src = fs.readFileSync(file, 'utf8'); - -/* ---- a tiny tokenizer for the object literal ----------------------- */ -function skipWs(s, i) { - for (;;) { - while (i < s.length && /\s/.test(s[i])) i++; - if (s.startsWith('/*', i)) { i = s.indexOf('*/', i) + 2; continue; } - if (s.startsWith('//', i)) { i = s.indexOf('\n', i); continue; } - return i; - } -} -function readValue(s, i) { - i = skipWs(s, i); - const c = s[i]; - if (c === "'" || c === '"' || c === '`') { - let j = i + 1; - while (s[j] !== c) { if (s[j] === '\\') j++; j++; } - return [s.slice(i, j + 1), j + 1]; - } - if (c === '{' || c === '[' || c === '(') { - const close = { '{': '}', '[': ']', '(': ')' }[c]; - let depth = 0, j = i; - for (; j < s.length; j++) { - const d = s[j]; - if (d === "'" || d === '"' || d === '`') { const [, k] = readValue(s, j); j = k - 1; continue; } - if (s.startsWith('/*', j)) { j = s.indexOf('*/', j) + 1; continue; } - if (d === c) depth++; - else if (d === close) { depth--; if (depth === 0) break; } - } - return [s.slice(i, j + 1), j + 1]; - } - let j = i; - while (j < s.length && !/[,}\]\n]/.test(s[j])) { - if (s[j] === '(') { const [, k] = readValue(s, j); j = k; continue; } - j++; - } - return [s.slice(i, j).trim(), j]; -} -function readObject(s) { - // s is '{ ... }' - const out = []; - let i = skipWs(s, 1); - while (i < s.length - 1) { - i = skipWs(s, i); - if (s[i] === '}') break; - const m = /^([A-Za-z_][A-Za-z0-9_]*)\s*:/.exec(s.slice(i)); - if (!m) { i++; continue; } - i += m[0].length; - const [v, j] = readValue(s, i); - out.push([m[1], v]); - i = skipWs(s, j); - if (s[i] === ',') i++; - } - return out; -} -function readArray(s) { - const out = []; - let i = skipWs(s, 1); - while (i < s.length - 1) { - i = skipWs(s, i); - if (s[i] === ']') break; - const [v, j] = readValue(s, i); - out.push(v); - i = skipWs(s, j); - if (s[i] === ',') i++; - } - return out; -} - -/* ---- TS value → Glimmer expression --------------------------------- */ -function str(v) { - const inner = v.slice(1, -1).replace(/\\'/g, "'").replace(/"/g, '\\"'); - return `"${inner}"`; -} -function expr(v) { - v = v.trim(); - if (/^['"`]/.test(v)) return str(v); - if (v[0] === '{') return `(hash ${readObject(v).map(([k, x]) => `${k}=${expr(x)}`).join(' ')})`; - if (v[0] === '[') return `(array ${readArray(v).map(expr).join(' ')})`; - const call = /^([A-Za-z_][A-Za-z0-9_.]*)\((.*)\)$/s.exec(v); - if (call) { - const args = readArray('[' + call[2] + ']'); - return `(${call[1]} ${args.map(expr).join(' ')})`; - } - if (/^-?\d/.test(v) || v === 'true' || v === 'false' || v === 'null') return v; - if (/^[A-Z][A-Z0-9_]*$/.test(v)) return v; // a constant - if (/^-\s*[A-Z]/.test(v)) return `(neg ${v.slice(1).trim()})`; - if (/[-+*/]/.test(v)) return `(calc "${v}")`; // arithmetic: honest about it - return v; -} -function attr(k, v) { return `@${k}={{${expr(v)}}}`; } - -/* ---- the grouping: which key goes where ----------------------------- */ -// the shot's own facts; the head pose is flattened onto the shot, the -// tail pose is a child, an eye-level walk is a child -const SHOT = ['id', 'ticks', 'cut', 'lead', 'lift', 'bob', 'hold', 'follow', 'to', 'dissolve', 'quality']; -const TYPE = ['mode', 'kicker', 'kanji', 'romaji', 'gloss', 'says', 'bare']; -// ADJUSTMENTS: parameter sets the PICTURE declares, yielded as contextual -// components under f.picture — the package knows none of these names. -// Look is a FILTER (it processes the frame); the rest hold values. -const FILTERS = [ - [F + 'picture.Look', ['grade', 'look', 'lut']], - [F + 'picture.Weather', ['theme', 'hours', 'hoursOver', 'haze', 'rain', 'wx', 'wxCut', 'lightning']], - [F + 'picture.Winter', ['winter']], - [F + 'picture.Sun', ['sun']], - [F + 'picture.Light', ['rim']], - [F + 'picture.Set', ['city', 'grass']], - [F + 'picture.Build', ['build', 'buildBy', 'settle', 'style']], -]; -const RENAME = { kanji: 'word', romaji: 'reading', build: 'clock', buildBy: 'by', style: 'subject', hoursOver: 'over' }; -function pose(tag, v) { - return `${tag} ${readObject(v).map(([k, x]) => attr(k, x)).join(' ')}`; -} - -/* ---- walk the table ------------------------------------------------- */ -const start = src.indexOf('const BEATS: Beat[] = ['); -const body = readValue(src, start + 'const BEATS: Beat[] = '.length)[0]; -// beats plus the banner comments between them -const items = []; -{ - let i = 1; - while (i < body.length - 1) { - const ws = skipWs(body, i); - const gap = body.slice(i, ws); - const banner = /\/\*\s*-+\s*\*\n\s*\*\s*(.+?)\n/.exec(gap); - if (banner) items.push({ banner: banner[1].trim() }); - i = ws; - if (body[i] === ']' || i >= body.length - 1) break; - const [v, j] = readValue(body, i); - items.push({ beat: readObject(v) }); - i = skipWs(body, j); - if (body[i] === ',') i++; - } -} - -const chapters = JSON.parse(process.env.CHAPTERS); -const out = []; -const P = (s) => out.push(s); -let openCh = -1; -const ind = (n) => ' '.repeat(n); -let prevId = null; - -for (const it of items) { - if (it.banner) { P(''); P(`${ind(3)}{{! ${it.banner} }}`); continue; } - const b = Object.fromEntries(it.beat); - const ch = Number(b.ch); - if (ch !== openCh) { - if (openCh >= 0) P(`${ind(3)}`); - const c = chapters[ch]; - const tints = (c.tint ? ` @tint="${c.tint}"` : '') + (c.tintD ? ` @tintD="${c.tintD}"` : ''); - P(`${ind(3)}<${F}Chapter @n="${c.n}" @title="${c.title}"${c.grade ? ` @grade="${c.grade}"` : ''}${c.lut ? ` @lut="${c.lut}"` : ''}${tints}>`); - openCh = ch; - } - const id = b.id.slice(1, -1); - // the seam INTO this shot, as a sibling before it - if (b.join) { - const j = [`@presentation=${str(b.join)}`]; - if (b.dipTo) j.push(`@to=${str(b.dipTo)}`); - P(`${ind(4)}<${F}Join ${j.join(' ')} />`); - } - const shotArgs = SHOT.filter((k) => k in b).map((k) => (k === 'id' ? `@name="${id}"` : attr(k, b[k]))); - const head = b.cam ? readObject(b.cam).map(([k, x]) => attr(k, x)) : []; - P(`${ind(4)}<${F}Shot ${[...shotArgs, ...head].join(' ')}>`); - if (b.toCam) P(`${ind(5)}<${pose(F + 'To', b.toCam)} />`); - if (b.eye) { const e = Object.fromEntries(readObject(b.eye)); P(`${ind(5)}<${F}Eye @from={{${expr(e.at)}}}${e.to ? ` @to={{${expr(e.to)}}}` : ''} @fov={{${e.fov}}} />`); } - const type = TYPE.filter((k) => k in b); - if (type.length) P(`${ind(5)}<${F}Type ${type.map((k) => attr(RENAME[k] ?? k, b[k])).join(' ')} />`); - if (b.vo) P(`${ind(5)}<${F}Voice @line=${str(b.vo)} @read={{get VO_SECS "${id}"}}${b.hush ? ' @hush={{true}}' : ''} />`); - else if (b.hush) P(`${ind(5)}<${F}Voice @hush={{true}} />`); - for (const [tag, keys] of FILTERS) { - const have = keys.filter((k) => k in b); - if (!have.length) continue; - // an object-valued knob (sun: {az, el}; winter: {gust, pack}) is its own - // filter with the object's keys as flat, typed arguments; null clears it - if (have.length === 1 && (b[have[0]].startsWith('{') || b[have[0]] === 'null') && ['sun', 'winter'].includes(have[0])) { - const v = b[have[0]]; - P(`${ind(5)}<${tag} ${v === 'null' ? '@off={{true}}' : readObject(v).map(([k, x]) => attr(k, x)).join(' ')} />`); - continue; - } - P(`${ind(5)}<${tag} ${have.map((k) => attr(RENAME[k] ?? k, b[k])).join(' ')} />`); - } - if (b.mix) P(`${ind(5)}<${F}sound.Mix ${readObject(b.mix).map(([k, v]) => attr(k, v)).join(' ')} />`); - if (b.stamp) { const s = Object.fromEntries(readObject(b.stamp)); P(`${ind(5)}<${F}Stamp @year={{${s.y}}}${s.at ? ` @at={{${s.at}}}` : ''} />`); } - if (b.sky) P(`${ind(5)}<${F}Sky ${readObject(b.sky).map(([k, v]) => attr(k, v)).join(' ')} />`); - if (b.mark) P(`${ind(5)}<${F}Mark ${readObject(b.mark).map(([k, v]) => attr(k, v)).join(' ')} />`); - if (b.trace) for (const t of readArray(b.trace)) P(`${ind(5)}<${F}Trace ${readObject(t).map(([k, v]) => attr(k, v)).join(' ')} />`); - if (b.cycle) P(`${ind(5)}<${F}Lineup @steps={{${expr(b.cycle)}}} />`); - if (b.photo) { - const p = Object.fromEntries(readObject(b.photo)); - P(`${ind(5)}<${F}Attach @lane={{1}}>`); - P(`${ind(6)}<${F}Insert @src=${str(p.src)} @caption=${str(p.caption)} @credit=${str(p.credit)} />`); - P(`${ind(5)}`); - } - if (b.clip) { - const c = Object.fromEntries(readObject(b.clip)); - const win = []; - if (c.at) win.push(`@at={{${c.at}}}`); - if (c.for) win.push(`@for={{${c.for}}}`); - if (c.end) win.push(`@end=${str(c.end)}`); - const kind = c.kind ? c.kind.slice(1, -1) : 'video'; - const comp = { freeze: 'Freeze', image: 'Still', video: 'Video' }[kind]; - const rest = Object.entries(c).filter(([k]) => !['at', 'for', 'end', 'kind'].includes(k)).map(([k, v]) => attr(k, v)); - P(`${ind(5)}<${F}Attach @lane={{2}} ${win.join(' ')}>`); - P(`${ind(6)}<${F}${comp} ${rest.join(' ')} />`); - P(`${ind(5)}`); - } - P(`${ind(4)}`); - prevId = id; -} -if (openCh >= 0) P(`${ind(3)}`); -fs.writeFileSync(`/dev/stdout`, out.join('\n') + '\n'); diff --git a/packages/choreo-gallery/docs/gallery/BOXEL-DEPLOYMENT.md b/packages/choreo-gallery/docs/gallery/BOXEL-DEPLOYMENT.md deleted file mode 100644 index a34661eda61..00000000000 --- a/packages/choreo-gallery/docs/gallery/BOXEL-DEPLOYMENT.md +++ /dev/null @@ -1,80 +0,0 @@ -# Deploy the gallery to Boxel Site - -This workflow uses your own Boxel account and source realm. No account, realm ID, -published demo URL, or credential is embedded in the repository. Obtain the -published site URL assigned to your realm through Boxel's publishing UI; do not -construct a URL from an account name or assume a particular hostname. - -## Build a standalone release - -From the repository root: - -```sh -pnpm install --frozen-lockfile -pnpm build -APP_BASE=./ APP_LOCATION=hash pnpm --dir test-app exec vite build --outDir ../out/widget-boxel-build -node scripts/package-widget-boxel.mjs -``` - -Output is `out/widget-boxel-publish/iframe/gallery/`. `WIDGET_BUILD` selects -another build directory; `WIDGET_PACKAGE` selects another package directory. -The package includes posters, narration, fonts, models, and demo dependencies. -Use a fresh output directory per release to avoid uploading stale chunks. - -The packager preserves raw browser modules as `.mjs` because Boxel processes -`.js` realm modules. It also rewrites Draco loader filenames and embeds a -same-origin document bridge for Sagrada and Towers. Hash routing keeps nested -demos inside the published directory. These steps are required, not optional -minification or performance tweaks. - -## Authenticate, inspect, and upload - -Install the `@cardstack/boxel-cli` version supported by your Boxel deployment, -then authenticate with `boxel profile add` (browser sign-in). Switch to the -intended profile with `boxel profile switch` when using multiple environments. -Credentials remain in the CLI's local profile storage. - -Set `BOXEL_SOURCE_REALM` to your source realm URL using your shell or local -secret configuration; do not commit its value. Inspect the files first: - -```sh -node scripts/publish-widget-boxel.mjs --dry-run -node scripts/publish-widget-boxel.mjs -``` - -`BOXEL_CLI` optionally selects the CLI executable. `WIDGET_UPLOAD_ROOT` selects -the local upload tree (default `out/widget-boxel-publish`). Paths under this tree -become realm-relative paths. The uploader writes only those files, uploads HTML -last, and does not delete other content. A repeated upload replaces files at the -same paths; use a versioned release subdirectory when you need rollback. - -If delivering a rendered MP4, place it under the upload tree's `media/` directory -before upload. Keep the source render outside Git. - -## Publish and verify - -Set `BOXEL_PUBLISHED_REALM` to the published site URL assigned by Boxel, then: - -```sh -boxel realm indexing-errors --realm "$BOXEL_SOURCE_REALM" -boxel realm publish "$BOXEL_SOURCE_REALM" "$BOXEL_PUBLISHED_REALM" -``` - -Use the CLI's readiness/publishability checks. Do not bypass them with `--force`. -For CLI versions without `realm publish`, use Boxel's publishing UI instead. -Publishing a realm publishes its contents; choose a realm intended for sharing. - -The gallery's path relative to the published root is: - -```text -iframe/gallery/widget-room.html?acceptHeader=application%2Fvnd.card%2Bsource&film=1#/_widgets -``` - -A video path uses `?acceptHeader=video%2Fmp4`. The accept-header query ensures -that a browser receives the HTML or video rather than the card viewer. - -Check the gallery anonymously on desktop and phone, confirm all posters load, -play consecutive full-tour clips, inspect Mockup in 3D, and run the freeze and -handoff checks against the published gallery URL. For MP4 delivery, verify -metadata, playback, seeking, and HTTP byte-range responses. The HTML has -`noindex,nofollow,noarchive`; an unlisted URL is still public to anyone who has it. diff --git a/packages/choreo-gallery/docs/source-repo/AGENTS.md b/packages/choreo-gallery/docs/source-repo/AGENTS.md deleted file mode 100644 index 58c19d34070..00000000000 --- a/packages/choreo-gallery/docs/source-repo/AGENTS.md +++ /dev/null @@ -1,90 +0,0 @@ -# Agent guide — Choreo / glimmer-motion - -One package, two layers. **`glimmer-motion`** binds the Motion (motion.dev) -engine — `motion-dom`, untouched — to Glimmer: the `{{motion}}` modifier, -``, ``, ``, reorder, drag, gestures, -scroll, view transitions. **Choreo** sits on top: `` watches a render -pass, hands its **changeset** (inserted/removed/kept, bounds before and -after) to a timeline you declare in the template. The repo is `choreo`; the -npm package is still `glimmer-motion` — every import in docs and code is the -real one. - -## Layout - -- `packages/glimmer-motion/` — the addon. `src/index.ts` is the public - surface; `src/choreo/` is Choreo's model; ~40 lines of Ember-specific glue, - the rest is host-agnostic. -- `test-app/` — the gallery (every demo in - `app/components/examples/*.gts` is real, catalogued in `app/lib/catalog.ts`) - and the test suite: ported upstream Motion tests (fidelity) + Choreo's - contract suite. -- `docs/guide.md` (tutorial), `README.md` (reference), - `docs/choreography.md` + `docs/nested-choreo.md` (Choreo design). - -## Building from the Gallery - -For recreating demos, combining patterns into applications or films, or adding -live DialKit controls, use `.claude/skills/choreo-create/SKILL.md`. Codex also -discovers the same skill through `.agents/skills/choreo-create`. The documentation -website at `/docs` teaches core glimmer-motion, interactive Choreo, spatial/3D -Choreo, and recorded/film Choreo as four learning paths. - -## Picking an animation pattern - -Before writing any animation, invoke the **`motion-pattern`** skill — it -routes to the right pattern skill. Short form: - -| Need | Use | Skill | -| ------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------- | -| One element animates | `{{motion}}` initial/animate/gestures | `motion-element` | -| Animate on removal/insert | `` + `exit` | `motion-presence` | -| Moved because layout changed; same thing in two places | `layout` / `layoutId` / `` | `motion-layout` | -| Ordered multi-element scenes, z-index windows, fly-to-a-place | `` timeline + `{{beacon}}` | `choreo-scene` | -| Multiple regions; cross-region flights | nested `` + far matching | `choreo-regions` | -| Route/page change | `` for live DOM; snapshot APIs when intentional | `magic-move-navigation` / `motion-page-transition` | -| The gallery ⇄ demo Magic Move recipe | `` pairing | `magic-move-navigation` | -| Pointer-driven movement | `drag` / `` | `motion-drag` | -| Scroll-driven / in-view | `scrollProgress` / `InView` | `motion-scroll` | -| Tests touching motion | `glimmer-motion/test-support` | `motion-testing` | - -Escalate element → presence → layout → Choreo; use the smallest pattern that -states the intent. Two standing vetoes: no view transitions over live -content (snapshots freeze it — use layout animation), and no `setTimeout` -sequencing (that is what `c.Sequence` is for). - -## Three rules that silently break motion in Glimmer - -1. CSS for a motion element goes through the modifier - (`{{motion style=…}}`), never a bound `style=` attribute — Glimmer - rewrites the attribute and wipes the engine's transform. -2. Anything the engine must tween/correct/measure must be declared to it — - a stylesheet `border-radius` on a `layout=true` element distorts; - `style=(styles borderRadius='14px')` doesn't. -3. Exit content rides on what `` yields; a leaving block re-runs - from live state, so don't read moved-on state during an exit. - -## Commands - -```bash -pnpm install -pnpm build # builds the addon (required before lint — glint resolves built declarations) -pnpm lint # eslint + glint + prettier -pnpm test # builds, then runs the suite in Chrome -``` - -Dev server for the gallery: `.claude/launch.json` → `glimmer-motion-tests` -(vite on 4202; `/tests` for the QUnit runner). CI runs `pnpm build` before -`pnpm lint` — keep that order. - -## Conventions - -- `.gts` templates; named exports, no `Component` suffix; Glint signatures - on every public component/modifier. -- Transition constants at module scope, not inline hashes; helpers - (`to`, `spring`, `tween`, `styles`, …) over `(hash …)`. -- `data-test-*` for test hooks; never sleep in tests - (`animationsSettled()`). -- `prefers-reduced-motion` is honoured by default (`@reducedMotion="user"`). - Don't opt out to make a demo look right. -- Dark theme is the reference look; light mode rebinds tokens - (`:root[data-theme='light']`) and must not change dark's resolved values. diff --git a/packages/choreo-gallery/docs/source-repo/README.md b/packages/choreo-gallery/docs/source-repo/README.md deleted file mode 100644 index d0d578aee5b..00000000000 --- a/packages/choreo-gallery/docs/source-repo/README.md +++ /dev/null @@ -1,678 +0,0 @@ -
- -

Choreo

- -**Motion, Choreo-graphed.** -The [Motion](https://motion.dev) engine for Ember — and a timeline for the scene. `` watches a render pass, measures its **changeset** — what was inserted, removed, kept, and where everything stood before and after — and plays a score you declare over it. Magic-Move crossings, gates you click through, wires drawn every frame, values derived from other elements' measurements — all interruptible mid-flight, by design. - -**Live gallery:** [**cardstack.github.io/choreo**](https://cardstack.github.io/choreo/)   ·   46 demos with source and guides - -
- -```gts - - {{#each this.cards key='id' as |card|}} -
{{card.title}}
- {{/each}} - - - - - - -
-``` - -
- -**Declared like a template**   ·   measured like layout   ·   sequenced like a score   ·   scrubbed like a video - -**Interrupted like Keynote**   ·   click again mid-flight and everything re-aims from where it is painted - -
- -

- Mockup: a live DOM interface projected inside a 3D device -

- -**Featured demo: Mockup.** The screen is a real Glimmer interface, projected inside -a 3D device. Its controls remain interactive while Choreo coordinates the screen's -layout transitions and the camera. Switch between 2D and 3D to compare the same -application in both spaces. [Source](test-app/app/components/examples/mockup.gts) · -[How DOM projection works](docs/dom-in-3d.md). - ---- - -## What is Choreo? - -Choreo is two layers in one repo, published as two packages. - -**The choreography layer** is the reason the repo exists: a region-scoped model that Motion's per-element API has no equivalent for. A `` region treats each render pass as one event. It snapshots its participants before the DOM changes, measures them after, and hands the difference — the **changeset** — to a timeline declared right in the template. One score describes the whole scene: what leaves, what flies, what arrives, in what order, on which clock. - -**The binding underneath** is **`glimmer-motion`**: `motion-dom` — Motion's framework-free engine, untouched — bound to Glimmer as a modifier and a handful of components. Layout animation, shared-element transitions, presence, variants, drag, reorder, scroll. It is a complete, usable port in its own right (held to Motion's own test suites, pixel for pixel), and it is what the choreography stands on. Both ship in the one `glimmer-motion` package. - -The second package, **`choreo-player`**, is a dependency-free headless transport for clocking Choreo runs from outside — a video renderer, a capture worker, a scrub UI ([packages/choreo-player](packages/choreo-player)). - -> Naming: Motion (motion.dev) is the library formerly called framer-motion; its React package is still published as `framer-motion`, which is why upstream paths in this repo read `packages/framer-motion/…`. The repo is **Choreo**; the published package is still **`glimmer-motion`** — one npm name, unchanged, and every import in these docs is the real one. - -**New here?** [docs/guide.md](docs/guide.md) teaches the binding from a Glimmer card rather than a React translation table. Then: [choreography.md](docs/choreography.md) (the model and its boxel-motion ancestry), [choreo-constructs.md](docs/choreo-constructs.md) (the full construct reference), [step-vocabulary.md](docs/step-vocabulary.md) (the open vocabulary: anchors, composite steps, derived values), [nested-choreo.md](docs/nested-choreo.md) (regions and far matching), [choreo-splices.md](docs/choreo-splices.md) (cuts as a first-class citizen of a score), [film.md](docs/film.md) (the `` construct, as built), [planes-and-cameras.md](docs/planes-and-cameras.md) and [dom-in-3d.md](docs/dom-in-3d.md) (a `` region as a plane in a three.js scene), [demo-recording.md](docs/demo-recording.md) (external clocks), [choreo-player.md](docs/choreo-player.md) (the headless transport), and [postmortem-follow.md](docs/postmortem-follow.md) — the engineering post-mortem that shaped the derived-value API, kept because the mistakes teach more than the result. - -`docs/` is the documentation. The workshop behind it — design records, handoffs, measurements, the films' scripts and wall text — is in [notes/](notes), which is drafts by definition: some of it describes things proposed and never built, or built and since changed. - -- [Why choreography?](#why-choreography) -- [Install](#install) -- [At a glance: the design decisions](#at-a-glance-the-design-decisions) -- [A tour of the vocabulary](#a-tour-of-the-vocabulary) -- [Where it earns its keep](#where-it-earns-its-keep) -- [Interruption, tested](#interruption-tested) -- [External clocks: choreo-player](#external-clocks-choreo-player) -- [DOM in 3D: c.Camera3D](#dom-in-3d-ccamera3d) -- [The binding underneath](#the-binding-underneath) · [Why the engine is untouched](#why-the-engine-is-untouched) · [How it was made](#how-it-was-made) · [Fidelity](#fidelity) -- [API](#api) · [React → Glimmer](#react--glimmer) · [Three rules React does not need](#three-rules-react-does-not-need) -- [Guides and tuning](#guides-and-tuning) · [3D gallery](#3d-gallery) -- [Testing](#testing) · [Architecture](#architecture) · [Development](#development) · [Roadmap](#roadmap) · [Credits](#credits) - -## Why choreography? - -Element-level animation answers "how does this element move?" — and for one element it is the right question. But most of what a product ships is _scenes_: a card opens into a page, a row grows into a detail panel, a slide builds in steps, a dropped card springs into its slot while its neighbours make room. In a scene, elements don't have independent motion — they have **relationships**: this one leaves _before_ that one flies; this badge rides _that_ card; this panel grows out of _that_ row; everything freezes _while_ the hero crosses. - -Per-element APIs make you reconstruct those relationships by hand: measure rects imperatively, spawn clone overlays, chain `setTimeout`s, hide the real element while a copy flies, and hope nobody clicks mid-flight. Every one of those hand-built mechanisms was found, catalogued, and replaced during the port of a real product workspace onto this library — the clones, the `afterRender` measurement hooks, the hand-matched timer pairs, all of it collapsed into declared timelines ([christse/bento-boxel#1](https://github.com/christse/bento-boxel/pull/1) is the receipts). - -The changeset is what makes the declarative form possible. Because the region measures **everything, both sides of every pass**, the timeline never needs a hand-measured rect: a step names _which sprites_ (by id, role, or what happened to them — `inserted`, `removed`, `moved`, `claimed`) and _what to do_, and the geometry is already there. And because every value is a function of the run's clock over measured endpoints, a second click mid-flight simply re-measures and re-aims — interruption is not an edge case to be defended against, it is the normal case the whole model is built around. - -## Install - -The verified setup for this checkout packs the local packages into a generated -app. Start with the -[complete first-app tutorial](test-app/app/content/guides/core-first-app.md): it -generates a clean Ember/Vite consumer and walks through installation, a working -scene, and verification. Current package manifests are version `0.0.0`; this -workflow does not depend on an npm release. [mise](https://mise.jdx.dev/) -provides the pinned Node and pnpm versions, and it ignores a configuration file -until it is trusted: - -```sh -mise trust -mise install -pnpm install -node scripts/create-tutorial-app.mjs /tmp/my-choreo-app -cd /tmp/my-choreo-app -mise trust -pnpm install -pnpm build -pnpm start -``` - -The generator builds `glimmer-motion` and `choreo-player` and packs them into the -app's `vendor/` directory, alongside the declared Motion peers. The app does not -depend on the repository after it is generated; generate a new one to pick up -library changes. The addon is a v2 addon with Glint signatures and TypeScript -declarations; `choreo-player` remains a separate package. - -## At a glance: the design decisions - -Twelve decisions that make Choreo feel the way it does. Each one is enforced by tests, and most were paid for the hard way. - -- **The scene is the unit.** A region owns a subtree; participants opt in with `{{motion id= role=}}`; each render pass is one changeset, one timeline, one run. Two regions are two scenes with separate clocks — and nesting is legal, because an inner region measures its own pass correctly even while an outer one is flying the box it lives in. -- **Leavers belong to the timeline.** A removed participant stays on screen, locked where it stood, exactly as long as the score names it — the region parks it in an orphan layer and releases it when its row ends. No `` inside a scene, no clones, no "keep this in state a bit longer" flags. -- **Identity pairs across renders.** An inserted id that replaces a removed one _claims_ it: one flight, two skins, crossfaded over the moving box. The same bare id in two different regions pairs across them — **far matching** — and the arrival flies from the departure's box as one continuous move. Pairing is also what makes "back" trail-correct for free: a closing page flies to wherever its identity truly renders next. -- **z-index is a window; elevation is a layer.** `c.Hold` sets any property for the span of its block and hands it back — the whole "timed z-index flag" genre, retired. `c.Raise` is for what z-index cannot say: promotion to a real layer above every stacking context and overflow clip in the region, for the window, with the slot held by a placeholder. -- **A gate is a pause; back is a hold.** `c.Gate` parks the run until `advance()` (Keynote's click), `@delay` opens it by itself, and `advance()` mid-segment is the click-through: complete instantly, park at the gate. `run.retreat()` is the Keynote back rule as a verb: land parked at the previous gate, everything ahead re-closed, and **hold** — nothing plays, no `@delay` gate self-opens, until the user advances and the build replays forward. -- **Interruption is the doctrine.** A sprite mid-flight reads as _moved_ on every re-pass — its `initial` is the painted box, by design — so a replacement run starts from what is on screen, with velocities handed over. And a replacement pass may not drop a flight: anything the prior run was driving that the new score doesn't name gets a synthesized continuation to its rest. (Without that rule, any unrelated re-render could snap a flying element home in one frame; the test that caught it watches every frame.) -- **Unnamed means no animation.** A reflow the score doesn't name lands instantly — apps rely on it. The continuity rule above is scoped to what was _flying_; layout stays layout. -- **A beacon is a place, not an identity.** `{{beacon 'trash'}}` publishes a box; `@from`/`@to` borrow it as one end of a flight. The bin never joins a changeset, never deforms, and the pass measures it fresh each time — including _after_ the layout it sits in has re-solved, which kills the "predict where the slot will be" computation entirely. -- **Derived values never read the page.** `c.Follow` computes one sprite's properties from another's _measurements_: each source arrives as `{ from, to, now }` — resting boxes from the pass, plus a current position composed from the values the run is already driving — and the follower's own box arrives as `rest`, which cannot contain what the follower writes. Pure by construction, so a follower cannot feed its own output back or force a style recalculation mid-move. The API looks this way because the obvious API (live boxes) shipped first and failed five ways; [the post-mortem](docs/postmortem-follow.md) is part of the documentation. -- **Some ink stands.** An open-ended `c.Tether` draws its wire every frame and every still, indefinitely — a standing run that reports settled while it ticks. Tether, raise, and scroll cues compare by identity across re-renders, so a busy page cannot tear a standing wire down and refade it sixty times a second. -- **The vocabulary is open.** Blocks answer to the anchor system (`@name` a block, `@at={{at 'carry'}}` a step against it), and a composite step is a published seam: subclass `StepComponent`, return a node tree, and your app speaks its own words — ``, ``, `` — with `generic: true` children that yield to anything more specific in the same score. -- **Time is a value.** `run.time` is settable in both directions; setting it across a gate parks there; a still is a computed frame, not a paused accumulation. That is what makes the Playhead demo scrubbable, the presentation reversible, and the whole thing renderable from an external clock. - -## A tour of the vocabulary - -One scene, ten statements. The fixture is the same shape throughout: participants declared with `{{motion id= role=}}`, a timeline beside them. - -**1 · The changeset, sequenced.** Steps select what _happened_ — `c.inserted` / `c.removed` / `c.kept` / `c.moved` / `c.still` (optionally by role), `c.role`, `c.id`, `c.claimed` / `c.received` for the two halves of a pair, and `c.onstage(query)` to touch only what a viewport can see. - -```gts - - - - - -``` - -A sequence can follow a spring — its length is computed with the engine's own generator, not guessed. - -**2 · z-index as a window.** - -```gts - - - - -``` - -The hold spans the block and releases; `@fill={{true}}` keeps it; `@duration` gives it its own window. Any property, not just z. - -**3 · The crossing — Keynote's Magic Move as one step.** For a view or route swap where the same identities exist on both sides: - -```gts - - {{#if (crossingActive)}} - - {{/if}} - {{outlet}} - -``` - -Leaves dissolve as the paired elements lift off; arrivals land near the settle; only what a viewport can see animates; the crossfade **carries color** (alpha is turned into an actual solid mid-flight so the ground never leaks through the flying box, and handed back to the stylesheet on landing). Shape matching follows the **matching-snapshot rule**: mark the real subject with `data-choreo-substance`, or `pack="content"` on type so the shrink-wrap (the ink) is the subject even when CSS stretched the frame. The entering subject is scaled uniformly _by width_ to fill the exiting subject's width, tops pinned, any vertical overflow cropped at the bottom by a clip window that travels with the box — nothing ever stretches. `c.Crossing @size` defaults to `'crop'`; `'scale'` and `true` are there when you mean them. A sibling step naming a role the crossing would have touched takes that sprite away from it (the yield rule) — a canned move with per-element overrides, no exclusion syntax. - -**4 · Gates, forward and back.** A presentation is gates all the way down: - -```gts - - - {{! opens itself — a preamble beat }} - - {{! waits for the click }} - -``` - -`ctx.run.advance()` opens the gate (or completes the segment and parks — the click-through). `ctx.run.retreat()` steps one build back and _holds_: gates ahead re-close, `@delay` gates do not self-open, and the next advance replays the build. Text can be delivered by word or character (`@by`, `@order`, stagger) as part of any tween. - -**5 · A value derived, not tweened.** The badge rides the card; the shadow spreads by how far the card is from home: - -```ts -const pin = ({ rest, sources }: DeriveContext) => { - const card = sources[0]!.now; - return { x: card.x + card.width - (rest.x + rest.width) }; -}; -``` - -```gts - -``` - -`@read` runs every frame and every scrubbed still, sees only the pass's measurements, and is right on frames nobody planned — including the frame after a mid-flight retarget. It may write transform, opacity, and filter; never layout. - -**6 · Wires.** Geometry drawn continuously between two sprites: - -```gts - -``` - -`@path` receives both boxes in region space each frame and returns SVG path data; the region hosts the layer. No `@duration` means a standing wire — drawn for as long as the score stands, redrawn through anything that moves either end (the Wires demo drags its notes through three drafts of copy). - -**7 · A place, not an identity.** - -```gts -… - -``` - -`@from` works the same way (out of the compose button; from where a drag let go — `c.gesture` borrows the live pointer). The bin never morphs; the pass measures it after layout has settled, so a destination that a re-solve just moved is simply _read_. - -**8 · Elevation and scroll as steps.** - -```gts - - - - -``` - -The scroll animates the sprite's own scroll container and yields to the user's wheel; the flash's _timing_ lives in the timeline while its _fade_ stays in the stylesheet's transition. `c.Raise` promotes a sprite to the region's elevated layer for its window — above every stacking context and clip — and `c.Camera` drives the region's own frame (zoom to a sprite, hold others steady) on the same clock as everything else. - -**9 · Anchors and composite steps.** Name a block, aim at it, and say your app's own words once: - -```gts -class Carry extends StepComponent { - node(): TimelineNode { - const { of, name, spring = carry } = this.args; - return { - at: this.args.at, - children: [ - { generic: true, kind: 'move', of, spring }, - { generic: true, kind: 'hold', of, props: { zIndex: 3 } }, - ], - kind: 'parallel', - name, - }; - } -} -``` - -```gts - - - - -``` - -`at('carry')` / `after('carry')` place steps against the named block; the composite's `generic` children yield their sprites to anything more specific. `c.Crossing` itself is written against this same public seam — the canned step is not privileged. - -**10 · Regions and far matching.** Two ``s are two scenes; give an element the same bare id in both and a pass that removes it from one while inserting it in the other pairs them at a render barrier — measure, match, run, with no frame painted between — and the receiver flies from the sender's box in page space. Scoping the id is precisely how you turn it off. ([docs/nested-choreo.md](docs/nested-choreo.md)) - -Arming — _when_ a timeline should exist — is a first-class helper, because the same ids exist on ordinary passes too: - -```ts -import { createArming } from '@cardstack/choreo'; -const crossing = createArming(); -crossing.begin(region); // watches the run, survives mid-flight replacement, stands down on settle -crossing.active(); // tracked — render the timeline only while true -``` - -## Where it earns its keep - -These are not hypotheticals — each row is a shipped pattern, from the gallery or from the product-workspace port that drove the vocabulary: - -| The scene | The score | -| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -| Card ⇄ page navigation (gallery ⇄ demo, tile ⇄ document) | `` + `c.Crossing` — real elements, no snapshots, live content never freezes | -| Master–detail: a row grows into a panel | `c.Move @from={{c.beacon row}}` `@size='scale'` — the real panel flies, the solver's re-layout is simply measured | -| Drag-to-shelve: release point → slot | counterpart pairing — the painted box at release _is_ the start | -| A card transmutes into its schema node | beacon flight out of a closing sheet, still measurable mid-exit | -| Slide decks with builds | `c.Gate` / `@delay` / `advance()` / `retreat()` — the Keynote rules | -| Annotations wired to their anchors | standing `c.Tether`s, redrawn through every reflow and every flight | -| "Jump to it and flash it" | `c.Scroll` + `c.Hold` — one clock instead of a scroll racing a classList write racing a timer | -| A badge riding a flying card; a shadow reading lift | `c.Follow` — right on the frames a tween would have had to predict | -| Timed highlight/z states (`justDropped`, pulse flags) | `c.Hold` windows — the timer-and-flag genre, retired | -| Scrubbing, replays, video export | `run.time` + gates + `choreo-player` | -| A live UI inside a device, on camera | `c.Camera3D` — the DOM is a plane in the scene; the shot seeks like any other score | -| A leaderboard that reorders when a result lands | `c.inserted` / `c.moved` / `c.removed` over ONE tracked write — no row is told where to go | -| A simulation running beside a score | the loop owns the physics and the chase camera; the region owns the scene change. Knowing which is which is the skill | - -## Interruption, tested - -The doctrine is that a second click mid-flight is _normal input_. The mechanics that make it true: `initial` is the painted box on every re-pass; velocities hand over to the replacement run; orphans aloft keep their run through unrelated re-renders; unnamed flying sprites get continuations; a fast keep declines pure-noise passes outright (fingerprinting the _composed_ offset chain, so a moved wrapper that is itself no participant still reads as the reflow it is). - -The test method matches: watch the primary element **per frame, in absolute coordinates**, normalize steps by the sample gap (a starved rAF observer merges engine frames; a real teleport pays inside one), and bound against measured travel — because a co-moving pin can stay perfect through a jump a person cannot miss. The suite's interruption soak hammers the real gallery components with clicks at 0 / 60 / 220 ms. - -```ts -import { - setupMotion, - animationsSettled, - bounds, - shape, - orphanCount, - strandedTransforms, - live, - liveAll, - advanceGate, - seekTo, - velocityOf, -} from 'glimmer-motion/test-support'; -``` - -| export | what it is for | -| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| `setupMotion(hooks)` | resets what outlives an owner: beacon registry, far-match barrier, motion speed | -| `animationsSettled(opts?)` | resolves when every motion element, layout animation and `` run has stopped — and names what was still moving when it times out | -| `bounds(el)` / `shape(el)` | rects relative to the test root; the cumulative 2×2 transform | -| `orphanCount()` | leavers currently parked in an orphan layer | -| `strandedTransforms()` | elements wearing a transform nobody is animating | -| `live(sel)` / `liveAll(sel)` | querySelector that declines mid-flight ghosts in the orphan layer — a person never clicks one, a selector must not either | -| `advanceGate()` / `seekTo(s)` | drive a parked run; scrub to a second | -| `velocityOf(el, prop)` | how fast a value is moving right now | -| `isMotionIdle()` / `whatIsBusy()` | the probe underneath, for a custom wait | - -`animationsSettled()` is explicit rather than a blocking test-waiter on purpose: the interruption suite's whole method is to click again while something is still in flight, and a waiter would silently turn every one of those into a wait-for-completion. - -## External clocks: choreo-player - -`choreo-player` is a dependency-free, headless transport for clocking public Choreo runs from outside — a frame-by-frame video renderer, parallel capture workers, a custom scrub surface. It owns runs you hand it (never before an external clock has actually arrived — [docs/demo-recording.md](docs/demo-recording.md) is the field guide for keeping a demo correct both interactively and under capture), and its run contract is structural and five members small: - -```ts -import { createChoreoPlayer } from '@cardstack/choreo-player'; - -const player = createChoreoPlayer({ - duration: 12, - prepare: () => mountScore(), // the first external clock arms the demo - runs: () => (scoreRun ? [scoreRun] : []), // read per operation, never leaked early -}); - -await player.renderAt(4.25); // pause, set speed and absolute time, settle — frame t is a function of t -await player.play(); -``` - -Because a Choreo still is a computed frame, `renderAt(t)` is deterministic across workers on separate pages — no wall clock, no accumulation, no frame counting. - -## DOM in 3D: c.Camera3D - -A `` region can be a plane in a three.js scene — live DOM, still clickable, still animating, with WebGL glass composited over it. Two stages ship on this: **Mockup** (a phone) and **Long Take** (a laptop whose screen is running a second camera of its own). - -```gts - - - - - - -``` - -`c.Camera3D` carries an **orbit pose in the terms a shoot uses** — `dolly` (distance as a multiple of the host's own framing), `pitch` and `yaw` in degrees, `x` / `y` as truck and pedestal in fractions of the framed height, because dolly alone pushes a tall subject's head out of the picture. `@by` adds to the pose in force instead of replacing it: Pan's rule, in 3D. - -**Choreo does not own a renderer and does not pretend to.** The step is a pure function of the run's clock and hands the host a five-number pose through `@onCamera3D`; the host applies them to three.js, a CSS 3D stage, or anything else that can take a pose. So a 3D shot seeks like every other score — `run.time`, gates, `choreo-player` — with no rAF and no accumulation of its own. - -The mapping from three.js to CSS is **85 lines** (`test-app/app/lib/css3d.ts`): three pure functions over a 4×4 matrix, against 454 for three's own `CSS3DRenderer`, because Glimmer already owns the elements and the change tracking and Choreo already owns the clock. The screen is a hole punched in the canvas with `NoBlending` — and because that hole is depth-tested, the laptop's own keyboard occludes the live DOM behind it for free. - -[docs/dom-in-3d.md](docs/dom-in-3d.md) is the write-up: what the two shipped cases cost, what floating cards, anchored pop-ups and effects over DOM would add, and the two things that stay hard regardless of line count. - -## The film: `` - -Two films were cut by hand on the same engine before the construct was written — **Towers** (`/towers`) and **Sagrada Família** (`/sagrada`) — and 69% of the second was the first. `` is that 69%, lifted: a headless cutting room in which a 3D page takes the place of the video track, and everything else is a component reading one clock — the chased lens, the joins (wipe, dip, blend, iris, melt, blur, luma, flash, defocus), the lower third and the plate on Choreo, the timeline, the voice with its measured reads, the transport, the front door and the end card. - -```gts -import { Film, IframePicture } from '@cardstack/choreo/film'; - - - <:picture as |register|> - - - <:default as |f|> - - - - - - - - … - - - - <:gate as |f|>…the front matter, f.begin… - <:end as |f|>…the back matter, f.restart… - -``` - -A film is a GRAPH: a spine of chapters and shots with everything else -attached to a shot, compiled to the shot list the engine runs. Its script -carries the reads measured by ffprobe, the geometry sampled off its -model, and an identity block. The one-task behaviours — walk up the street and only then put your head back, follow the part being built, no line on a thing still going up — are beat fields with their handles exposed. The picture is a port (`Picture`), so a third film can hand in something other than an iframe. - -[docs/film.md](docs/film.md) is the **as-built reference**: every argument and block, the beat table, the clock and its one-tick cue offset, the two seek modes, the `Picture` port — and the section that matters most for the rest of the library, how `` composes with the other constructs rather than replacing them. It is an ordinary consumer of ``: one `c.Camera3D @through` for the entire picture, one `c.Perform` per beat anchored with `at('film')`, the lower third on its own region, and — in exact mode — two Choreo runs the film drives by writing `run.time` instead of playing them. - -[docs/film-graph/](docs/film-graph/) is the design record for the graph — the memo, the constructs, the revisions log and the plan — and [notes/film-construct.md](notes/film-construct.md) is the record behind the construct itself: the two reference films, the duplication measured, the fork between a seekable film and a chased one, and what was built. - ---- - -## The binding underneath - -Everything below this line is **`glimmer-motion`** proper: Motion's own implementation driven through Glimmer, covered by ports of the upstream tests that pin it. - -**Animation** — `initial` / `animate` / `exit` targets, keyframes, per-value transitions, `transitionEnd`; springs, tweens, inertia, `delay`, `repeat`, easings — the engine's full transition surface; **variants** with labels, functions, `custom`, propagation, `staggerChildren` / `delayChildren` / `when`; motion values (`motionValue`, `transformValue`, `animateMotionValue`) bound through `style`; static values and `MotionValue`s side by side, CSS variables, transform shorthands; SVG attribute animation and `pathLength`; `onAnimationStart` / `Complete` / `onUpdate`; unmount cleanup. - -**Layout** — `layout` (`true` / `"position"` / `"size"`): FLIP with the projection tree's scale correction (border radius, box shadow, children don't distort); `layoutId` shared-element transitions, crossfade, lead/follow promotion, the lightbox pattern; `` with `@id` and `@inherit`; `layoutDependency`, `layoutScroll`, `layoutRoot`, `layoutAnchor`, `layoutCrossfade`, `onLayoutMeasure`; nested and relative projection targets; portals, `instantLayoutTransition()`, `layoutChange()` / `snapshotAll()` / `requestSettle()`. - -**Presence** — `` = AnimatePresence: `sync` / `wait` / `popLayout`, `@initial={{false}}`, `@custom`, `@onExitComplete`, nested presence with `@propagate`; `h.isPresent` (tracked); exit-then-enter of the same key; a leaving item keeps its last props; interaction with `layout` / `layoutId`. - -**Drag** (Motion's own pan/drag session, inlined from framer-motion's build) — `drag` / axis locks / `dragPropagation` / `dragListener`; constraints as object, element or ref, `dragElastic`, `dragMomentum`, `dragTransition`, `dragSnapToOrigin`, re-measure on resize; `createDragControls()` with `snapToCursor`; `whileDrag` and the full handler set; pan handlers; drag inside `layout` / `layoutId`, nested draggables, drag under scroll; `transformPagePoint` with `correctParentTransform()` and `transformViewBoxPoint()`. - -**Gestures** — `whileHover` / `whileTap` / `whileFocus` / `whileInView` with their handlers, keyboard tap activation, `globalTapTarget`, viewport options, gesture priority; `` — and `@reducedMotion` defaults to **`"user"`** here, not React's `"never"`: honouring a platform setting is not a feature. `scrollProgress()` and `InView` over Motion's `scroll()` / `inView()`. - -**Reorder** — `` / `` on real `ul` / `li`; axis detection; auto-scroll near edges; works inside ``. - -**Glimmer** — `.gts` with Glint signatures throughout; plain-function template helpers (`to`, `spring`, `tween`, `inertia`, `stagger`, `ease`) so a template is not written in `(hash)`; a v2 addon, ESM, tree-shakeable; host hooks isolated in ~40 lines so the engine glue can be re-hosted. - -## Why the engine is untouched - -Motion's React library is two things. An animation engine — `motion-dom`: motion values, springs and keyframes, the _projection tree_ that does layout animation and scale correction, the pan/drag session — and a thin layer of React that feeds that engine props at the right moments. The engine is framework-free and published on its own. Only the glue is React. - -So this package does not re-implement animation, and it does not fork Motion. It binds the engine to Glimmer's rendering lifecycle and reproduces, exactly, the _ordering_ React's glue gives the engine: - -- visual elements are constructed **parents-first during render** and mounted **children-first in effects**; -- every element that re-renders **snapshots its layout before the DOM changes**, the root **measures after**; -- presence is announced **after the commit**, and a leaving element is rendered **from its last element** — its props frozen; -- the engine's own update runs in a microtask **after all of that**, never between a render and its mounts. - -Getting those right is the whole job. When they are right, the engine's behaviour matches Motion to the pixel — which is not an aspiration but the thing we measure. - -## How it was made - -Choreo started as the motion layer of a port: a React app built on Motion (the "bentobox" reference, frozen as ground truth) moving to a modern Vite/Embroider Ember app. The first question was how much work Motion needed to run under Glimmer. The answer turned out to be "none, if you don't touch it." - -**Strategy.** Cardstack had done this once before with [boxel-motion](https://github.com/cardstack/boxel), a Glimmer animation library with its own engine. Comparing the two made the call obvious: Motion's value is in the engine (the projection tree especially), and that engine is already framework-free. The binding would use `motion-dom` as-is and replicate only the React glue — and it would be held to the standard of Motion's own tests rather than our eyes. - -**Method: port the test suite, in order, and let it drive the binding.** Each step ported one of Motion's Jest suites or Cypress fixture specs line by line — the fixture components rebuilt as Glimmer components, the literal expected pixel values kept — and whatever failed was a place where the binding's ordering differed from React's. In sequence: the animate prop, variants, AnimatePresence, LayoutGroup, keyframes and delay, the style prop and unmount, then the eighteen `layout-*` Cypress specs, then the nineteen `drag-*` specs, then Reorder. Nothing was declared done on a behaviour until the upstream test for it was green in a real Chrome. - -**What that surfaced.** Almost everything a port like this gets wrong is a timing rule, and the tests found each one: - -- Glimmer installs modifiers children-first, so a child can't find its parent's visual element when its modifier first runs. First runs queue; the queue constructs in document order and mounts post-order after the pass — and sibling order matters, because the engine's stagger index is built from it. -- There is no `getSnapshotBeforeUpdate`. boxel-motion's render detector — a getter consuming `@glimmer/validator`'s `VOLATILE_TAG` re-evaluates on every render pass — lets `` snapshot every projection node before the DOM changes, the way React re-rendering each motion component would. -- The engine's update microtask must not run between Glimmer's render and the mounts, or it clears snapshots and layout animations become identity transitions. One settle per pass, after the mounts. -- An element created already-absent must still mount _present_ and then leave, or its exit is swallowed. -- AnimatePresence renders a leaving child from its last present element; a live Glimmer block doesn't. The modifier freezes an exiting element's props — React's behaviour, arrived at from a failing Cypress fixture. -- `initial` values must be in the DOM before the projection first measures; React has them in the style attribute synchronously, so the binding renders them synchronously at mount. -- A Cypress page is fresh per test; a test runner isn't. The document projection node caches scroll per `animationId` and leaked a stale offset across tests until the harness reset it the way a page visit does. - -**And then the choreography earned its own list.** The same discipline — ship nothing a test didn't force — produced the region model's rules: the fast keep that declines noise passes without touching the world; page-space deltas so a region can move in the pass that moves its children; the release-measure-reassert cycle that makes every measurement a _resting_ measurement; screen-CTM mapping for every place a client rect becomes local pixels (orphan locks, raises, scroll targets, tether ink — each was found double-scaling under an external transform, each is now pinned by a fixture inside a scaled wrapper); and the derived-value API redesign that [postmortem-follow.md](docs/postmortem-follow.md) documents in full. - -**Packaging.** With the suite green the binding was split along the line the imports already drew: `MotionNode` (one element's lifecycle, no host framework), the layout pipeline, the features and a `postRender` scheduler on one side; the `ember-modifier` shell, the scheduler's runloop adapter and the Glimmer components on the other. That second side is about forty lines plus the components — the surface another Glimmer host (a SES-sandboxed renderer, say) re-implements to reuse everything else. - -## Fidelity - -The suite has two halves, and they answer different questions. - -**Is it Motion?** `test-app` carries the upstream ports: the Jest suites (animate prop, variants, AnimatePresence, LayoutGroup, keyframes/delay, style prop, unmount) and the Cypress fixtures (all `layout-*`, all `drag-*`, `drag-to-reorder`, `drag-tabs`) — the fixture components rebuilt as Glimmer components with the literal expected pixel values kept. Where a literal was changed the reason is inline; each is a Cypress-runner artefact. Environment deltas are documented the same way. - -**Does the choreography model hold?** `` is not Motion's, so it has no upstream test to inherit. It has a contract suite that states its rules on small fixtures — nesting, beacon lifetime, undo, far matching, orphan ownership, anchors, composite steps, derived-value purity (a test literally vandalises the page mid-window and asserts the frame at _t_ does not change), gate retreat, continuity from both sides, and the external-scale rules — plus per-frame interruption tests and a soak that hammers the real gallery. - -**589 cases — 587 pass, 2 are upstream's own `it.skip`.** - -``` -pnpm install && pnpm test # builds the addon, runs the suite (a development-mode build, as boxel does) in Chrome -``` - -## API - -Named exports from `glimmer-motion`; deep imports (`glimmer-motion/presence`, …) are the same modules. - -### `{{motion}}` — `motion.div`, `motion.circle`, … as a modifier - -Any element. Named arguments are Motion's props — same names, same types (`MotionNodeOptions` from `motion-dom`): - -| group | props | -| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| animation | `initial` `animate` `exit` `variants` `transition` `custom` `inherit` `onAnimationStart` `onAnimationComplete` `onUpdate` | -| layout | `layout` `layoutId` `layoutDependency` `layoutScroll` `layoutRoot` `layoutCrossfade` `layoutAnchor` `onLayoutMeasure` | -| drag | `drag` `dragConstraints` `dragElastic` `dragMomentum` `dragSnapToOrigin` `dragDirectionLock` `dragPropagation` `dragTransition` `dragControls` `dragListener` `onDragStart` `onDrag` `onDragEnd` `onDirectionLock` `onMeasureDragConstraints` `whileDrag` | -| pan | `onPanStart` `onPan` `onPanEnd` `onPanSessionStart` | -| style | `style` — static values land on the element as React's style attribute would; `MotionValue`s are bound | -| choreo | `id` `role` — participation in the nearest `` region | -| glimmer-specific | `presence` — the handle a `` block yields; `transformPagePoint` — per element, or inherited from `` | - -### `` — the region - -`@id` (nesting and far matching), `@route` (a route swap inside is one pass), `@scroll` (`'top'` or a thunk — applied inside the pass, before final bounds), `@quiet` (pause the rest of the page for the run's span), `@debug` (outlines + `console.table` per run). Yields the context: the step components (`Sequence` `Parallel` `Gate` `Tween` `Spring` `Move` `Hold` `Wait` `Raise` `Scroll` `Camera` `Tether` `Follow` `Crossing`), the queries, `c.beacon` / `c.gesture`, and `c.run` — `advance()` `retreat()` `time` `speed` `parked` `standing` `segment` `finished`. - -Timing: `@duration` / `@delay` in seconds, `@spring` for spring specs, `@stagger` across a query's sprites; `@name` / `@at={{at 'name'}}` / `after('name')` on steps _and blocks_. Any property may be a function `(sprite, changeset) => value`; sprites carry `initial` / `final` bounds in `context`, `parent` and `page` space, a `delta`, and their `counterpart`. Open vocabulary: `StepComponent` + `toMs` + node literals ([docs/step-vocabulary.md](docs/step-vocabulary.md)). - -`c.Tween` also accepts normalized `@times` for keyframe arrays, so a loop can -hold, scatter, and regroup on an explicit rhythm without remounting its elements. -The [Circle continuity example](test-app/app/components/examples/circle-loop.gts) -and [guide](test-app/app/content/guides/interactive-circle-loop.md) show the pattern. - -### `` / `` / `` + `` / `` - -As in Motion — see [React → Glimmer](#react--glimmer). `` keeps leaving items rendered until their exit finishes (`@mode`, `@initial`, `@custom`, `@onExitComplete`, `@propagate`); `` namespaces `layoutId`s and hosts the render detector; outside one, wrap the state change in `layoutChange(() => …)`. - -### Template helpers - -Plain functions, no registration: `(to opacity=0 y=20)`, `(spring stiffness=300 damping=30)`, `(tween …)`, `(inertia …)`, `(stagger 0.05)`, `(ease 0.4 0 0.1 1)`. Typed, which is the difference: `(hash opacty=1)` is a perfectly good hash, and `(to opacty=1)` is a Glint error. `(hash …)` remains valid everywhere. - -### Drag helpers and motion values - -`createDragControls()`, `correctParentTransform(elOrRef)`, `transformViewBoxPoint(svgOrRef)`. For motion values use `motion-dom` directly — `motionValue()`, `transformValue()`, `animateMotionValue()` — and hand them in through `style`. - -## React → Glimmer - -| React | here | -| ---------------------------------------------- | ---------------------------------------------------------------------------- | -| `` | `
` — any tag, including SVG | -| `ref` | the element itself, or a `{current}` object filled by a modifier | -| `useMotionValue`, `useTransform` | `motionValue`, `transformValue` from `motion-dom` | -| `` with keyed children | `` yielding a handle | -| `useIsPresent()` | `h.isPresent` | -| `` | `` (also the render detector) | -| re-render of a motion component → snapshot | any render pass inside a `LayoutGroup` / `ReorderGroup`, or `layoutChange()` | -| `` | `` — same args, found by DOM proximity | -| `useDragControls()` | `createDragControls()` | -| `useScroll()` / `useInView()` | `scrollProgress()` / `new InView(…)` — no hooks, so no `use` prefix | -| `Reorder.Group` / `Reorder.Item` | `` / `` | -| — | ``: region-scoped timelines over a render pass's changeset | - -## Three rules React does not need - -React's model hides three things that Glimmer's does not, and each surfaced while porting a real app onto this binding. They are the only places where the port is not a mechanical translation. - -**1. Motion owns a motion element's inline style — pass CSS through the modifier, not a `style` attribute.** In React the `style` prop belongs to Motion: it merges what you write with the transforms it renders. In Glimmer a bound `style="…"` attribute is yours, and Glimmer rewrites the whole declaration whenever the bound value changes — wiping out the transform Motion just wrote. The symptom is brutal and quiet: a card that stays put while the drag logic runs perfectly around it. - -```gts -{{! ✗ the next re-render erases the drag transform }} -
- -{{! ✓ Motion applies these itself, custom properties included }} -
-``` - -**2. A leaving child stays live — read exiting content from the yielded item.** React keeps the _element tree_ it captured before the diff, so a leaving child cannot re-render. A Glimmer block re-runs from live tracked state for as long as the leaver is on screen. So anything the exit needs — the symbol the panel was showing, the rect the modal flew from — has to ride on the item `` yields, not be read back out of state that has already moved on. - -**3. Declare what you want tweened — a value in the stylesheet is invisible to the engine.** A layout animation moves and resizes by transform, and the engine corrects `borderRadius` and `boxShadow` against the scale in force — but only values it holds. A radius that lives in CSS is not one, and a square tile growing into a wide hero comes out with oval corners. Hand the value to Motion and every frame is corrected. - -```gts -{{! ✗ the engine cannot correct what it does not know about }} -
/* .card { border-radius: 14px } */ - -{{! ✓ corrected per frame, against whatever scale the layout animation is applying }} -
-``` - -## Testing - -See [Interruption, tested](#interruption-tested) for the test-support surface. The suite is in halves: `tests/integration/choreo/contract-test.gts` states the choreography rules on fixtures small enough that a failure names the defect; sibling suites cover the crossing, gates and retreat, continuity, wires, raise/scroll, far matching, and derived values; `interruption-test.gts` is the soak. The binding's half is the upstream ports ([Fidelity](#fidelity)). - -## Architecture - -``` -motion-dom (engine, unchanged) - ▲ 30 public exports -node.ts MotionNode — one element's lifecycle, no host framework: construct in document order, - mount post-order after the pass, update, freeze props while exiting, tear down -layout.ts snapshot → mount → measure → settle (React's getSnapshotBeforeUpdate / componentDidUpdate timing) -features.ts animation / exit / layout / drag / pan registrations, and the scale correctors -scheduler.ts postRender(fn): the single host hook — "after this render pass has committed" -activity.ts who is still moving, and why — what animationsSettled() waits on -helpers.ts to / styles / from / spring / tween / inertia / perValue / stagger / ease -framer-motion-internals.ts - Motion's pan/drag session, gesture and animation features, and Reorder's - checkReorder / detectAxis / auto-scroll, inlined from framer-motion's build (VENDORED.md) -gestures/ drag-gesture.ts (glimmer-motion's two drag deviations), drag controls, transformPagePoint helpers -choreo/ the choreography layer: changeset, compile, run, anchors, beacons, arming, - far-match barrier, measure — the region model in ~a dozen modules -test-support/ animationsSettled, bounds, live, orphanCount, … — a published entrypoint - ▲ Ember host adapter -motion.ts {{motion}} — the ember-modifier shell; installs the runloop as postRender -presence.gts, layout-group.gts, motion-config.gts, choreo.gts, reorder/{group,item}.gts -``` - -Only the last two lines know about Ember. Re-hosting means re-doing the modifier shell, the `postRender` adapter and the components; everything above the adapter line is untouched. `choreo-player` sits beside all of it, dependency-free, speaking only the run contract. - -## Not ported (yet) - -`m` / `LazyMotion` (a React bundle-splitting device; the addon is tree-shaken per module already), Reorder's `as` prop (the group is a `ul`, items are `li`), server rendering. - -## Examples - -`test-app` serves a gallery of **46 stages** at `/` — filter by **Animate**, **Layout**, **Drag**, **Scroll**, **Choreo**, **3D**, **Timeline**, or **Deep Dive**, and open any one for its annotated source. Most stages carry a speed control (**Full · ÷2 · ÷5 · ÷10**); a transition you cannot see is a transition you cannot judge, and the divisor scales the transition on its way to the engine rather than slowing a running animation, so what you watch at ÷10 is the same motion, born slower. The gallery ⇄ demo navigation is itself the crossing, eating its own cooking on every click. - -Every demo is also a test fixture: the interruption soak hammers them, which is why they are the first place a regression shows up. - -``` -pnpm --filter test-app start -``` - -### Guides and tuning - -Three complete tutorials provide runnable starting points: -[first application](test-app/app/content/guides/core-first-app.md), -[first spatial scene](test-app/app/content/guides/spatial-first-scene.md), and -[first MP4](test-app/app/content/guides/film-first-export.md). The -[troubleshooting guide](test-app/app/content/guides/core-troubleshooting.md) -connects common failures to concrete checks. - -The documentation website at `/docs` teaches four paths: **core glimmer-motion**, -**interactive Choreo**, **spatial & 3D Choreo**, and **recorded & film Choreo**. -Each section starts with motivation and goals; concept pages pair explanations -with code, live examples, experiments, and pitfalls. - -Every catalog demo links to its detailed guide and a DialKit workspace at -`/playground/:demo_id`. Controls edit named variables consumed by that demo, -with units, authored presets, and restoration of source defaults. Timing edits -update running motion without remounting the example. - -The [API inventory](docs/api-inventory.json) and -[teaching map](test-app/app/content/demo-lessons.json) cover 343 API/vocabulary -entries and all 46 demos across 88 guides. `pnpm docs:check` checks those mappings, -guide depth, section goals, and internal links. Focused docs-only spatial and film -studies remain [planned](docs/docs-studies-plan.md), separate from shipped examples. -See [documentation authoring](docs/documentation-site.md) for the content workflow. - -### 3D gallery - -`/_widgets` arranges the catalog in a dark exhibition space. Mockup-style -thumbsticks provide pan, zoom, and rotation around the viewer's fixed position; -arrow keys work when a control is focused. Inactive tiles use prerendered previews, -with live interaction activated on approach to limit rendering work. - -The full audio tour connects the demos through narrated explanations and guided -clicks. Its resume point survives pauses, exploration, and refreshes. A separate -highlight tour covers selected capabilities in about 59 seconds, with narration, -camera, and clicks following one media clock. Manual navigation pauses the guide. -The existing recorded highlight remains a separate export. - -Additional [screenshots and motion previews](docs/media/README.md) are linked -separately to keep this reference focused on the library. - -## Development - -``` -pnpm install -pnpm build # packages/glimmer-motion → dist/ + declarations/ -pnpm test # build, then test-app: vite build --mode=development --out-dir dist-tests && ember test --path dist-tests -pnpm lint:types # glint, workspace packages -pnpm docs:check # API and demo teaching coverage -pnpm build:boxel # gallery adaptation and realm artifacts -pnpm --filter test-app start # examples at / ; tests at /tests -``` - -Cards in a [Boxel](https://github.com/cardstack/boxel) realm import -`glimmer-motion` and `@cardstack/choreo` by their package names. The Boxel host -shims both packages, so a realm needs no bundled copy of either. - -The complete gallery, guides, tuning workspaces, and 3D room can also be deployed -through the [Boxel gallery build](packages/choreo-gallery/README.md). Use your own -realm and host configuration; account-specific addresses do not belong in the -source documentation. GitHub Pages continues to build the canonical `test-app`. - -Agents can use the shared [choreo-create skill](.claude/skills/choreo-create/SKILL.md) -from Claude Code or [Codex](.agents/skills/choreo-create/SKILL.md). It routes to -real demo sources, motion patterns, composition recipes, tuning, and verification. - -`packages/glimmer-motion/VENDORED.md` lists the Motion modules glimmer-motion inlines from framer-motion's build, the two deliberate deviations, and the one adapted file; when bumping Motion, re-check the declarations in `src/framer-motion-internals.ts` against upstream. The Cypress-port harness lives in `test-app/tests/helpers/layout-fixture.ts`. - -## Roadmap - -- **`0.1.0` on npm.** Consumers today use `workspace:*` against a checkout. A version number is what turns "copy this style" into a dependency, and it is the gate for everything below. -- **`ember-try` against LTS** (5.12 / 6.4 / release). The peer range already says `>= 5.4`; nothing proves it. -- **`ember-a11y-testing` over the gallery.** `reducedMotion` already defaults to `"user"`, which was the substantive half; a smoke pass is the other half. -- **Focused spatial and film studies** — smaller, tunable teaching examples alongside the existing composite demos; see the [study plan](docs/docs-studies-plan.md). -- **boxel-motion** — a re-host inside [cardstack/boxel](https://github.com/cardstack/boxel) for its constraints (SES-sandboxed card code, cross-realm orchestration, fitted vs embedded intrinsic sizing). It replaces the modifier shell, the scheduler adapter and the components and keeps everything above the adapter line. Three host transitions are the proving ground: a stack open/close (far match), a panel that is its own scene over a moving shell (nested ``), and compose/trash (`{{beacon}}`, not `layoutId`). - -## License - -MIT. © 2026 Cardstack Foundation. See [LICENSE](LICENSE). - -## Credits - -The engine, the algorithms and the test suites are [Motion](https://motion.dev) ([motiondivision/motion](https://github.com/motiondivision/motion), MIT). The render-detector idea and the choreography model's ambitions are from [boxel-motion](https://github.com/cardstack/boxel). This package is the Glimmer binding and the choreography layer, by Cardstack. diff --git a/packages/choreo-gallery/eslint.config.mjs b/packages/choreo-gallery/eslint.config.mjs index b0502a68fab..5cb26e454a5 100644 --- a/packages/choreo-gallery/eslint.config.mjs +++ b/packages/choreo-gallery/eslint.config.mjs @@ -8,10 +8,6 @@ import globals from 'globals'; export default defineConfig([ globalIgnores([ - // the film-graph sketches are proposals written in a syntax that does - // not exist yet (f.Spine, f.Attach, f.picture.*): read them, don't lint them - 'docs/film-graph/**/*.gts', - 'docs/film-graph/**/*.mjs', 'out/**', // the film app's build (choreo-film-app) and the films' vendored scripts 'realm/film-app/**', diff --git a/packages/choreo-gallery/realm/asset/towers-model.html b/packages/choreo-gallery/realm/asset/towers-model.html index 3ec8cdce11c..415c3955cbf 100644 --- a/packages/choreo-gallery/realm/asset/towers-model.html +++ b/packages/choreo-gallery/realm/asset/towers-model.html @@ -18,7 +18,7 @@ master/voice bus mixer with narration on the graph, winter (settled snow and a pinned blizzard on the shot's clock), lightning on cue, the page's own UI hidden from the first paint under html[data-hosted], and the - lightning flash plane hidden when unlit. docs/towers-porting-delta.md + lightning flash plane hidden when unlit. design/towers-porting-delta.md keeps the list of what upstream can do that the film does not use yet. --> diff --git a/packages/choreo-gallery/notes/README.md b/packages/choreo-gallery/realm/design/README.md similarity index 65% rename from packages/choreo-gallery/notes/README.md rename to packages/choreo-gallery/realm/design/README.md index d4e02850d8f..16ea6ffe5ca 100644 --- a/packages/choreo-gallery/notes/README.md +++ b/packages/choreo-gallery/realm/design/README.md @@ -1,16 +1,22 @@ -# Working notes +# Design notes This is the workshop, not the documentation. Everything here is a draft, a design record, a handoff, a measurement or a production script — kept because it says _why_ something is the way it is, and cited from the source where the reasoning matters. -**The documentation is in [`docs/`](../docs).** If you are trying to use -Choreo, start at [docs/guide.md](../docs/guide.md) and -[docs/choreo-constructs.md](../docs/choreo-constructs.md). Nothing in +**The documentation is in [`docs/`](../docs/README.md).** If you are trying to use +Choreo, start at [the guide](../docs/guide.md) and +[the construct reference](../docs/choreo-constructs.md). Nothing in this folder is a reference, and some of it describes things that were proposed and never built, or built and then changed. +The notes were written in Choreo's own repository, before it moved into the +Boxel monorepo, so the source paths they name (`test-app/…`, +`packages/glimmer-motion/src/choreo/…`) are that repository's layout at the +time. The demos are now cards in this realm, Choreo and Film are +`packages/choreo`, and the films' app is `packages/choreo-film-app`. + ## What is here **Design records** — the argument behind a construct, written before or @@ -19,7 +25,7 @@ during the work. - [film-construct.md](film-construct.md) — toward ``: the two reference films, the duplication measured, the fork between a seekable film and a chased one. The as-built reference is - [docs/film.md](../docs/film.md). + [film.md](../docs/film.md). - [choreo-composition.md](choreo-composition.md) — the compositor. - [drift.md](drift.md) — a driving model written by hand, and why a score was the wrong shape for it. @@ -32,6 +38,13 @@ during the work. proposals. - [camera-api-wishlist.md](camera-api-wishlist.md) — what `` was missing, written from the outside. +- [film-graph/](film-graph/README.md) — the films as a graph: the memo's + sketches, the constructs, the revisions log and the plan. +- [docs-studies-plan.md](docs-studies-plan.md) — focused, tunable teaching + studies, planned rather than shipped. +- [documentation-site.md](documentation-site.md) — the guides site: its four + learning paths, how a guide is written and registered, live tuning, and + teaching coverage. **Handoffs and measurements** — written for whoever picks the work up. @@ -39,6 +52,10 @@ during the work. - [subdivision-browser-timing-test.md](subdivision-browser-timing-test.md) - [towers-porting-delta.md](towers-porting-delta.md), [towers-quality.md](towers-quality.md) +- [sagrada-web-model.md](sagrada-web-model.md) — the Sagrada picture's + procedural model pass +- [gallery/](gallery/README.md) — the 3D gallery room and its narrated + tours: room design, tile capacity, tour script and pacing, performance **Film production** — the scripts, the wall text, the narration. diff --git a/packages/choreo-gallery/notes/camera-api-wishlist.md b/packages/choreo-gallery/realm/design/camera-api-wishlist.md similarity index 100% rename from packages/choreo-gallery/notes/camera-api-wishlist.md rename to packages/choreo-gallery/realm/design/camera-api-wishlist.md diff --git a/packages/choreo-gallery/notes/choreo-composition.md b/packages/choreo-gallery/realm/design/choreo-composition.md similarity index 99% rename from packages/choreo-gallery/notes/choreo-composition.md rename to packages/choreo-gallery/realm/design/choreo-composition.md index 01d470a1626..ab47b2e4211 100644 --- a/packages/choreo-gallery/notes/choreo-composition.md +++ b/packages/choreo-gallery/realm/design/choreo-composition.md @@ -615,7 +615,7 @@ piece is at gallery quality. the existing seekable step — after Clip, not before. The plane-and-camera pairing has its own design: -[planes-and-cameras.md](planes-and-cameras.md) — independent pan and zoom +[planes-and-cameras.md](../docs/planes-and-cameras.md) — independent pan and zoom per plane (shipped in the reel's clock plane), the coordinate contract, and sprites moving between planes without relayout. diff --git a/packages/choreo-gallery/notes/demos-as-applications.md b/packages/choreo-gallery/realm/design/demos-as-applications.md similarity index 100% rename from packages/choreo-gallery/notes/demos-as-applications.md rename to packages/choreo-gallery/realm/design/demos-as-applications.md diff --git a/packages/choreo-gallery/notes/dialkit.md b/packages/choreo-gallery/realm/design/dialkit.md similarity index 99% rename from packages/choreo-gallery/notes/dialkit.md rename to packages/choreo-gallery/realm/design/dialkit.md index bd0c6af5452..58c7c83fb11 100644 --- a/packages/choreo-gallery/notes/dialkit.md +++ b/packages/choreo-gallery/realm/design/dialkit.md @@ -240,7 +240,7 @@ localStorage with no way home. They sit in one row because to a player they are one question — what am I driving — but only one of them can be deleted. **`type: 'spring'` is NOT cheap, and the plan that said it was got the name -wrong.** `notes/drift.md` proposed one spring control per pair instead of two +wrong.** `drift.md` proposed one spring control per pair instead of two sliders, "cheap once folders exist". The store does not emit `'spring'` for a `SpringConfig` at all: it emits `type: 'transition'` (store `index.js:552`), whose value is the whole spring object plus a companion `path.__mode` that diff --git a/packages/choreo-gallery/docs/docs-studies-plan.md b/packages/choreo-gallery/realm/design/docs-studies-plan.md similarity index 100% rename from packages/choreo-gallery/docs/docs-studies-plan.md rename to packages/choreo-gallery/realm/design/docs-studies-plan.md diff --git a/packages/choreo-gallery/docs/documentation-site.md b/packages/choreo-gallery/realm/design/documentation-site.md similarity index 89% rename from packages/choreo-gallery/docs/documentation-site.md rename to packages/choreo-gallery/realm/design/documentation-site.md index e73ac38b22e..95313077dd0 100644 --- a/packages/choreo-gallery/docs/documentation-site.md +++ b/packages/choreo-gallery/realm/design/documentation-site.md @@ -1,5 +1,8 @@ # Choreo Documentation Website +> The design of the test app's documentation site, which the gallery realm's +> guides take over. Paths name the test app as it was. + The guides are part of `test-app`, sharing its brand mark, theme, navigation, and deployment. Open `/docs`; `/docs/:topic_id` is a guide, `/playground/:demo_id` is a live tuning workspace, and `/_widgets` opens the 3D gallery. ## Information Architecture @@ -19,7 +22,7 @@ The writing reference is [ember-learn/guides-source](https://github.com/ember-le Use a friendly, direct explanation of an observable behavior. Start with the common case, show a small example, then explain the relevant lines. Introduce technical terms when the reader needs them. Open each page with a preamble that explains its purpose before the live example. Each main section starts with Motivation and Learning Goals. Keep a guide focused enough to read in a few screens, and split advanced topics into linked pages. Code blocks show a filename or explicitly identify an excerpt. Use this repository's named exports and `.gts` conventions instead of copying Ember's `.gjs` naming literally. -Source Markdown lives in `test-app/app/content/guides/`. Register introductory pages in `test-app/app/lib/guides.ts` and deeper concept pages in `test-app/app/lib/guide-reference.ts`. The reviewed metadata is recorded in `docs/guide-topics.json`. The renderer escapes raw HTML, highlights code with the gallery's existing highlighter, and rewrites internal URLs through Ember's router so hash and subdirectory deployments work. +Source Markdown lives in `test-app/app/content/guides/`. Register introductory pages in `test-app/app/lib/guides.ts` and deeper concept pages in `test-app/app/lib/guide-reference.ts`. The reviewed metadata is recorded in `test-app/app/content/guide-topics.json`. The renderer escapes raw HTML, highlights code with the gallery's existing highlighter, and rewrites internal URLs through Ember's router so hash and subdirectory deployments work. Use `/docs/` for guide links, the public catalog ID for demos, and `/_widgets` for the 3D room. Do not assume an example filename is its public route ID. Label proposal-only material clearly; the guides describe implemented behavior. @@ -52,7 +55,7 @@ Publish the complete build and required static media using the chosen host. Do n ## API Coverage and Depth -`docs/api-inventory.json` maps 343 exported names and yielded vocabulary members to their concept guides. Each registered guide has at least 300 explanatory prose words, excluding code and API tables. Related aliases and supporting types share their behavior chapter. `scripts/check-guide-coverage.mjs` compares the inventory with the TypeScript declarations and checks depth, preambles, section goals, and internal guide links. Run `pnpm docs:check` after an API or documentation change. +`packages/choreo-gallery/tools/api-inventory.json` maps 343 exported names and yielded vocabulary members to their concept guides. Each registered guide has at least 300 explanatory prose words, excluding code and API tables. Related aliases and supporting types share their behavior chapter. `scripts/check-guide-coverage.mjs` compares the inventory with the TypeScript declarations and checks depth, preambles, section goals, and internal guide links. Run `pnpm docs:check` after an API or documentation change. The inventory appears at `/docs/core-api-inventory`. Keep it synchronized when adding a public API. Do not automatically map a new export to a broad introduction simply to satisfy the check: review whether its concept needs a dedicated treatment. @@ -90,7 +93,7 @@ example code. Seven focused walkthroughs are registered in `demo-guides.ts`. `docs:check` verifies that all catalog examples remain embedded and have complete teaching records; prose correctness still requires source review. -`docs/docs-studies-plan.md` specifies the planned docs-only spatial and film +[docs-studies-plan.md](docs-studies-plan.md) specifies the planned docs-only spatial and film studies. Keep these separate from shipped demo coverage until implemented. The 3D room and its tile frames force a dark palette without overwriting the visitor’s saved theme. `scripts/check-guide-lessons.mjs` checks real embedded diff --git a/packages/choreo-gallery/notes/drift.md b/packages/choreo-gallery/realm/design/drift.md similarity index 99% rename from packages/choreo-gallery/notes/drift.md rename to packages/choreo-gallery/realm/design/drift.md index 8b1292374a2..d4e0534ce41 100644 --- a/packages/choreo-gallery/notes/drift.md +++ b/packages/choreo-gallery/realm/design/drift.md @@ -170,7 +170,7 @@ emits `type: 'transition'` for a `SpringConfig` (store `index.js:552`), whose value is the whole spring object plus a companion `path.__mode` switching between an easing curve, a two-number "simple" form and a five-number "advanced" one. Folders of ordinary sliders get the same nesting for none of -that. See `notes/dialkit.md` for the full account of the second pass. +that. See `dialkit.md` for the full account of the second pass. **The skid marks are a canvas.** The note wanted them as `c.inserted` / `c.removed` over a list nobody clicked to build. A mark is laid every few world diff --git a/packages/choreo-gallery/notes/external-clock-camera-seek-handoff.md b/packages/choreo-gallery/realm/design/external-clock-camera-seek-handoff.md similarity index 100% rename from packages/choreo-gallery/notes/external-clock-camera-seek-handoff.md rename to packages/choreo-gallery/realm/design/external-clock-camera-seek-handoff.md diff --git a/packages/choreo-gallery/notes/film-construct.md b/packages/choreo-gallery/realm/design/film-construct.md similarity index 99% rename from packages/choreo-gallery/notes/film-construct.md rename to packages/choreo-gallery/realm/design/film-construct.md index 8e9eb1d8118..c01d8ec47c4 100644 --- a/packages/choreo-gallery/notes/film-construct.md +++ b/packages/choreo-gallery/realm/design/film-construct.md @@ -660,7 +660,7 @@ derives everything else from it: `Beat.clip` exists (2026-09-02, after seek). A beat is a shot of the picture; a clip is something laid over it for a while — a video, a still, or a freeze of the picture itself — and it is the compositor's `ClipSpec` -arithmetic (`notes/choreo-composition.md`, Phase C3) lifted whole: +arithmetic (`choreo-composition.md`, Phase C3) lifted whole: ``` source = in + (film − start) × rate diff --git a/packages/choreo-gallery/docs/film-graph/CONSTRUCTS.md b/packages/choreo-gallery/realm/design/film-graph/CONSTRUCTS.md similarity index 100% rename from packages/choreo-gallery/docs/film-graph/CONSTRUCTS.md rename to packages/choreo-gallery/realm/design/film-graph/CONSTRUCTS.md diff --git a/packages/choreo-gallery/docs/film-graph/GAPS.md b/packages/choreo-gallery/realm/design/film-graph/GAPS.md similarity index 100% rename from packages/choreo-gallery/docs/film-graph/GAPS.md rename to packages/choreo-gallery/realm/design/film-graph/GAPS.md diff --git a/packages/choreo-gallery/docs/film-graph/PLAN.md b/packages/choreo-gallery/realm/design/film-graph/PLAN.md similarity index 98% rename from packages/choreo-gallery/docs/film-graph/PLAN.md rename to packages/choreo-gallery/realm/design/film-graph/PLAN.md index 33f464b1eef..fe12244fe37 100644 --- a/packages/choreo-gallery/docs/film-graph/PLAN.md +++ b/packages/choreo-gallery/realm/design/film-graph/PLAN.md @@ -73,7 +73,7 @@ Three things have to be true at the end, and each is testable: Mockup, the Long Take, Sylva, Replate — is written the way its sketch in this folder is written, and the old `FilmSignature` with its thirty-five arguments is gone. -- **Documentation:** `docs/film.md` describes the graph; `docs/choreo-constructs.md` +- **Documentation:** `../../docs/film.md` describes the graph; `../../docs/choreo-constructs.md` §3 lists Attach and Value with the other constructs; this folder's memo, sketches and revisions log become the design record. @@ -321,12 +321,12 @@ are useful without it. ### Documentation, alongside each phase -- `docs/film.md` rewritten as the as-built reference for the graph +- `../../docs/film.md` rewritten as the as-built reference for the graph (Phase 2), keeping its §4 clock section verbatim — the load-bearing TICK offset does not change. -- `docs/choreo-constructs.md` §3 gains Attach and Value; §4 gains the +- `../../docs/choreo-constructs.md` §3 gains Attach and Value; §4 gains the driven-run and value contracts (Phases 1 and 4). -- `docs/step-vocabulary.md` gains the `Shot` base class beside +- `../../docs/step-vocabulary.md` gains the `Shot` base class beside `StepComponent` (Phase 2). - This folder moves to `docs/film-graph/` as the design record: the memo, `CONSTRUCTS.md`, `REVISIONS.md`, the six sketches (Phase 2). diff --git a/packages/choreo-gallery/docs/film-graph/README.md b/packages/choreo-gallery/realm/design/film-graph/README.md similarity index 78% rename from packages/choreo-gallery/docs/film-graph/README.md rename to packages/choreo-gallery/realm/design/film-graph/README.md index a035d6fb6ed..58d2aa9fb80 100644 --- a/packages/choreo-gallery/docs/film-graph/README.md +++ b/packages/choreo-gallery/realm/design/film-graph/README.md @@ -6,26 +6,26 @@ > real films before any of it is built. The design they follow is the > 2026-09-03 memo _Film as a Graph_ (an artifact; to be filed here). -| file | what it is | -| --------------- | ------------------------------------------------------------------------------------------------------- | -| `sagrada.gts` | Sagrada Família, 29 shots in 5 chapters, in the graph syntax | -| `towers.gts` | Towers, 26 shots in 5 chapters, in the graph syntax | -| `mockup.gts` | the Mockup demo as a film: six scenes, jump cuts and joins it could not say | -| `long-take.gts` | the Long Take as a film: one shot, two cameras, the inner one a lane of each shot | -| `sylva.gts` | Sylva as a film: a custom shot kind whose pose is a query on the picture; planes that replace | -| `replate.gts` | Replate as a film: a reel attached to a moving anchor through a time map; one filter, not an adjustment | -| `tograph.mjs` | the converter: reads a film's `BEATS` table and emits its spine, verbatim | -| `REVISIONS.md` | the running log: what each demo changed in syntax, library, parameters | -| `CONSTRUCTS.md` | the rationalisation: two engine constructs, and what everything else is | -| `PLAN.md` | what it takes to make this the baseline: comfort, spikes, phases, sizes | -| `SPIKES.md` | the two spikes' written results: the lane door passes; the fold's order is pinned | +| file | what it is | +| ----------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| [`sagrada.gts`](sagrada.gts.md) | Sagrada Família, 29 shots in 5 chapters, in the graph syntax | +| [`towers.gts`](towers.gts.md) | Towers, 26 shots in 5 chapters, in the graph syntax | +| [`mockup.gts`](mockup.gts.md) | the Mockup demo as a film: six scenes, jump cuts and joins it could not say | +| [`long-take.gts`](long-take.gts.md) | the Long Take as a film: one shot, two cameras, the inner one a lane of each shot | +| [`sylva.gts`](sylva.gts.md) | Sylva as a film: a custom shot kind whose pose is a query on the picture; planes that replace | +| [`replate.gts`](replate.gts.md) | Replate as a film: a reel attached to a moving anchor through a time map; one filter, not an adjustment | +| `tograph.mjs` | the converter, in `packages/choreo-film-app/tools/`: reads a film's `BEATS` table and emits its spine, verbatim | +| [`REVISIONS.md`](REVISIONS.md) | the running log: what each demo changed in syntax, library, parameters | +| [`CONSTRUCTS.md`](CONSTRUCTS.md) | the rationalisation: two engine constructs, and what everything else is | +| [`PLAN.md`](PLAN.md) | what it takes to make this the baseline: comfort, spikes, phases, sizes | +| [`SPIKES.md`](SPIKES.md) | the two spikes' written results: the lane door passes; the fold's order is pinned | Both spines were **generated** from the beat tables on `origin/main` (`2547ded`), not written by hand, so every pose, phrase, stamp and trace is the film's own and the translation can be re-run when the tables move: ```bash -CHAPTERS='[…]' node docs/film-graph/tograph.mjs test-app/app/components/sagrada-film.gts +CHAPTERS='[…]' node packages/choreo-film-app/tools/tograph.mjs packages/choreo-film-app/app/lib/films/sagrada.ts ``` ## The legend diff --git a/packages/choreo-gallery/docs/film-graph/REVISIONS.md b/packages/choreo-gallery/realm/design/film-graph/REVISIONS.md similarity index 99% rename from packages/choreo-gallery/docs/film-graph/REVISIONS.md rename to packages/choreo-gallery/realm/design/film-graph/REVISIONS.md index ae9f63c6f73..508babb09ab 100644 --- a/packages/choreo-gallery/docs/film-graph/REVISIONS.md +++ b/packages/choreo-gallery/realm/design/film-graph/REVISIONS.md @@ -464,7 +464,7 @@ length, not a regression to hunt. Measured: `fade('chapter', 0.9201)` one frame after the cut on main, `fade('chapter', 0)` after the fix. - _"the dip isn't super smooth. are there better curves?"_ Yes, and the - reason is compositing space, not easing. See docs/film.md §5.5. + reason is compositing space, not easing. See ../../docs/film.md §5.5. - And the tier's own regression, found while measuring the dip: the decode gate defers the CUT but not the BEAT, so the hour, the build clock and the model landed two or three frames before the seam had diff --git a/packages/choreo-gallery/docs/film-graph/SPIKES.md b/packages/choreo-gallery/realm/design/film-graph/SPIKES.md similarity index 100% rename from packages/choreo-gallery/docs/film-graph/SPIKES.md rename to packages/choreo-gallery/realm/design/film-graph/SPIKES.md diff --git a/packages/choreo-gallery/docs/film-graph/TRANSITIONS.md b/packages/choreo-gallery/realm/design/film-graph/TRANSITIONS.md similarity index 100% rename from packages/choreo-gallery/docs/film-graph/TRANSITIONS.md rename to packages/choreo-gallery/realm/design/film-graph/TRANSITIONS.md diff --git a/packages/choreo-gallery/docs/film-graph/long-take.gts b/packages/choreo-gallery/realm/design/film-graph/long-take.gts.md similarity index 98% rename from packages/choreo-gallery/docs/film-graph/long-take.gts rename to packages/choreo-gallery/realm/design/film-graph/long-take.gts.md index be1284a8cd8..db7a8094b9d 100644 --- a/packages/choreo-gallery/docs/film-graph/long-take.gts +++ b/packages/choreo-gallery/realm/design/film-graph/long-take.gts.md @@ -1,3 +1,8 @@ +# `long-take.gts`, in the graph syntax + +A sketch: it does not compile. See [the film graph](README.md). + +```gts /** * THE LONG TAKE, AS A FILM — a sketch, not a build. * @@ -176,3 +181,4 @@ export class LongTake extends Component {
} +``` diff --git a/packages/choreo-gallery/docs/film-graph/mockup.gts b/packages/choreo-gallery/realm/design/film-graph/mockup.gts.md similarity index 98% rename from packages/choreo-gallery/docs/film-graph/mockup.gts rename to packages/choreo-gallery/realm/design/film-graph/mockup.gts.md index af1c65041eb..ed4342a3d1a 100644 --- a/packages/choreo-gallery/docs/film-graph/mockup.gts +++ b/packages/choreo-gallery/realm/design/film-graph/mockup.gts.md @@ -1,3 +1,8 @@ +# `mockup.gts`, in the graph syntax + +A sketch: it does not compile. See [the film graph](README.md). + +```gts /** * THE MOCKUP, AS A FILM — a sketch, not a build. * @@ -183,3 +188,4 @@ export class Mockup extends Component {
} +``` diff --git a/packages/choreo-gallery/docs/film-graph/replate.gts b/packages/choreo-gallery/realm/design/film-graph/replate.gts.md similarity index 98% rename from packages/choreo-gallery/docs/film-graph/replate.gts rename to packages/choreo-gallery/realm/design/film-graph/replate.gts.md index 38469fb3234..4c7876cb133 100644 --- a/packages/choreo-gallery/docs/film-graph/replate.gts +++ b/packages/choreo-gallery/realm/design/film-graph/replate.gts.md @@ -1,3 +1,8 @@ +# `replate.gts`, in the graph syntax + +A sketch: it does not compile. See [the film graph](README.md). + +```gts /** * REPLATE, AS A FILM — a sketch, not a build. * @@ -121,3 +126,4 @@ export class Replate extends Component { f.send('picture', which); }; } +``` diff --git a/packages/choreo-gallery/docs/film-graph/sagrada.gts b/packages/choreo-gallery/realm/design/film-graph/sagrada.gts.md similarity index 99% rename from packages/choreo-gallery/docs/film-graph/sagrada.gts rename to packages/choreo-gallery/realm/design/film-graph/sagrada.gts.md index 1c9603681e3..05f69bed224 100644 --- a/packages/choreo-gallery/docs/film-graph/sagrada.gts +++ b/packages/choreo-gallery/realm/design/film-graph/sagrada.gts.md @@ -1,3 +1,8 @@ +# `sagrada.gts`, in the graph syntax + +A sketch: it does not compile. See [the film graph](README.md). + +```gts /** * SAGRADA FAMÍLIA, AS A GRAPH — a sketch, not a build. * @@ -1223,3 +1228,4 @@ export default class SagradaFilm extends Component<{ Args: { embed?: boolean } } } +``` diff --git a/packages/choreo-gallery/docs/film-graph/sylva.gts b/packages/choreo-gallery/realm/design/film-graph/sylva.gts.md similarity index 98% rename from packages/choreo-gallery/docs/film-graph/sylva.gts rename to packages/choreo-gallery/realm/design/film-graph/sylva.gts.md index e67febcd596..a5772bbe383 100644 --- a/packages/choreo-gallery/docs/film-graph/sylva.gts +++ b/packages/choreo-gallery/realm/design/film-graph/sylva.gts.md @@ -1,3 +1,8 @@ +# `sylva.gts`, in the graph syntax + +A sketch: it does not compile. See [the film graph](README.md). + +```gts /** * SYLVA, AS A FILM — a sketch, not a build. * @@ -157,3 +162,4 @@ export class SylvaStage extends Component<{ Args: { theater?: boolean } }> { f.send('picture', 'fly', spot.id); }; } +``` diff --git a/packages/choreo-gallery/docs/film-graph/towers.gts b/packages/choreo-gallery/realm/design/film-graph/towers.gts.md similarity index 99% rename from packages/choreo-gallery/docs/film-graph/towers.gts rename to packages/choreo-gallery/realm/design/film-graph/towers.gts.md index 2f25b9520ad..64db916e2dd 100644 --- a/packages/choreo-gallery/docs/film-graph/towers.gts +++ b/packages/choreo-gallery/realm/design/film-graph/towers.gts.md @@ -1,3 +1,8 @@ +# `towers.gts`, in the graph syntax + +A sketch: it does not compile. See [the film graph](README.md). + +```gts /** * TOWERS, AS A GRAPH — a sketch, not a build. * @@ -885,3 +890,4 @@ export default class TowerFilm extends Component<{ Args: { embed?: boolean } }> {{! THE IDENTITY: the ~140 lines of serif, hanko and cue overrides, unchanged }} } +``` diff --git a/packages/choreo-gallery/docs/gallery/PERFORMANCE.md b/packages/choreo-gallery/realm/design/gallery/PERFORMANCE.md similarity index 100% rename from packages/choreo-gallery/docs/gallery/PERFORMANCE.md rename to packages/choreo-gallery/realm/design/gallery/PERFORMANCE.md diff --git a/packages/choreo-gallery/docs/gallery/QUICK-TOUR.md b/packages/choreo-gallery/realm/design/gallery/QUICK-TOUR.md similarity index 100% rename from packages/choreo-gallery/docs/gallery/QUICK-TOUR.md rename to packages/choreo-gallery/realm/design/gallery/QUICK-TOUR.md diff --git a/packages/choreo-gallery/docs/gallery/README.md b/packages/choreo-gallery/realm/design/gallery/README.md similarity index 98% rename from packages/choreo-gallery/docs/gallery/README.md rename to packages/choreo-gallery/realm/design/gallery/README.md index 9d926250106..a662f23c2dd 100644 --- a/packages/choreo-gallery/docs/gallery/README.md +++ b/packages/choreo-gallery/realm/design/gallery/README.md @@ -67,7 +67,7 @@ assets, and repeatable capture tooling, not a hosted video link. - `scripts/capture-widget-previews-now.mjs`: recapture posters from actual cards. - `scripts/package-widget-boxel.mjs`: standalone Boxel host packaging. -See [Boxel deployment](BOXEL-DEPLOYMENT.md), [room design](ROOM-DESIGN.md), +See [room design](ROOM-DESIGN.md), [highlight score](QUICK-TOUR.md), and [performance notes](PERFORMANCE.md). The accompanying Sagrada changes refine model geometry, shader stability, material grading, and typography; its procedural model is an interpretation, diff --git a/packages/choreo-gallery/docs/gallery/ROOM-DESIGN.md b/packages/choreo-gallery/realm/design/gallery/ROOM-DESIGN.md similarity index 100% rename from packages/choreo-gallery/docs/gallery/ROOM-DESIGN.md rename to packages/choreo-gallery/realm/design/gallery/ROOM-DESIGN.md diff --git a/packages/choreo-gallery/docs/gallery/TILE-CAPACITY.md b/packages/choreo-gallery/realm/design/gallery/TILE-CAPACITY.md similarity index 100% rename from packages/choreo-gallery/docs/gallery/TILE-CAPACITY.md rename to packages/choreo-gallery/realm/design/gallery/TILE-CAPACITY.md diff --git a/packages/choreo-gallery/docs/gallery/TOUR-PACING.md b/packages/choreo-gallery/realm/design/gallery/TOUR-PACING.md similarity index 100% rename from packages/choreo-gallery/docs/gallery/TOUR-PACING.md rename to packages/choreo-gallery/realm/design/gallery/TOUR-PACING.md diff --git a/packages/choreo-gallery/docs/gallery/TOUR-SCRIPT.md b/packages/choreo-gallery/realm/design/gallery/TOUR-SCRIPT.md similarity index 100% rename from packages/choreo-gallery/docs/gallery/TOUR-SCRIPT.md rename to packages/choreo-gallery/realm/design/gallery/TOUR-SCRIPT.md diff --git a/packages/choreo-gallery/notes/hyperframes-choreo-foundation.md b/packages/choreo-gallery/realm/design/hyperframes-choreo-foundation.md similarity index 100% rename from packages/choreo-gallery/notes/hyperframes-choreo-foundation.md rename to packages/choreo-gallery/realm/design/hyperframes-choreo-foundation.md diff --git a/packages/choreo-gallery/notes/sagrada-film.md b/packages/choreo-gallery/realm/design/sagrada-film.md similarity index 98% rename from packages/choreo-gallery/notes/sagrada-film.md rename to packages/choreo-gallery/realm/design/sagrada-film.md index a368670d68f..8cf2e043571 100644 --- a/packages/choreo-gallery/notes/sagrada-film.md +++ b/packages/choreo-gallery/realm/design/sagrada-film.md @@ -1,6 +1,6 @@ # The Sagrada film -`/_sagrada` is a second film on the Towers engine (`docs/choreo-splices.md` +`/_sagrada` is a second film on the Towers engine (`../docs/choreo-splices.md` for the machinery). The scene is the construction study in `~/Projects/sagrada-familia`, vendored to `test-app/public/sagrada.html` by that repo's own build (`node build/build.mjs --lean …`), and the film is @@ -217,7 +217,7 @@ completion date, and the credits no longer claim someone else's work. ## Status: the second reference film This film is committed as the second creative reference for the Choreo -film construct — see `notes/film-construct.md` for what the two films have +film construct — see `film-construct.md` for what the two films have in common, what a `` would absorb, and the traps found building them. `/_sagrada` serves the picture alone: no wall plate, no deep dive. The notes component is still in the repo for whoever writes the construct; diff --git a/packages/choreo-gallery/notes/sagrada-vo.md b/packages/choreo-gallery/realm/design/sagrada-vo.md similarity index 100% rename from packages/choreo-gallery/notes/sagrada-vo.md rename to packages/choreo-gallery/realm/design/sagrada-vo.md diff --git a/packages/choreo-gallery/docs/sagrada-web-model.md b/packages/choreo-gallery/realm/design/sagrada-web-model.md similarity index 100% rename from packages/choreo-gallery/docs/sagrada-web-model.md rename to packages/choreo-gallery/realm/design/sagrada-web-model.md diff --git a/packages/choreo-gallery/notes/subdivision-browser-timing-test.md b/packages/choreo-gallery/realm/design/subdivision-browser-timing-test.md similarity index 100% rename from packages/choreo-gallery/notes/subdivision-browser-timing-test.md rename to packages/choreo-gallery/realm/design/subdivision-browser-timing-test.md diff --git a/packages/choreo-gallery/notes/sylva-one-world.md b/packages/choreo-gallery/realm/design/sylva-one-world.md similarity index 100% rename from packages/choreo-gallery/notes/sylva-one-world.md rename to packages/choreo-gallery/realm/design/sylva-one-world.md diff --git a/packages/choreo-gallery/notes/towers-about.md b/packages/choreo-gallery/realm/design/towers-about.md similarity index 95% rename from packages/choreo-gallery/notes/towers-about.md rename to packages/choreo-gallery/realm/design/towers-about.md index 39be0485600..4a1dcec24af 100644 --- a/packages/choreo-gallery/notes/towers-about.md +++ b/packages/choreo-gallery/realm/design/towers-about.md @@ -40,5 +40,5 @@ file. - The film: `/_towers` (front door, sound choice, player, cutting room) - The gallery face: `/towers` (the film in a frame, the deep dive below) -- The mechanics: `docs/choreo-splices.md`, `notes/towers-quality.md`, - `notes/towers-porting-delta.md` +- The mechanics: `../docs/choreo-splices.md`, `towers-quality.md`, + `towers-porting-delta.md` diff --git a/packages/choreo-gallery/notes/towers-porting-delta.md b/packages/choreo-gallery/realm/design/towers-porting-delta.md similarity index 99% rename from packages/choreo-gallery/notes/towers-porting-delta.md rename to packages/choreo-gallery/realm/design/towers-porting-delta.md index 84d872e376c..66c237f1e1b 100644 --- a/packages/choreo-gallery/notes/towers-porting-delta.md +++ b/packages/choreo-gallery/realm/design/towers-porting-delta.md @@ -183,7 +183,7 @@ ducks under the voice; the demo page's well takes the film's own aspect. Later the same cut: the narrative goes on every face — the gallery card, the door, the tile, the deep dive's opening and a director's statement -(`notes/towers-about.md`), the end card — _not a video: a live 3D composite +(`towers-about.md`), the end card — _not a video: a live 3D composite with motion graphics, interactive chapters, its own mixer, rendered in the browser at any size; entirely generated with AI, directed by a human_. The bar's chapters glyph is gone (the chapter name beside the clock is the way diff --git a/packages/choreo-gallery/notes/towers-quality.md b/packages/choreo-gallery/realm/design/towers-quality.md similarity index 99% rename from packages/choreo-gallery/notes/towers-quality.md rename to packages/choreo-gallery/realm/design/towers-quality.md index 82c5482aff7..1f0f9e2430a 100644 --- a/packages/choreo-gallery/notes/towers-quality.md +++ b/packages/choreo-gallery/realm/design/towers-quality.md @@ -120,7 +120,7 @@ scratchpad (`towers-profile.js`) and runs from the console. These are the improvements the film argues for in `packages/glimmer-motion` and its neighbours. None are built yet. -1. **A freeze-blend is a library step.** `docs/choreo-splices.md` names the +1. **A freeze-blend is a library step.** `../docs/choreo-splices.md` names the WebGL problem: a crossfade needs both frames, and a canvas has one. The answer that works is _the source keeps the freeze_ — the host asks for a dissolve by kind and duration and the glass does it. Choreo's junction diff --git a/packages/choreo-gallery/notes/towers-vo-files.md b/packages/choreo-gallery/realm/design/towers-vo-files.md similarity index 97% rename from packages/choreo-gallery/notes/towers-vo-files.md rename to packages/choreo-gallery/realm/design/towers-vo-files.md index d7998795772..6a3e9407393 100644 --- a/packages/choreo-gallery/notes/towers-vo-files.md +++ b/packages/choreo-gallery/realm/design/towers-vo-files.md @@ -7,8 +7,8 @@ does not load. Voice: **Calvin — Asian, Calm, British, Professional** - **In app:** `/asset/towers/vo/.mp3` - **On disk:** `test-app/public/asset/towers/vo/.mp3` -The lines are in `notes/towers-vo.md`, the timings in the beats themselves, and -the pronunciations in `notes/towers-vo-pronunciation.md`. This file is about +The lines are in `towers-vo.md`, the timings in the beats themselves, and +the pronunciations in `towers-vo-pronunciation.md`. This file is about the audio as audio. ## Level diff --git a/packages/choreo-gallery/notes/towers-vo-pronunciation.md b/packages/choreo-gallery/realm/design/towers-vo-pronunciation.md similarity index 97% rename from packages/choreo-gallery/notes/towers-vo-pronunciation.md rename to packages/choreo-gallery/realm/design/towers-vo-pronunciation.md index 5c0ee7b30a4..f104b9cd29b 100644 --- a/packages/choreo-gallery/notes/towers-vo-pronunciation.md +++ b/packages/choreo-gallery/realm/design/towers-vo-pronunciation.md @@ -1,6 +1,6 @@ # TOWERS — pronunciation layer for the voiceover -Companion to `notes/towers-vo.md`, which is generated from the beats and is the +Companion to `towers-vo.md`, which is generated from the beats and is the authority on WHAT is said and WHEN. This file is hand-authored and is the authority on HOW the foreign words are said. Nothing here changes a line's words or its window. @@ -54,7 +54,7 @@ before the release. Final `-e` is always "eh", never silent. ## Canonical read -The lines in `notes/towers-vo.md` are recorded verbatim. Only two carry a +The lines in `towers-vo.md` are recorded verbatim. Only two carry a foreign word, and both are respelled for the take: - **`azuchi`** — "Fifteen seventy-six. Seven gilded storeys over Lake bee-wah. @@ -79,7 +79,7 @@ air in it. **ADOPTED, 2026-09-01.** All ten alternates were recorded and are now the canonical `.mp3`; the takes that do not name the term are kept beside them as `-plain.mp3`. Each beat's `vo` in the component was updated to -match what is spoken, and `notes/towers-vo.md` regenerated from it. +match what is spoken, and `towers-vo.md` regenerated from it. One correction to the estimate above: the named takes did NOT all fit their existing windows. Four ran over — ishigaki by 1.5s, timber by 0.8s, shachi diff --git a/packages/choreo-gallery/notes/towers-vo.md b/packages/choreo-gallery/realm/design/towers-vo.md similarity index 99% rename from packages/choreo-gallery/notes/towers-vo.md rename to packages/choreo-gallery/realm/design/towers-vo.md index 1118ff26e8a..7406165cf61 100644 --- a/packages/choreo-gallery/notes/towers-vo.md +++ b/packages/choreo-gallery/realm/design/towers-vo.md @@ -19,7 +19,7 @@ in the read — the dry ones land harder delivered straight. Ten beats NAME their term aloud, once, as the first word, and never again in that beat: the screen is holding the kanji, and a narrator who repeats a word the audience is currently reading sounds like he does not trust -them. `notes/towers-vo-pronunciation.md` is the authority on how each is +them. `towers-vo-pronunciation.md` is the authority on how each is said, and the takes were prompted with its respellings. Do not chase the pictures. The camera is always moving. The voice should diff --git a/packages/choreo-gallery/notes/towers-wall-text.md b/packages/choreo-gallery/realm/design/towers-wall-text.md similarity index 100% rename from packages/choreo-gallery/notes/towers-wall-text.md rename to packages/choreo-gallery/realm/design/towers-wall-text.md diff --git a/packages/choreo-gallery/realm/docs/README.md b/packages/choreo-gallery/realm/docs/README.md new file mode 100644 index 00000000000..d6f99f4b542 --- /dev/null +++ b/packages/choreo-gallery/realm/docs/README.md @@ -0,0 +1,53 @@ +# Choreo documentation + +The long-form documentation for glimmer-motion, Choreo and choreo-player. Each +package's README is its reference: what it exports and how to install it. + +- [glimmer-motion](https://github.com/cardstack/boxel/tree/main/packages/glimmer-motion#readme): + the Motion engine bound to Glimmer, with `{{motion}}`, ``, + ``, drag and reorder +- [@cardstack/choreo](https://github.com/cardstack/boxel/tree/main/packages/choreo#readme): + ``, its vocabulary and design decisions, and `` +- [@cardstack/choreo-player](https://github.com/cardstack/boxel/tree/main/packages/choreo-player#readme): + the headless transport for clocking Choreo runs from outside + +## Start here + +- [guide.md](guide.md): Choreo from a Glimmer card. How you actually write + motion in a `.gts` file, in the order you meet the problems. + +## The choreography model + +- [choreography.md](choreography.md): the model. A region, its render pass's + changeset, and the timeline declared over it, plus the boxel-motion lineage + they descend from. +- [choreo-constructs.md](choreo-constructs.md): the full construct reference. +- [step-vocabulary.md](step-vocabulary.md): the open vocabulary. Anchors, + composite steps and derived values. +- [nested-choreo.md](nested-choreo.md): regions inside regions, and far + matching between them. +- [choreo-splices.md](choreo-splices.md): cuts as a first-class part of a score. + +## Space and film + +- [planes-and-cameras.md](planes-and-cameras.md) and [dom-in-3d.md](dom-in-3d.md): + a `` region as a plane in a three.js scene. +- [film.md](film.md): the `` construct, as built. + +## External clocks + +- [demo-recording.md](demo-recording.md): keeping a demo correct both when + someone pokes at it and when a recorder clocks it frame by frame. + +## Post-mortem + +- [postmortem-follow.md](postmortem-follow.md): the engineering post-mortem that + shaped the derived-value API. It is kept because the mistakes teach more than + the result. + +## Design history + +[The design notes](../design/README.md) are the workshop behind these documents: +design records, handoffs, measurements, and the films' scripts. They are drafts +by definition. Some describe things that were proposed and never built, or +built and then changed. diff --git a/packages/choreo-gallery/docs/choreo-constructs.md b/packages/choreo-gallery/realm/docs/choreo-constructs.md similarity index 99% rename from packages/choreo-gallery/docs/choreo-constructs.md rename to packages/choreo-gallery/realm/docs/choreo-constructs.md index 39fa81bc411..5826a59bff5 100644 --- a/packages/choreo-gallery/docs/choreo-constructs.md +++ b/packages/choreo-gallery/realm/docs/choreo-constructs.md @@ -4,7 +4,7 @@ | -------------- | ------------------------------------------------------------------------- | | **Status** | Implemented — `choreo/constructs`; demo coverage follows (§7) | | **Start date** | 2026-08-25 | -| **Package** | `glimmer-motion` (the Choreo layer) | +| **Package** | `@cardstack/choreo` | | **Drivers** | frameloop ✅ · native WAAPI/CSS realm target ☐ (§6.2) | | **Related** | [choreography.md](choreography.md) · [nested-choreo.md](nested-choreo.md) | @@ -53,7 +53,7 @@ inspector — `with`/`after` relations, delays, durations, by-word and by-character delivery, a scrubbable playhead — and the language now carries every one of those semantics natively. The demo pass (§7) finishes the thought: the inspector becomes a thin surface over a real -timeline, and its private sampled score in `test-app/app/lib/builds.ts` +timeline, and its private sampled score in the gallery's `lib/builds.ts` is deleted. ### 2.1 The Keynote map @@ -166,7 +166,7 @@ holds, else the earliest future one. Under `@exact`, a child whose score integrates (a spring, a follow) is refused once, loudly, and never driven: an integrator's state is its history, and a driven clock has none. This is the first of the two constructs the film graph needs -(docs/film-graph/CONSTRUCTS.md); `` uses it for the type, the +([film-graph/CONSTRUCTS.md](../design/film-graph/CONSTRUCTS.md)); `` uses it for the type, the insert and the clip, and it is what makes a whole film a pure function of one number. @@ -1038,7 +1038,7 @@ fits first — and the pan that solves `x + P = centre`, all from the changeset's rest-layout boxes divided back by `measureZoom`. That is the measurement space FLIP already uses, so a dive begun mid-flight on a different tile is correct by construction -(`notes/camera-api-wishlist.md` is the case study). And a cue with no +([camera-api-wishlist.md](../design/camera-api-wishlist.md) is the case study). And a cue with no origin of its own holds the aim point in force rather than recentring — backing out of a dive backs straight out of its tile, instead of sliding across the neighbouring one mid-flight. diff --git a/packages/choreo-gallery/docs/choreo-splices.md b/packages/choreo-gallery/realm/docs/choreo-splices.md similarity index 99% rename from packages/choreo-gallery/docs/choreo-splices.md rename to packages/choreo-gallery/realm/docs/choreo-splices.md index 4caead5936b..73152bd3f2e 100644 --- a/packages/choreo-gallery/docs/choreo-splices.md +++ b/packages/choreo-gallery/realm/docs/choreo-splices.md @@ -196,7 +196,7 @@ they cost one idle element for a few hundred milliseconds. ## What a splice means to the smoother (2026-09-04) `c.Camera3D @settle` filters the path by averaging it — see §4.2a of -`docs/film.md`. An average is exactly the machinery this note exists to +[film.md](film.md). An average is exactly the machinery this note exists to keep away from a seam, so the window is tapered to the shot the clock is in: it shrinks to the room it has and reaches zero at the cut itself. A seam therefore stays a step function no matter how much hand is asked diff --git a/packages/choreo-gallery/docs/choreography.md b/packages/choreo-gallery/realm/docs/choreography.md similarity index 97% rename from packages/choreo-gallery/docs/choreography.md rename to packages/choreo-gallery/realm/docs/choreography.md index 7d6f133a0d8..024ca3acb2e 100644 --- a/packages/choreo-gallery/docs/choreography.md +++ b/packages/choreo-gallery/realm/docs/choreography.md @@ -209,7 +209,7 @@ timeline the legacy could only print as a matrix. completes when its row ends - **interruption** — a second dirty pass cancels timers, releases holds, and does not strand orphans (a soak over the gallery demos, so it lives in - `packages/choreo-test-app/tests/integration/choreo/`) + the host suite, `packages/host/tests/integration/choreo-gallery-interruption-test.gts`) - **counterpart** — an inserted id matching a removed one carries the old element as `counterpart` @@ -236,6 +236,6 @@ sequence repeat or extend a run's finite transport duration. Add a finite timing window when the run must be seekable. Playback, text delivery and numeric still sampling honor these offsets. -The [Circle continuity guide](../test-app/app/content/guides/interactive-circle-loop.md) -combines a parent zoom with stable circles that rearrange locally. Its playground -is `/playground/circle-loop`; the same live example also appears in the 3D gallery and full tour. +The [Circle continuity demo](../demos/circle-loop) combines a parent zoom with +stable circles that rearrange locally, and its lesson links the guide that +walks through it. diff --git a/packages/choreo-gallery/docs/demo-recording.md b/packages/choreo-gallery/realm/docs/demo-recording.md similarity index 99% rename from packages/choreo-gallery/docs/demo-recording.md rename to packages/choreo-gallery/realm/docs/demo-recording.md index 3c6d763c3b0..8deda42fbdc 100644 --- a/packages/choreo-gallery/docs/demo-recording.md +++ b/packages/choreo-gallery/realm/docs/demo-recording.md @@ -119,7 +119,7 @@ and nothing else. - **Derived values are fine, and are the good answer.** A `c.Follow` is reproducible precisely because it is a pure function of the pass's measurements and the run's clock — it never reads the live page at all - (docs/postmortem-follow.md). + ([postmortem-follow.md](postmortem-follow.md)). ## Things that pause themselves diff --git a/packages/choreo-gallery/docs/dom-in-3d.md b/packages/choreo-gallery/realm/docs/dom-in-3d.md similarity index 99% rename from packages/choreo-gallery/docs/dom-in-3d.md rename to packages/choreo-gallery/realm/docs/dom-in-3d.md index 520e6049d32..7ccaf97c7cb 100644 --- a/packages/choreo-gallery/docs/dom-in-3d.md +++ b/packages/choreo-gallery/realm/docs/dom-in-3d.md @@ -8,7 +8,7 @@ remain design. ## What exists -`test-app/app/lib/css3d.ts` is **85 lines**: three pure functions over a +`packages/choreo-film-app/app/lib/css3d.ts` is **85 lines**: three pure functions over a 4×4 matrix. ```ts @@ -254,7 +254,7 @@ explicitly. `c.Camera3D` is a step, not a loop — the pose is a pure function of the run's time, the host merely applies what it is handed. So a 3D shot seeks like any other score: `run.time`, a gate, or `choreo-player` clocking it from outside -([docs/demo-recording.md](docs/demo-recording.md)). Nothing in the 85 +([demo-recording.md](demo-recording.md)). Nothing in the 85 lines runs a rAF of its own, and nothing accumulates, so frame _t_ is a function of _t_ on a worker page too. diff --git a/packages/choreo-gallery/docs/film.md b/packages/choreo-gallery/realm/docs/film.md similarity index 99% rename from packages/choreo-gallery/docs/film.md rename to packages/choreo-gallery/realm/docs/film.md index 111e2ac0a67..3ab2bda02be 100644 --- a/packages/choreo-gallery/docs/film.md +++ b/packages/choreo-gallery/realm/docs/film.md @@ -6,7 +6,7 @@ > than replacing them. For the design record (why it exists, the two > reference films, the duplication that was measured, the fork between a > seekable film and a chased one) see -> [film-construct.md](film-construct.md). +> [film-construct.md](../design/film-construct.md). ```ts import { Film } from '@cardstack/choreo/film'; @@ -269,7 +269,7 @@ actually is; they were one thing for a while and should not have been. One limit worth knowing before you plan around it: **a beat carries one clip**. Two `f.Inset` children on the same shot compile to one row and the second wins. Lifting that is the insert-track question in -`docs/film-graph/GAPS.md`. +[film-graph/GAPS.md](../design/film-graph/GAPS.md). --- @@ -388,14 +388,14 @@ That is what makes the film **measurable**. `schedule(beats, chapters, defaultJoin)` returns the whole thing as one object — every beat's start, the cue table, the waypoints, the chapters — and two things pin it: -- `scripts/film-fixtures.mjs` writes it for each reference film to - `test-app/tests/fixtures/film/{sagrada,towers}.json`, headless, on +- `scripts/film-fixtures.mjs` in `packages/choreo-film-app` writes it for each reference film to + `tests/fixtures/film/{sagrada,towers}.json`, headless, on Node's own type stripping (`node scripts/film-fixtures.mjs`; `--check` fails when a fixture would change). It can do that because the films' data is plain TypeScript with no Ember in it: - `test-app/app/lib/films/{sagrada,towers}.ts` — the shot list, the + `app/lib/films/{sagrada,towers}.ts` — the shot list, the chapters, the measured reads, the geometry sampled off the model. -- `test-app/tests/unit/film-schedule-test.ts` recomputes the schedule in +- `tests/unit/film-schedule-test.ts` recomputes the schedule in the browser and asserts it equals the fixture to the digit. The rule that follows: **the cue table and the camera path are the diff --git a/packages/choreo-gallery/docs/guide.md b/packages/choreo-gallery/realm/docs/guide.md similarity index 93% rename from packages/choreo-gallery/docs/guide.md rename to packages/choreo-gallery/realm/docs/guide.md index c256b2e32c9..25d3c4bda00 100644 --- a/packages/choreo-gallery/docs/guide.md +++ b/packages/choreo-gallery/realm/docs/guide.md @@ -1,6 +1,6 @@ # Choreo, from a Glimmer card -The [README](../README.md) is a fidelity document: it says what Motion does and where each piece +The [glimmer-motion README](https://github.com/cardstack/boxel/tree/main/packages/glimmer-motion#readme) is a fidelity document: it says what Motion does and where each piece went. This is the other thing — how you actually write motion in a `.gts` file, in the order you meet the problems. @@ -174,8 +174,8 @@ A removed element is kept alive, locked where it stood, for exactly as long as i timeline lasts — then dropped. You do not manage that. A `` inside another `` is a separate scene: the outer one does not see the inner -one's participants, and does not collect its steps. [docs/choreography.md](choreography.md) is the -full design; [docs/nested-choreo.md](nested-choreo.md) is the nesting model. +one's participants, and does not collect its steps. [choreography.md](choreography.md) is the +full design; [nested-choreo.md](nested-choreo.md) is the nesting model. --- @@ -335,7 +335,8 @@ own settings UI. ## Where to go next -- [README](../README.md) — the whole API surface, and the React translation table -- [docs/choreography.md](choreography.md) — ``'s design, and the boxel-motion lineage -- [docs/nested-choreo.md](nested-choreo.md) — regions, nesting, and far matching -- `test-app` — every example in the gallery is a real component you can read +- [glimmer-motion's README](https://github.com/cardstack/boxel/tree/main/packages/glimmer-motion#readme) — the whole API surface, and the React translation table +- [Choreo's README](https://github.com/cardstack/boxel/tree/main/packages/choreo#readme) — the region, its vocabulary and its design decisions +- [choreography.md](choreography.md) — ``'s design, and the boxel-motion lineage +- [nested-choreo.md](nested-choreo.md) — regions, nesting, and far matching +- [the gallery](../index) — every demo is a real component you can read diff --git a/packages/choreo-gallery/docs/nested-choreo.md b/packages/choreo-gallery/realm/docs/nested-choreo.md similarity index 93% rename from packages/choreo-gallery/docs/nested-choreo.md rename to packages/choreo-gallery/realm/docs/nested-choreo.md index b0b52c1dcac..6b4aae1cbbe 100644 --- a/packages/choreo-gallery/docs/nested-choreo.md +++ b/packages/choreo-gallery/realm/docs/nested-choreo.md @@ -79,7 +79,7 @@ same render, they play side by side. There is no shared clock. ## What nesting does not do - **No shared timeline across the boundary.** Far matching (below in - spirit, `src/choreo/far.ts` in fact) pairs an inserted id in one region + spirit, `packages/choreo/src/far.ts` in fact) pairs an inserted id in one region with a removed id in another so the receiver flies from the sender's box — but each region still compiles and plays its own timeline. - **No shared clock.** The shell's `Sequence` does not wait for the @@ -232,12 +232,12 @@ the high-risk path and is out of scope. ## Status -| Piece | State | -| -------------------------------------------------- | ----------------------------------------------- | -| Nearest-ancestor host (`closestChoreo`) | done | -| `collect()` skips nested `[data-choreo]` | done | -| Isolated snapshot / changeset / orphans / timeline | done | -| Far-matching across regions | done — `src/choreo/far.ts`, the Far match demo | -| `{{beacon}}`, `c.beacon`, `changeset.beacon` | done — `src/beacon.ts`, `src/choreo/beacons.ts` | -| `Move` `@from` / `@to` rewriting initial / final | done — `compile.ts` `resolveMove` | -| App-global beacon registry | done — the version the Beacons demo needs | +| Piece | State | +| -------------------------------------------------- | ------------------------------------------------------------------------ | +| Nearest-ancestor host (`closestChoreo`) | done | +| `collect()` skips nested `[data-choreo]` | done | +| Isolated snapshot / changeset / orphans / timeline | done | +| Far-matching across regions | done — `packages/choreo/src/far.ts`, the Far match demo | +| `{{beacon}}`, `c.beacon`, `changeset.beacon` | done — `packages/choreo/src/beacon.ts`, `packages/choreo/src/beacons.ts` | +| `Move` `@from` / `@to` rewriting initial / final | done — `compile.ts` `resolveMove` | +| App-global beacon registry | done — the version the Beacons demo needs | diff --git a/packages/choreo-gallery/docs/planes-and-cameras.md b/packages/choreo-gallery/realm/docs/planes-and-cameras.md similarity index 98% rename from packages/choreo-gallery/docs/planes-and-cameras.md rename to packages/choreo-gallery/realm/docs/planes-and-cameras.md index 7438a1e7665..0901e582a2c 100644 --- a/packages/choreo-gallery/docs/planes-and-cameras.md +++ b/packages/choreo-gallery/realm/docs/planes-and-cameras.md @@ -1,7 +1,7 @@ # Planes and cameras, hand in hand **Status:** design — nothing here is implemented beyond what "Standing -today" lists. Parent documents: [choreo-composition.md](choreo-composition.md) +today" lists. Parent documents: [choreo-composition.md](../design/choreo-composition.md) (the plane concept and its review gates), [nested-choreo.md](nested-choreo.md) (regions and far matching), [demo-recording.md](demo-recording.md) (what any of this owes an external clock). @@ -26,7 +26,7 @@ BETWEEN planes without relayout. resolved against the pose in force at cue start — which is precisely what makes multi-plane direction composable: a plane's pulses stack on its own state with no absolute coupling to any other plane's score. -- Far matching (`src/choreo/far.ts`) pairs one bare id across regions at +- Far matching (`packages/choreo/src/far.ts`) pairs one bare id across regions at a render-pass barrier: MEASURE → MATCH → RUN with no frame painted between phases. The receiver flies from the sender's PAGE box as a `kept` sprite with a counterpart; the sender is dropped, not orphaned. diff --git a/packages/choreo-gallery/docs/postmortem-follow.md b/packages/choreo-gallery/realm/docs/postmortem-follow.md similarity index 100% rename from packages/choreo-gallery/docs/postmortem-follow.md rename to packages/choreo-gallery/realm/docs/postmortem-follow.md diff --git a/packages/choreo-gallery/docs/step-vocabulary.md b/packages/choreo-gallery/realm/docs/step-vocabulary.md similarity index 98% rename from packages/choreo-gallery/docs/step-vocabulary.md rename to packages/choreo-gallery/realm/docs/step-vocabulary.md index e573b43859f..8a2e15f3213 100644 --- a/packages/choreo-gallery/docs/step-vocabulary.md +++ b/packages/choreo-gallery/realm/docs/step-vocabulary.md @@ -177,7 +177,7 @@ part is not done. ## Part 3 — `c.Follow` and the derived cue > **Landed — twice.** The first shipped design handed `@read` live boxes -> and paid for it five separate ways (docs/postmortem-follow.md); what +> and paid for it five separate ways ([postmortem-follow.md](postmortem-follow.md)); what > stands now is the postmortem's replacement: **the changeset is the > context**. `@read` receives only what the pass measured — each source's > resting boxes and its composed position this frame, plus the follower's @@ -273,7 +273,7 @@ drawn, not the frame last painted: a rect read from the page mid-FLIP is a whole bay stale on frame one, because the layout has already jumped to the destination while the transform still has to carry it back. The first design corrected for that staleness per frame and found four distinct -ways for the correction to be wrong (docs/postmortem-follow.md); this one +ways for the correction to be wrong ([postmortem-follow.md](postmortem-follow.md)); this one never measures, so there is nothing to correct. And the write is _rendered_, not scheduled: a scheduled render lands a hop later than the synchronous pin the move it follows just did, which is a visible diff --git a/packages/choreo-gallery/docs/licenses/lucide-icons.txt b/packages/choreo-gallery/realm/licenses/lucide-icons.txt similarity index 100% rename from packages/choreo-gallery/docs/licenses/lucide-icons.txt rename to packages/choreo-gallery/realm/licenses/lucide-icons.txt diff --git a/packages/choreo-gallery/realm/notes/towers.gts b/packages/choreo-gallery/realm/notes/towers.gts index 83735c6ae57..eedd9f39895 100644 --- a/packages/choreo-gallery/realm/notes/towers.gts +++ b/packages/choreo-gallery/realm/notes/towers.gts @@ -239,7 +239,7 @@ const TowersNotes: TOC<{ still-based join possible. The full design note lives at docs/choreo-splices.md; the quality pass and its numbers in - notes/towers-quality.md. + design/towers-quality.md.

diff --git a/packages/choreo-gallery/realm/reel/compositor.ts b/packages/choreo-gallery/realm/reel/compositor.ts index fa80ffb65e2..ead0e1d9005 100644 --- a/packages/choreo-gallery/realm/reel/compositor.ts +++ b/packages/choreo-gallery/realm/reel/compositor.ts @@ -1,5 +1,5 @@ /** - * The C1 composition host (notes/choreo-composition.md, Phase C1) — + * The C1 composition host (design/choreo-composition.md, Phase C1) — * application-side code on purpose. It owns the scene-level model a * directed composition needs and consumes only public surfaces: * `choreo-player` for the run transport, and actor "ports" the @@ -66,7 +66,7 @@ export interface ActorPorts { /** * A clip: an activation and a source-time window over one actor - * (notes/choreo-composition.md, Phase C3). Pure parent→source arithmetic: + * (design/choreo-composition.md, Phase C3). Pure parent→source arithmetic: * * source = sourceIn + (parent − at) × rate * window = (sourceOut − sourceIn) / rate seconds of parent time @@ -208,7 +208,7 @@ export class Compositor { * Preview passes `parameters: false`: a parameter channel is capture * reconstruction — it stands a demo's own engine at a still, which * would fight the very playback the preview exists to show. play() is - * GPU; renderAt(t) is a still (notes/choreo-composition.md, scope + * GPU; renderAt(t) is a still (design/choreo-composition.md, scope * decisions). */ async foldTo( diff --git a/packages/choreo-gallery/realm/reel/feature-reel.gts b/packages/choreo-gallery/realm/reel/feature-reel.gts index 039dd0a2dfc..9cd2d076313 100644 --- a/packages/choreo-gallery/realm/reel/feature-reel.gts +++ b/packages/choreo-gallery/realm/reel/feature-reel.gts @@ -75,7 +75,7 @@ export class FeatureReel extends Component { private ready = new Promise((resolve) => (this.resolveReady = resolve)); /** - * The composition host (notes/choreo-composition.md, Phase C1). Cues are + * The composition host (design/choreo-composition.md, Phase C1). Cues are * idempotent semantic commands folded through the composition clock in * BOTH modes; parameter channels are capture-only reconstruction — in * preview the demos play their own engines. @@ -329,7 +329,7 @@ export class FeatureReel extends Component { /** * Live preview: the runs PLAY — play() is GPU, renderAt(t) is a still - * (notes/choreo-composition.md, scope decisions). This clock only fires + * (design/choreo-composition.md, scope decisions). This clock only fires * cues through the same fold capture uses, and retimes a newly compiled * replacement run to the composition clock ONCE; it never pauses or * seeks a run that is already playing. @@ -539,7 +539,7 @@ export class FeatureReel extends Component { {{! the natural authoring contract: a move, then a wait. Every wait here once had to be a constant-easing camera duplicate so a random-access seek would land on the pose — the workaround - notes/external-clock-camera-seek-handoff.md records. The score + design/external-clock-camera-seek-handoff.md records. The score staying Wait-based IS the proof the transport reconstructs. }} diff --git a/packages/choreo-gallery/realm/stages/fold.gts b/packages/choreo-gallery/realm/stages/fold.gts index a73493f722e..5bcfa023eab 100644 --- a/packages/choreo-gallery/realm/stages/fold.gts +++ b/packages/choreo-gallery/realm/stages/fold.gts @@ -86,7 +86,7 @@ interface Firing { const COLD: Firing = { cone: false, damper: false, gas: 0, soak: false }; /** - * `` — commands as part of the score (notes/choreo-composition.md §C4). + * `` — commands as part of the score (design/choreo-composition.md §C4). * * The law is one sentence: THE SET OF COMMANDS AT OR BEFORE THE CLOCK IS THE * COMMANDED STATE. Everything here exists to make that visible, because it is diff --git a/packages/choreo-gallery/docs/api-inventory.json b/packages/choreo-gallery/tools/api-inventory.json similarity index 100% rename from packages/choreo-gallery/docs/api-inventory.json rename to packages/choreo-gallery/tools/api-inventory.json diff --git a/packages/choreo-gallery/tools/check-realm.mjs b/packages/choreo-gallery/tools/check-realm.mjs index 963e540f1a8..8030026b3ba 100644 --- a/packages/choreo-gallery/tools/check-realm.mjs +++ b/packages/choreo-gallery/tools/check-realm.mjs @@ -10,6 +10,10 @@ * - Every guide meets the prose floor and has a preamble, the `-start` * guides state their motivation and goals, and internal guide links * resolve. + * - The docs and design records in the realm link only to files and + * headings that exist; every import written in them, in the guides and in + * the package READMEs names an export its package has; and every + * choreo.boxel.site URL names a file in the realm. * * Reads sources and JSON only, so it needs no build. */ @@ -52,7 +56,7 @@ function source(file) { // ---- the API inventory --------------------------------------------------- const inventory = readJSON( - path.join(root, 'packages/choreo-gallery/docs/api-inventory.json'), + path.join(root, 'packages/choreo-gallery/tools/api-inventory.json'), ); const files = { 'glimmer-motion': 'packages/glimmer-motion/src/index.ts', @@ -115,7 +119,7 @@ assert.equal( assert.deepEqual( [...listed].sort(), [...actual].sort(), - 'API exports changed: review and update packages/choreo-gallery/docs/api-inventory.json', + 'API exports changed: review and update packages/choreo-gallery/tools/api-inventory.json', ); // ---- the guides ---------------------------------------------------------- @@ -317,3 +321,255 @@ for (const { id, ...lesson } of testAppLessons) { console.log( `${testAppLessons.length} test-app lessons cover its catalog and match the realm's.`, ); + +// ---- the docs ------------------------------------------------------------ + +function markdownFiles(dir) { + return fs + .readdirSync(dir, { recursive: true }) + .filter((file) => file.endsWith('.md')) + .map((file) => path.join(dir, file)); +} +const relative = (file) => path.relative(root, file); +/** fenced code, which may quote link or import syntax it doesn't mean */ +const withoutFences = (markdown) => + markdown.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, ''); + +/** the ids GitHub (and the host's renderer) give a document's headings */ +function headingIds(markdown) { + const ids = new Set(); + const seen = new Map(); + for (const [, text] of withoutFences(markdown).matchAll( + /^#{1,6}\s+(.*?)\s*#*\s*$/gm, + )) { + // a code span keeps its text as written, `` and `/_sylva` included; + // outside one, tags, link targets and emphasis underscores drop out + const base = text + .split(/(`[^`]*`)/) + .map((part, i) => + i % 2 + ? part.slice(1, -1) + : part + .replace(/<[^>]+>/g, '') + .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') + .replace(/(? path.join(root, 'packages', pkg, 'README.md')); +const brokenLinks = []; +for (const file of [...realmDocs, ...packageReadmes]) { + // a realm doc is published with the realm, so it may link only inside it + const inRealm = file.startsWith(realm + path.sep); + const markdown = fs.readFileSync(file, 'utf8'); + for (const [, target] of withoutFences(markdown).matchAll( + /\]\(\s*]+)>?(?:\s+"[^"]*")?\s*\)/g, + )) { + if (/^[a-z][a-z+.-]*:/i.test(target)) { + continue; + } + const [pathPart, fragment] = target.split('#'); + const resolved = pathPart + ? path.resolve(path.dirname(file), decodeURIComponent(pathPart)) + : file; + // an extensionless link is a card: `../demos/fold` is demos/fold.json + const exists = fs.existsSync(resolved) || fs.existsSync(`${resolved}.json`); + if (inRealm && (!resolved.startsWith(realm + path.sep) || !exists)) { + brokenLinks.push(`${relative(file)}: ${target} is not a realm file`); + } else if (!exists) { + brokenLinks.push(`${relative(file)}: ${target} names no file`); + } else if ( + fragment && + resolved.endsWith('.md') && + !headingIds(fs.readFileSync(resolved, 'utf8')).has( + decodeURIComponent(fragment), + ) + ) { + brokenLinks.push(`${relative(file)}: ${target} names no heading`); + } + } +} +assert.deepEqual(brokenLinks, [], 'Broken links in the docs'); + +/* + * Imports, wherever the docs show one. Choreo and Film moved out of + * glimmer-motion into @cardstack/choreo, so a name imported from a package + * has to be one that package exports today. + */ +const packageSources = { + 'glimmer-motion': 'packages/glimmer-motion/src', + '@cardstack/choreo': 'packages/choreo/src', + '@cardstack/choreo-player': 'packages/choreo-player/src', +}; +const retiredSpecifiers = new Set(['choreo', 'choreo-player']); +function moduleFile(specifier) { + const name = Object.keys(packageSources) + .sort((a, b) => b.length - a.length) + .find((pkg) => specifier === pkg || specifier.startsWith(`${pkg}/`)); + if (!name) { + return undefined; + } + const base = path.join( + root, + packageSources[name], + specifier.slice(name.length + 1) || 'index', + ); + return [ + `${base}.ts`, + `${base}.gts`, + path.join(base, 'index.ts'), + path.join(base, 'index.gts'), + ].find((candidate) => fs.existsSync(candidate)); +} +function exportsOf(file) { + const tree = ts.createSourceFile( + file, + fs.readFileSync(file, 'utf8'), + ts.ScriptTarget.Latest, + true, + ); + const names = new Set(); + for (const node of tree.statements) { + if (ts.isExportDeclaration(node) && !node.exportClause) { + return undefined; // `export *`: anything may come through + } + if ( + ts.isExportDeclaration(node) && + node.exportClause && + ts.isNamedExports(node.exportClause) + ) { + for (const item of node.exportClause.elements) { + names.add(item.name.text); + } + } else if ( + node.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword, + ) + ) { + if (node.name) { + names.add(node.name.text); + } + if (ts.isVariableStatement(node)) { + for (const item of node.declarationList.declarations) { + names.add(item.name.getText(tree)); + } + } + } + } + return names; +} +const exportCache = new Map(); +const importedDocs = [ + ...realmDocs, + ...markdownFiles(guidesDir), + ...packageReadmes, +]; +const badImports = []; +for (const file of importedDocs) { + const markdown = fs.readFileSync(file, 'utf8'); + for (const [statement, clause, specifier] of markdown.matchAll( + /\bimport\s+(?:type\s+)?([\w$]+\s*,\s*\{[^}]*\}|\{[^}]*\}|[\w$]+|\*\s+as\s+[\w$]+)\s+from\s+['"]([^'"]+)['"]/g, + )) { + const where = `${relative(file)}: ${statement.replace(/\s+/g, ' ')}`; + if (retiredSpecifiers.has(specifier)) { + badImports.push(`${where} — no such package`); + continue; + } + const isOurs = Object.keys(packageSources).some( + (pkg) => specifier === pkg || specifier.startsWith(`${pkg}/`), + ); + if (!isOurs) { + continue; + } + const module = moduleFile(specifier); + if (!module) { + badImports.push(`${where} — no such module`); + continue; + } + if (!exportCache.has(module)) { + exportCache.set(module, exportsOf(module)); + } + const exported = exportCache.get(module); + const named = clause.match(/\{([^}]*)\}/)?.[1] ?? ''; + for (const item of named.split(',')) { + const name = item + .trim() + .replace(/^type\s+/, '') + .split(/\s+as\s+/)[0]; + if (name && exported && !exported.has(name)) { + badImports.push(`${where} — ${specifier} has no export ${name}`); + } + } + } +} +assert.deepEqual(badImports, [], 'Imports the packages do not provide'); + +/** the hosted gallery is this realm, so its URLs name realm files */ +const siteURLs = []; +for (const file of importedDocs) { + const markdown = fs.readFileSync(file, 'utf8'); + for (const [url, pathname] of markdown.matchAll( + /https:\/\/choreo\.boxel\.site\/([^\s)"'<>`#?]*)/g, + )) { + const target = path.join(realm, decodeURIComponent(pathname)); + const exists = + pathname === '' || + fs.existsSync(target) || + fs.existsSync(`${target}.json`); + if (!exists) { + siteURLs.push(`${relative(file)}: ${url}`); + } + } +} +assert.deepEqual( + siteURLs, + [], + 'choreo.boxel.site URLs that name no realm file', +); + +/* + * Links into the monorepo name files that exist, and the docs (the design + * history aside, which records where things were) don't send a reader to + * Choreo's archived repository. + */ +const repoLinks = []; +const designDir = path.join(realm, 'design') + path.sep; +for (const file of importedDocs) { + const markdown = fs.readFileSync(file, 'utf8'); + for (const [url, repoPath] of markdown.matchAll( + /https:\/\/github\.com\/cardstack\/boxel\/(?:blob|tree)\/main\/([^\s)"'<>`#?]+)/g, + )) { + if (!fs.existsSync(path.join(root, repoPath))) { + repoLinks.push(`${relative(file)}: ${url} names no file`); + } + } + if (!file.startsWith(designDir)) { + for (const [url] of markdown.matchAll( + /https:\/\/github\.com\/cardstack\/choreo[^\s)"'<>`]*/g, + )) { + repoLinks.push(`${relative(file)}: ${url} is the archived repository`); + } + } +} +assert.deepEqual(repoLinks, [], 'Links to repository files that are gone'); +console.log( + `${realmDocs.length} realm docs link only to files and headings that exist; imports, choreo.boxel.site URLs and repository links in ${importedDocs.length} docs resolve.`, +); diff --git a/packages/choreo-player/README.md b/packages/choreo-player/README.md index 3a49e90208d..3222963e5bb 100644 --- a/packages/choreo-player/README.md +++ b/packages/choreo-player/README.md @@ -1,8 +1,10 @@ -# @cardstack/choreo-player +# @cardstack/choreo-player A dependency-free, headless transport for externally clocking public Choreo -runs. It builds around `@cardstack/choreo`; it does not patch Choreo, emulate a -GSAP timeline, or bundle a renderer. +runs. It builds around [`@cardstack/choreo`](https://github.com/cardstack/boxel/tree/main/packages/choreo#readme); +it does not patch Choreo, emulate a GSAP timeline, or bundle a renderer. It +ships no UI, demo state reducers, DOM discovery, generated media, or +HyperFrames itself. ## Install @@ -40,6 +42,11 @@ await player.play(); player.pause(); ``` +`runs()` is deliberately not a global registry. A recorder must not touch an +interactive run merely because both exist on the same page. The external clock +arms ownership through `prepare()` on its first call and keeps it for the +page's recording session. + The run type is deliberately structural: ```ts @@ -52,6 +59,9 @@ interface ChoreoRun { } ``` +That is the whole surface the player uses, and the `ChoreoRun` a `` +region yields satisfies it without an adapter. + `renderAt()` pauses every owned run, sets speed and absolute time, and waits for the configured host settlement. Its requests are latest-wins while an asynchronous `prepare()` is pending. `sync()` applies the current transport @@ -77,9 +87,16 @@ async function renderFrameAt(time) { } ``` +The first seek makes a newly mounted score and its compiled cues available. +The state fold makes the application a function of time. The awaited Glimmer +pass commits that state, and the final seek reasserts the Choreo still on the +DOM that will be captured. + The exact state fold and Glimmer render barrier belong to the application; -the package cannot infer them safely. See `docs/demo-recording.md` in the -Choreo repository for the complete ownership and determinism checklist. +the package cannot infer them safely. +[Demos that record](https://choreo.boxel.site/docs/demo-recording.md) is the +complete ownership and determinism checklist: every rule in it is a bug that +shipped once. ## HyperFrames @@ -90,6 +107,11 @@ CLI version in the video project that performs the rendering: pnpm add --save-dev hyperframes@0.8.14 ``` +Run the pinned binary from the project's scripts, such as +`hyperframes render build --workers 10`. Don't install HyperFrames globally, +declare it as a peer, or run an unpinned `npx --yes hyperframes` in a durable +composition. + Bind the application's complete frame transaction to HyperFrames' native awaited seek event: diff --git a/packages/choreo-test-app/app/components/dial-controls.gts b/packages/choreo-test-app/app/components/dial-controls.gts index 39c2d2f26c7..1f11fb4e58b 100644 --- a/packages/choreo-test-app/app/components/dial-controls.gts +++ b/packages/choreo-test-app/app/components/dial-controls.gts @@ -21,7 +21,7 @@ import type { AnyDial } from 'test-app/lib/dial'; * want closed until the day you care about it. So this walks the tree, renders * a folder as a folder, and recurses. * - * ## A correction to notes/drift.md + * ## A correction to packages/choreo-gallery/realm/design/drift.md * * That note says a spring pair should be one `type: 'spring'` control rather * than two sliders, and calls it cheap once folders exist. It is not cheap. The diff --git a/packages/choreo-test-app/app/components/examples/drift.gts b/packages/choreo-test-app/app/components/examples/drift.gts index 6ba8146072b..051d4cf058a 100644 --- a/packages/choreo-test-app/app/components/examples/drift.gts +++ b/packages/choreo-test-app/app/components/examples/drift.gts @@ -53,7 +53,7 @@ import { preventSelect } from 'test-app/lib/pointer'; * * ## What is Choreo here, and what is deliberately not * - * The driving loop is not a score, and `notes/drift.md` is written mostly to say + * The driving loop is not a score, and `packages/choreo-gallery/realm/design/drift.md` is written mostly to say * so before anyone builds this and discovers it halfway in. Motion's springs * are scalar interpolators toward a target; a drift is a velocity decomposition * with a friction coefficient on the sideways half. So `lib/drift.ts` is fifty @@ -79,7 +79,7 @@ import { preventSelect } from 'test-app/lib/pointer'; * * ## The skid marks are a canvas, and that is also on purpose * - * `notes/drift.md` wanted them as `c.inserted` / `c.removed` over a list nobody + * `packages/choreo-gallery/realm/design/drift.md` wanted them as `c.inserted` / `c.removed` over a list nobody * clicked to build. They are a canvas instead. A mark is laid every thirteen * world pixels of slide, which at speed is twenty a second, and a changeset * twenty times a second is not a changeset — it is a render loop wearing one. diff --git a/packages/choreo-test-app/app/components/examples/escort.gts b/packages/choreo-test-app/app/components/examples/escort.gts index 9ad0b6d3194..b04ed21e593 100644 --- a/packages/choreo-test-app/app/components/examples/escort.gts +++ b/packages/choreo-test-app/app/components/examples/escort.gts @@ -26,7 +26,7 @@ import { tuneSeconds, tuneSpring } from 'test-app/lib/demo-tuning'; * holds the card THAT frame — so it is right on frames nobody planned. * And it never reads the page to find out: the resting boxes come from * the pass's own measurements, the card's position from the values the - * run is already driving (docs/postmortem-follow.md). + * run is already driving (packages/choreo-gallery/realm/docs/postmortem-follow.md). */ /* ── the score's words ───────────────────────────────────────────────── */ diff --git a/packages/choreo-test-app/app/components/examples/fold.gts b/packages/choreo-test-app/app/components/examples/fold.gts index e1a94fac21d..c6f4ce95b5e 100644 --- a/packages/choreo-test-app/app/components/examples/fold.gts +++ b/packages/choreo-test-app/app/components/examples/fold.gts @@ -84,7 +84,7 @@ interface Firing { const COLD: Firing = { cone: false, damper: false, gas: 0, soak: false }; /** - * `` — commands as part of the score (notes/choreo-composition.md §C4). + * `` — commands as part of the score (packages/choreo-gallery/realm/design/choreo-composition.md §C4). * * The law is one sentence: THE SET OF COMMANDS AT OR BEFORE THE CLOCK IS THE * COMMANDED STATE. Everything here exists to make that visible, because it is diff --git a/packages/choreo-test-app/app/components/examples/hang.gts b/packages/choreo-test-app/app/components/examples/hang.gts index 799681084c5..3cc1e2e0270 100644 --- a/packages/choreo-test-app/app/components/examples/hang.gts +++ b/packages/choreo-test-app/app/components/examples/hang.gts @@ -64,7 +64,7 @@ import { preventSelect } from 'test-app/lib/pointer'; * COAST being derived from them, and there was no instrument for the job. * * `[value, min, max, step]` is dialkit's slider tuple. The panel is drawn by - * `DialPanel`; the store→tracked bridge is `lib/dial.ts`, and notes/dialkit.md + * `DialPanel`; the store→tracked bridge is `lib/dial.ts`, and packages/choreo-gallery/realm/design/dialkit.md * is the evaluation that led here. */ const TUNING = { diff --git a/packages/choreo-test-app/app/components/feature-reel.gts b/packages/choreo-test-app/app/components/feature-reel.gts index 5054d023571..d4f7cb58381 100644 --- a/packages/choreo-test-app/app/components/feature-reel.gts +++ b/packages/choreo-test-app/app/components/feature-reel.gts @@ -74,7 +74,7 @@ export class FeatureReel extends Component { private ready = new Promise((resolve) => (this.resolveReady = resolve)); /** - * The composition host (notes/choreo-composition.md, Phase C1). Cues are + * The composition host (packages/choreo-gallery/realm/design/choreo-composition.md, Phase C1). Cues are * idempotent semantic commands folded through the composition clock in * BOTH modes; parameter channels are capture-only reconstruction — in * preview the demos play their own engines. @@ -316,7 +316,7 @@ export class FeatureReel extends Component { (window as Window & { __choreoRecording?: boolean }).__choreoRecording ) { // a recorder owns this page from birth: the first render plays - // nothing (docs/demo-recording.md) — no preview clock ever starts, + // nothing (packages/choreo-gallery/realm/docs/demo-recording.md) — no preview clock ever starts, // no cue fires on wall time, and the score mounts through the // player's own prepare barrier when the first seek arrives this.externallyDriven = true; @@ -335,7 +335,7 @@ export class FeatureReel extends Component { /** * Live preview: the runs PLAY — play() is GPU, renderAt(t) is a still - * (notes/choreo-composition.md, scope decisions). This clock only fires + * (packages/choreo-gallery/realm/design/choreo-composition.md, scope decisions). This clock only fires * cues through the same fold capture uses, and retimes a newly compiled * replacement run to the composition clock ONCE; it never pauses or * seeks a run that is already playing. @@ -554,7 +554,7 @@ export class FeatureReel extends Component { {{! the natural authoring contract: a move, then a wait. Every wait here once had to be a constant-easing camera duplicate so a random-access seek would land on the pose — the workaround - notes/external-clock-camera-seek-handoff.md records. The score + packages/choreo-gallery/realm/design/external-clock-camera-seek-handoff.md records. The score staying Wait-based IS the proof the transport reconstructs. }} diff --git a/packages/choreo-test-app/app/components/films/sagrada-score.gts b/packages/choreo-test-app/app/components/films/sagrada-score.gts index 85ee7b64541..d62a8e7fd2e 100644 --- a/packages/choreo-test-app/app/components/films/sagrada-score.gts +++ b/packages/choreo-test-app/app/components/films/sagrada-score.gts @@ -2,7 +2,7 @@ * SAGRADA FAMÍLIA, AS A GRAPH — the film's score in the film vocabulary. * * Generated from the beat table in `lib/films/sagrada.ts` by - * `docs/film-graph/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, + * `packages/choreo-film-app/tools/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, * stamp and trace is the table's; only the shape differs. It takes the * vocabulary as an argument so it can be rendered inside `` * and, in `tests/integration/film/graph-test.gts`, inside a bare diff --git a/packages/choreo-test-app/app/components/films/towers-score.gts b/packages/choreo-test-app/app/components/films/towers-score.gts index c6b2755641b..305d07d04d0 100644 --- a/packages/choreo-test-app/app/components/films/towers-score.gts +++ b/packages/choreo-test-app/app/components/films/towers-score.gts @@ -2,7 +2,7 @@ * TOWERS, AS A GRAPH — the film's score in the film vocabulary. * * Generated from the beat table in `lib/films/towers.ts` by - * `docs/film-graph/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, + * `packages/choreo-film-app/tools/tograph.mjs` (PREFIX='@f.'), so every pose, phrase, * stamp and trace is the table's; only the shape differs. It takes the * vocabulary as an argument so it can be rendered inside `` * and, in `tests/integration/film/graph-test.gts`, inside a bare diff --git a/packages/choreo-test-app/app/components/notes/towers.gts b/packages/choreo-test-app/app/components/notes/towers.gts index 51bf9f8b96a..2e4a55efafa 100644 --- a/packages/choreo-test-app/app/components/notes/towers.gts +++ b/packages/choreo-test-app/app/components/notes/towers.gts @@ -237,9 +237,9 @@ const TowersNotes: TOC<{ wet, a frame budget that owns the pixel ratio, a master fader and a hold — and the one-frame synchronous snapshot that makes every still-based join possible. The full design note lives at - docs/choreo-splices.md; the quality pass and its numbers - in - notes/towers-quality.md. + packages/choreo-gallery/realm/docs/choreo-splices.md; the + quality pass and its numbers in + packages/choreo-gallery/realm/design/towers-quality.md.

diff --git a/packages/choreo-test-app/app/components/sylva-stage.gts b/packages/choreo-test-app/app/components/sylva-stage.gts index b8b9dc4b5ba..f937909b3ad 100644 --- a/packages/choreo-test-app/app/components/sylva-stage.gts +++ b/packages/choreo-test-app/app/components/sylva-stage.gts @@ -18,7 +18,7 @@ import { /** * A SPIKE, at `/_sylva`. No catalog entry, no gallery card, not a demo — it - * exists to answer the questions `docs/dom-in-3d.md` leaves open at the end of + * exists to answer the questions `packages/choreo-gallery/realm/docs/dom-in-3d.md` leaves open at the end of * cases 3 and 4, and it should be deleted or promoted rather than left lying * around. * diff --git a/packages/choreo-gallery/docs/guide-topics.json b/packages/choreo-test-app/app/content/guide-topics.json similarity index 100% rename from packages/choreo-gallery/docs/guide-topics.json rename to packages/choreo-test-app/app/content/guide-topics.json diff --git a/packages/choreo-test-app/app/content/guides/core-api-inventory.md b/packages/choreo-test-app/app/content/guides/core-api-inventory.md index cc128dd7a21..1de86ed266d 100644 --- a/packages/choreo-test-app/app/content/guides/core-api-inventory.md +++ b/packages/choreo-test-app/app/content/guides/core-api-inventory.md @@ -4,7 +4,7 @@ Choreo has four connected areas: an element binding over Motion, interactive sce ## Scope and Reading Depth -The inventory covers the `glimmer-motion` root and its `glimmer-motion/test-support` subpath, the `@cardstack/choreo` root and its `@cardstack/choreo/film` and `@cardstack/choreo/test-support` subpaths, and the separate `choreo-player` package. It also includes the components and queries yielded by ChoreoContext and FilmVocabulary. Each entry links to a substantial concept chapter with motivation, behavior, examples, and relevant constraints. +The inventory covers the `glimmer-motion` root and its `glimmer-motion/test-support` subpath, the `@cardstack/choreo` root and its `@cardstack/choreo/film` and `@cardstack/choreo/test-support` subpaths, and the separate `@cardstack/choreo-player` package. It also includes the components and queries yielded by ChoreoContext and FilmVocabulary. Each entry links to a substantial concept chapter with motivation, behavior, examples, and relevant constraints. Related helpers, compatibility aliases, and supporting TypeScript types share the chapter about the behavior they describe. For example, a spring configuration and its argument type belong with spring transitions; the several query selectors belong with the changeset model. This avoids pretending that a type alias is an independent feature while still making every exported name discoverable. @@ -18,397 +18,397 @@ The library uses the engine's own MotionValues. `glimmer-motion` re-exports a sm ## Maintaining Coverage -Run `pnpm docs:check` after changing an export or a guide. The check compares this inventory with the actual source declarations, verifies guide destinations and preambles, and enforces the minimum prose depth. A new public symbol fails that check until its documentation mapping is reviewed. Browser verification still matters for examples, controls, and renderer behavior; a coverage table alone cannot prove that a demonstration works. +Run `pnpm lint:realm` in `packages/choreo-gallery` after changing an export or a guide. The check compares this inventory with the actual source declarations, verifies guide destinations and preambles, and enforces the minimum prose depth. A new public symbol fails that check until its documentation mapping is reviewed. Browser verification still matters for examples, controls, and renderer behavior; a coverage table alone cannot prove that a demonstration works. ## Find an API ### glimmer-motion -| API | Guide | Source | -| ---------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `BusyProbe` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/activity.ts) | -| `registerBusyProbe` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/activity.ts) | -| `scroll` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `scrollInfo` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `ScrollInfo` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `ScrollOffset` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `ScrollOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `InViewOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `inView` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `createDragControls` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/gestures/drag-controls.ts) | -| `DragControls` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/gestures/drag-controls.ts) | -| `correctParentTransform` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/gestures/transform-page-point.ts) | -| `transformViewBoxPoint` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/gestures/transform-page-point.ts) | -| `InertiaArgs` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `SpringArgs` | [core-springs](/docs/core-springs) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `TweenArgs` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `ease` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `inertia` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `perValue` | [core-targets](/docs/core-targets) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `spring` | [core-springs](/docs/core-springs) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `stagger` | [core-variants](/docs/core-variants) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `styles` | [core-targets](/docs/core-targets) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `to` | [core-targets](/docs/core-targets) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `tween` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts) | -| `afterSettle` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts) | -| `instantLayoutTransition` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts) | -| `layoutChange` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts) | -| `layoutLoopDetected` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts) | -| `requestSettle` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts) | -| `resetLayoutLoopGuard` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts) | -| `snapshotAll` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts) | -| `closestLayoutGroup` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout-group.gts) | -| `LayoutGroup` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout-group.gts) | -| `snapshotOnRender` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout-group.gts) | -| `MotionEl` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion.ts) | -| `MotionProps` | [core-elements](/docs/core-elements) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion.ts) | -| `motion` | [core-elements](/docs/core-elements) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion.ts) | -| `MotionModifier` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion.ts) | -| `MotionConfigContext` | [core-config](/docs/core-config) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion-config.gts) | -| `closestMotionConfig` | [core-config](/docs/core-config) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion-config.gts) | -| `MotionConfig` | [core-config](/docs/core-config) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion-config.gts) | -| `flushPendingMounts` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/node.ts) | -| `MotionNode` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/node.ts) | -| `MotionParticipant` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/participant.ts) | -| `ParticipantArgs` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/participant.ts) | -| `ParticipantHost` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/participant.ts) | -| `closestParticipantHost` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/participant.ts) | -| `defineParticipantArg` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/participant.ts) | -| `PARTICIPANT_HOST_ATTRIBUTE` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/participant.ts) | -| `setParticipantHost` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/participant.ts) | -| `Presence` | [core-presence](/docs/core-presence) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/presence.gts) | -| `PresenceHandle` | [core-presence](/docs/core-presence) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/presence-types.ts) | -| `ReorderGroup` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/reorder/group.gts) | -| `ReorderItem` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/reorder/item.gts) | -| `ReorderAxis` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/reorder/types.ts) | -| `ReorderContextProps` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/reorder/types.ts) | -| `postRender` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scheduler.ts) | -| `setPostRender` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scheduler.ts) | -| `ScrollValues` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `UseInViewOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `UseScrollOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `InView` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `scrollProgress` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `useInView` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `useScroll` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts) | -| `motionSpeed` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/speed.ts) | -| `onMotionSpeed` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/speed.ts) | -| `scaleTransition` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/speed.ts) | -| `setMotionSpeed` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/speed.ts) | -| `ViewTransitionBuilder` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/view-transition.ts) | -| `ViewTransitionOptions` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/view-transition.ts) | -| `ViewTransitionUpdate` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/view-transition.ts) | -| `animateView` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/view-transition.ts) | -| `viewTransition` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/view-transition.ts) | -| `animate` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/index.ts) | -| `frame` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/index.ts) | -| `MotionValue` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/index.ts) | -| `motionValue` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/index.ts) | -| `styleEffect` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/index.ts) | -| `transformValue` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/index.ts) | +| API | Guide | Source | +| ---------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| `BusyProbe` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/activity.ts) | +| `registerBusyProbe` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/activity.ts) | +| `scroll` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `scrollInfo` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `ScrollInfo` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `ScrollOffset` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `ScrollOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `InViewOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `inView` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `createDragControls` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/gestures/drag-controls.ts) | +| `DragControls` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/gestures/drag-controls.ts) | +| `correctParentTransform` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/gestures/transform-page-point.ts) | +| `transformViewBoxPoint` | [core-drag-handles](/docs/core-drag-handles) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/gestures/transform-page-point.ts) | +| `InertiaArgs` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `SpringArgs` | [core-springs](/docs/core-springs) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `TweenArgs` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `ease` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `inertia` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `perValue` | [core-targets](/docs/core-targets) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `spring` | [core-springs](/docs/core-springs) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `stagger` | [core-variants](/docs/core-variants) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `styles` | [core-targets](/docs/core-targets) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `to` | [core-targets](/docs/core-targets) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `tween` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts) | +| `afterSettle` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts) | +| `instantLayoutTransition` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts) | +| `layoutChange` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts) | +| `layoutLoopDetected` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts) | +| `requestSettle` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts) | +| `resetLayoutLoopGuard` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts) | +| `snapshotAll` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts) | +| `closestLayoutGroup` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout-group.gts) | +| `LayoutGroup` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout-group.gts) | +| `snapshotOnRender` | [core-layout](/docs/core-layout) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout-group.gts) | +| `MotionEl` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion.ts) | +| `MotionProps` | [core-elements](/docs/core-elements) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion.ts) | +| `motion` | [core-elements](/docs/core-elements) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion.ts) | +| `MotionModifier` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion.ts) | +| `MotionConfigContext` | [core-config](/docs/core-config) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion-config.gts) | +| `closestMotionConfig` | [core-config](/docs/core-config) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion-config.gts) | +| `MotionConfig` | [core-config](/docs/core-config) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion-config.gts) | +| `flushPendingMounts` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/node.ts) | +| `MotionNode` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/node.ts) | +| `MotionParticipant` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/participant.ts) | +| `ParticipantArgs` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/participant.ts) | +| `ParticipantHost` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/participant.ts) | +| `closestParticipantHost` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/participant.ts) | +| `defineParticipantArg` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/participant.ts) | +| `PARTICIPANT_HOST_ATTRIBUTE` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/participant.ts) | +| `setParticipantHost` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/participant.ts) | +| `Presence` | [core-presence](/docs/core-presence) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/presence.gts) | +| `PresenceHandle` | [core-presence](/docs/core-presence) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/presence-types.ts) | +| `ReorderGroup` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/reorder/group.gts) | +| `ReorderItem` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/reorder/item.gts) | +| `ReorderAxis` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/reorder/types.ts) | +| `ReorderContextProps` | [core-reorder](/docs/core-reorder) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/reorder/types.ts) | +| `postRender` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scheduler.ts) | +| `setPostRender` | [core-host](/docs/core-host) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scheduler.ts) | +| `ScrollValues` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `UseInViewOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `UseScrollOptions` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `InView` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `scrollProgress` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `useInView` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `useScroll` | [core-scroll](/docs/core-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts) | +| `motionSpeed` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/speed.ts) | +| `onMotionSpeed` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/speed.ts) | +| `scaleTransition` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/speed.ts) | +| `setMotionSpeed` | [core-tempo](/docs/core-tempo) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/speed.ts) | +| `ViewTransitionBuilder` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/view-transition.ts) | +| `ViewTransitionOptions` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/view-transition.ts) | +| `ViewTransitionUpdate` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/view-transition.ts) | +| `animateView` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/view-transition.ts) | +| `viewTransition` | [core-page-transitions](/docs/core-page-transitions) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/view-transition.ts) | +| `animate` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/index.ts) | +| `frame` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/index.ts) | +| `MotionValue` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/index.ts) | +| `motionValue` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/index.ts) | +| `styleEffect` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/index.ts) | +| `transformValue` | [core-motion-values](/docs/core-motion-values) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/index.ts) | ### @cardstack/choreo -| API | Guide | Source | -| ------------------ | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -| `beacon` | [interactive-beacons](/docs/interactive-beacons) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/beacon.ts) | -| `ChoreoContext` | [interactive-start](/docs/interactive-start) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `Choreo` | [interactive-start](/docs/interactive-start) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `AnchorRef` | [interactive-anchors](/docs/interactive-anchors) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/anchors.ts) | -| `after` | [interactive-anchors](/docs/interactive-anchors) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/anchors.ts) | -| `at` | [interactive-anchors](/docs/interactive-anchors) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/anchors.ts) | -| `Arming` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/arming.ts) | -| `ArmingOptions` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/arming.ts) | -| `ArmingRegion` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/arming.ts) | -| `createArming` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/arming.ts) | -| `BeaconRef` | [interactive-beacons](/docs/interactive-beacons) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/beacons.ts) | -| `Changeset` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/changeset.ts) | -| `easeIn` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/easings.ts) | -| `easeInAndOut` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/easings.ts) | -| `easeOut` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/easings.ts) | -| `GestureRef` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/gesture.ts) | -| `ChoreoHost` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/registry.ts) | -| `choreoHostAt` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/registry.ts) | -| `choreoHostById` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/registry.ts) | -| `ChoreoProvider` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/registry.ts) | -| `closestChoreo` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/registry.ts) | -| `ChoreoRun` | [interactive-run](/docs/interactive-run) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/run.ts) | -| `PlanePoint` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/space.ts) | -| `appliedCamera` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/space.ts) | -| `toLocal` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/space.ts) | -| `toPage` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/space.ts) | -| `StepArgs` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/steps.gts) | -| `StepArgsBase` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/steps.gts) | -| `StepComponent` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/steps.gts) | -| `toMs` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/steps.gts) | -| `Block` | [interactive-blocks](/docs/interactive-blocks) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Bounds` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Camera3DState` | [spatial-camera3d](/docs/spatial-camera3d) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Camera3DWaypoint` | [spatial-paths](/docs/spatial-paths) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `CameraState` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `DeliveryBy` | [interactive-delivery](/docs/interactive-delivery) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `DeliveryOrder` | [interactive-delivery](/docs/interactive-delivery) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `DeriveContext` | [interactive-follow](/docs/interactive-follow) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Easing` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `FollowSource` | [interactive-follow](/docs/interactive-follow) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `GateNode` | [interactive-gates](/docs/interactive-gates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `PerformCommand` | [interactive-commands](/docs/interactive-commands) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `PropSource` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `PropValue` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Query` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Rect` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `SpringSpec` | [core-springs](/docs/core-springs) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Sprite` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `Step` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | -| `TimelineNode` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts) | +| API | Guide | Source | +| ------------------ | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `beacon` | [interactive-beacons](/docs/interactive-beacons) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/beacon.ts) | +| `ChoreoContext` | [interactive-start](/docs/interactive-start) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `Choreo` | [interactive-start](/docs/interactive-start) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `AnchorRef` | [interactive-anchors](/docs/interactive-anchors) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/anchors.ts) | +| `after` | [interactive-anchors](/docs/interactive-anchors) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/anchors.ts) | +| `at` | [interactive-anchors](/docs/interactive-anchors) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/anchors.ts) | +| `Arming` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/arming.ts) | +| `ArmingOptions` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/arming.ts) | +| `ArmingRegion` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/arming.ts) | +| `createArming` | [interactive-arming](/docs/interactive-arming) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/arming.ts) | +| `BeaconRef` | [interactive-beacons](/docs/interactive-beacons) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/beacons.ts) | +| `Changeset` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/changeset.ts) | +| `easeIn` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/easings.ts) | +| `easeInAndOut` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/easings.ts) | +| `easeOut` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/easings.ts) | +| `GestureRef` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/gesture.ts) | +| `ChoreoHost` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/registry.ts) | +| `choreoHostAt` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/registry.ts) | +| `choreoHostById` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/registry.ts) | +| `ChoreoProvider` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/registry.ts) | +| `closestChoreo` | [interactive-registry](/docs/interactive-registry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/registry.ts) | +| `ChoreoRun` | [interactive-run](/docs/interactive-run) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/run.ts) | +| `PlanePoint` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/space.ts) | +| `appliedCamera` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/space.ts) | +| `toLocal` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/space.ts) | +| `toPage` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/space.ts) | +| `StepArgs` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/steps.gts) | +| `StepArgsBase` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/steps.gts) | +| `StepComponent` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/steps.gts) | +| `toMs` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/steps.gts) | +| `Block` | [interactive-blocks](/docs/interactive-blocks) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Bounds` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Camera3DState` | [spatial-camera3d](/docs/spatial-camera3d) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Camera3DWaypoint` | [spatial-paths](/docs/spatial-paths) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `CameraState` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `DeliveryBy` | [interactive-delivery](/docs/interactive-delivery) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `DeliveryOrder` | [interactive-delivery](/docs/interactive-delivery) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `DeriveContext` | [interactive-follow](/docs/interactive-follow) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Easing` | [core-tweens](/docs/core-tweens) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `FollowSource` | [interactive-follow](/docs/interactive-follow) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `GateNode` | [interactive-gates](/docs/interactive-gates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `PerformCommand` | [interactive-commands](/docs/interactive-commands) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `PropSource` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `PropValue` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Query` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Rect` | [spatial-coordinates](/docs/spatial-coordinates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `SpringSpec` | [core-springs](/docs/core-springs) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Sprite` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `Step` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | +| `TimelineNode` | [interactive-composites](/docs/interactive-composites) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts) | ### @cardstack/choreo/film -| API | Guide | Source | -| ------------------------ | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | -| `Clip` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clip.gts) | -| `ClipEnd` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `ClipKind` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `clipLanes` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `ClipLook` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `ClipSpec` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `ClipState` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `clipWindow` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `resolveClip` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `resolveClips` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `ResolvedClip` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts) | -| `Film` | [film-start](/docs/film-start) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/film.gts) | -| `FilmContext` | [film-start](/docs/film-start) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/film.gts) | -| `FilmSignature` | [film-start](/docs/film-start) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/film.gts) | -| `TICK` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/film.gts) | -| `Adjustment` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Build` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Filter` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `IFRAME_PICTURE` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Light` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Look` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Mix` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Set` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `SOUND` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Sun` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Weather` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `Winter` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts) | -| `AttachNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `CompiledGraph` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `compileGraph` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `EyeNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `GraphNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `GroupNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `JoinNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `Patch` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `PatchNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `ShotNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `ToNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `VoiceNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts) | -| `FilmGraph` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `FilmGraphSignature` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `FilmVocabulary` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `VOCABULARY` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `collectGraph` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Eye` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Freeze` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `GraphAttach` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `GraphChapter` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `GraphInsert` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `GraphNodeComponent` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `GraphProvider` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `GraphStamp` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `JoinInto` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Lineup` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Mark` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `PatchComponent` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Sequence` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Shot` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Sky` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Spine` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `To` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Trace` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Type` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Video` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Voice` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts) | -| `Blend` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Blur` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Dip` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Flash` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `inGlass` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Iris` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `JOIN_SECS` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Joins` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Luma` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Melt` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Presentation` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `PresentationComponent` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `PRESENTATIONS` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `PresentationSignature` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `retire` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `seamShape` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `STILL_JOINS` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `Wipe` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts) | -| `clamp01` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `hex` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `lerp` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `luminance` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `mmss` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `RAD` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `rgba` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `smooth` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts) | -| `Captions` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/overlays.gts) | -| `Insert` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/overlays.gts) | -| `Stamp` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/overlays.gts) | -| `Track` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/overlays.gts) | -| `IframePicture` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/picture.gts) | -| `IframePictureSignature` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/picture.gts) | -| `PictureSpec` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/picture.gts) | -| `Plate` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/plate.gts) | -| `Burst` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/player.gts) | -| `Menu` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/player.gts) | -| `MenuEntry` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/player.gts) | -| `PlaybarSegment` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/player.gts) | -| `Player` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/player.gts) | -| `Rail` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/rail.gts) | -| `RailMark` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/rail.gts) | -| `beatStart` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `chapterHeads` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `Content` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `contents` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `Cue` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `cues` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `joinInto` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `Schedule` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `schedule` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `secsBefore` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `tailFor` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `totalSecs` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `Waypoint` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `waypoints` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts) | -| `Seam` | [film-seam-readiness](/docs/film-seam-readiness) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/seam.ts) | -| `EndCard` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/titles.gts) | -| `Gate` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/titles.gts) | -| `Beat` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `Cam` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `Chapter` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `FilmClock` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `FilmGrade` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `FilmHandle` | [film-transport](/docs/film-transport) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `Join` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `JoinName` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `LookFx` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `Over` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `Picture` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `PlateMode` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `Pt3` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `SeamSpec` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | -| `ShotState` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts) | +| API | Guide | Source | +| ------------------------ | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | +| `Clip` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clip.gts) | +| `ClipEnd` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `ClipKind` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `clipLanes` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `ClipLook` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `ClipSpec` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `ClipState` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `clipWindow` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `resolveClip` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `resolveClips` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `ResolvedClip` | [film-clips](/docs/film-clips) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts) | +| `Film` | [film-start](/docs/film-start) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/film.gts) | +| `FilmContext` | [film-start](/docs/film-start) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/film.gts) | +| `FilmSignature` | [film-start](/docs/film-start) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/film.gts) | +| `TICK` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/film.gts) | +| `Adjustment` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Build` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Filter` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `IFRAME_PICTURE` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Light` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Look` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Mix` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Set` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `SOUND` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Sun` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Weather` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `Winter` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts) | +| `AttachNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `CompiledGraph` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `compileGraph` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `EyeNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `GraphNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `GroupNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `JoinNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `Patch` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `PatchNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `ShotNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `ToNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `VoiceNode` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts) | +| `FilmGraph` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `FilmGraphSignature` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `FilmVocabulary` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `VOCABULARY` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `collectGraph` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Eye` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Freeze` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `GraphAttach` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `GraphChapter` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `GraphInsert` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `GraphNodeComponent` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `GraphProvider` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `GraphStamp` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `JoinInto` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Lineup` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Mark` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `PatchComponent` | [film-graph-extension](/docs/film-graph-extension) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Sequence` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Shot` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Sky` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Spine` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `To` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Trace` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Type` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Video` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Voice` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts) | +| `Blend` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Blur` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Dip` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Flash` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `inGlass` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Iris` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `JOIN_SECS` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Joins` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Luma` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Melt` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Presentation` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `PresentationComponent` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `PRESENTATIONS` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `PresentationSignature` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `retire` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `seamShape` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `STILL_JOINS` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `Wipe` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts) | +| `clamp01` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `hex` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `lerp` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `luminance` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `mmss` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `RAD` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `rgba` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `smooth` | [film-utilities](/docs/film-utilities) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts) | +| `Captions` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/overlays.gts) | +| `Insert` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/overlays.gts) | +| `Stamp` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/overlays.gts) | +| `Track` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/overlays.gts) | +| `IframePicture` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/picture.gts) | +| `IframePictureSignature` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/picture.gts) | +| `PictureSpec` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/picture.gts) | +| `Plate` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/plate.gts) | +| `Burst` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/player.gts) | +| `Menu` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/player.gts) | +| `MenuEntry` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/player.gts) | +| `PlaybarSegment` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/player.gts) | +| `Player` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/player.gts) | +| `Rail` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/rail.gts) | +| `RailMark` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/rail.gts) | +| `beatStart` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `chapterHeads` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `Content` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `contents` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `Cue` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `cues` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `joinInto` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `Schedule` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `schedule` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `secsBefore` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `tailFor` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `totalSecs` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `Waypoint` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `waypoints` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts) | +| `Seam` | [film-seam-readiness](/docs/film-seam-readiness) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/seam.ts) | +| `EndCard` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/titles.gts) | +| `Gate` | [film-interface](/docs/film-interface) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/titles.gts) | +| `Beat` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `Cam` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `Chapter` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `FilmClock` | [film-schedule](/docs/film-schedule) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `FilmGrade` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `FilmHandle` | [film-transport](/docs/film-transport) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `Join` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `JoinName` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `LookFx` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `Over` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `Picture` | [film-picture](/docs/film-picture) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `PlateMode` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `Pt3` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `SeamSpec` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | +| `ShotState` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts) | ### glimmer-motion/test-support -| API | Guide | Source | -| --------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -| `isMotionIdle` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/activity.ts) | -| `whatIsBusy` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/activity.ts) | -| `Box` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `bounds` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `shape` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `boundsAndShape` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `SettleOptions` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `animationsSettled` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `velocityOf` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `setupMotion` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `resetMotion` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | -| `registerMotionReset` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| API | Guide | Source | +| --------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `isMotionIdle` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/activity.ts) | +| `whatIsBusy` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/activity.ts) | +| `Box` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `bounds` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `shape` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `boundsAndShape` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `SettleOptions` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `animationsSettled` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `velocityOf` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `setupMotion` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `resetMotion` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | +| `registerMotionReset` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts) | ### @cardstack/choreo/test-support -| API | Guide | Source | -| -------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `setupChoreo` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts) | -| `advanceGate` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts) | -| `seekTo` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts) | -| `orphanCount` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts) | -| `live` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts) | -| `liveAll` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts) | -| `strandedTransforms` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts) | +| API | Guide | Source | +| -------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------- | +| `setupChoreo` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts) | +| `advanceGate` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts) | +| `seekTo` | [core-test-timing](/docs/core-test-timing) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts) | +| `orphanCount` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts) | +| `live` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts) | +| `liveAll` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts) | +| `strandedTransforms` | [core-test-geometry](/docs/core-test-geometry) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts) | -### choreo-player +### @cardstack/choreo-player -| API | Guide | Source | -| --------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- | -| `ChoreoRun` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/choreo-player/src/index.ts) | -| `ChoreoPlayer` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/choreo-player/src/index.ts) | -| `ChoreoPlayerUpdateOptions` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/choreo-player/src/index.ts) | -| `ChoreoPlayerOptions` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/choreo-player/src/index.ts) | -| `createChoreoPlayer` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/choreo-player/src/index.ts) | +| API | Guide | Source | +| --------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------- | +| `ChoreoRun` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo-player/src/index.ts) | +| `ChoreoPlayer` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo-player/src/index.ts) | +| `ChoreoPlayerUpdateOptions` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo-player/src/index.ts) | +| `ChoreoPlayerOptions` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo-player/src/index.ts) | +| `createChoreoPlayer` | [film-player](/docs/film-player) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo-player/src/index.ts) | ### ChoreoContext -| API | Guide | Source | -| --------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| `c.Aim` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Attach` | [film-attachments](/docs/film-attachments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Camera` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Camera3D` | [spatial-camera3d](/docs/spatial-camera3d) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Crossing` | [interactive-crossings](/docs/interactive-crossings) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Follow` | [interactive-follow](/docs/interactive-follow) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Frame` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Gate` | [interactive-gates](/docs/interactive-gates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Hold` | [interactive-holds](/docs/interactive-holds) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Move` | [interactive-move](/docs/interactive-move) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Pan` | [spatial-relative](/docs/spatial-relative) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Parallel` | [interactive-blocks](/docs/interactive-blocks) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Perform` | [interactive-commands](/docs/interactive-commands) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Raise` | [interactive-raise-scroll](/docs/interactive-raise-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Scroll` | [interactive-raise-scroll](/docs/interactive-raise-scroll) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Sequence` | [interactive-blocks](/docs/interactive-blocks) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.SlowZoom` | [spatial-relative](/docs/spatial-relative) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Spring` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Tether` | [interactive-tethers](/docs/interactive-tethers) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Tween` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.Wait` | [interactive-holds](/docs/interactive-holds) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.advance` | [interactive-gates](/docs/interactive-gates) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.all` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.beacon` | [interactive-beacons](/docs/interactive-beacons) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.camera` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.counterpart` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.gesture` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.id` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.inserted` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.kept` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.moved` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.onstage` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.received` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.removed` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.role` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.run` | [interactive-run](/docs/interactive-run) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | -| `c.still` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts) | +| API | Guide | Source | +| --------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| `c.Aim` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Attach` | [film-attachments](/docs/film-attachments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Camera` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Camera3D` | [spatial-camera3d](/docs/spatial-camera3d) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Crossing` | [interactive-crossings](/docs/interactive-crossings) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Follow` | [interactive-follow](/docs/interactive-follow) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Frame` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Gate` | [interactive-gates](/docs/interactive-gates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Hold` | [interactive-holds](/docs/interactive-holds) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Move` | [interactive-move](/docs/interactive-move) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Pan` | [spatial-relative](/docs/spatial-relative) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Parallel` | [interactive-blocks](/docs/interactive-blocks) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Perform` | [interactive-commands](/docs/interactive-commands) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Raise` | [interactive-raise-scroll](/docs/interactive-raise-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Scroll` | [interactive-raise-scroll](/docs/interactive-raise-scroll) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Sequence` | [interactive-blocks](/docs/interactive-blocks) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.SlowZoom` | [spatial-relative](/docs/spatial-relative) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Spring` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Tether` | [interactive-tethers](/docs/interactive-tethers) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Tween` | [interactive-property-steps](/docs/interactive-property-steps) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.Wait` | [interactive-holds](/docs/interactive-holds) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.advance` | [interactive-gates](/docs/interactive-gates) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.all` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.beacon` | [interactive-beacons](/docs/interactive-beacons) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.camera` | [spatial-frame](/docs/spatial-frame) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.counterpart` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.gesture` | [core-drag](/docs/core-drag) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.id` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.inserted` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.kept` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.moved` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.onstage` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.received` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.removed` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.role` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.run` | [interactive-run](/docs/interactive-run) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | +| `c.still` | [interactive-queries](/docs/interactive-queries) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts) | ### FilmVocabulary -| API | Guide | Source | -| ------------ | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | -| `f.Attach` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Chapter` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Eye` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Freeze` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Insert` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Inset` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Join` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Lineup` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Mark` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Sequence` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Shot` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Sky` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Spine` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Stamp` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.To` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Trace` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Type` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Video` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.Voice` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.clip` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.picture` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | -| `f.sound` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts) | +| API | Guide | Source | +| ------------ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | +| `f.Attach` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Chapter` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Eye` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Freeze` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Insert` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Inset` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Join` | [film-joins](/docs/film-joins) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Lineup` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Mark` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Sequence` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Shot` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Sky` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Spine` | [film-graph](/docs/film-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Stamp` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.To` | [film-poses](/docs/film-poses) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Trace` | [film-world-annotations](/docs/film-world-annotations) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Type` | [film-typography](/docs/film-typography) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Video` | [film-media-graph](/docs/film-media-graph) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.Voice` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.clip` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.picture` | [film-adjustments](/docs/film-adjustments) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | +| `f.sound` | [film-audio-mix](/docs/film-audio-mix) | [Implementation](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts) | diff --git a/packages/choreo-test-app/app/content/guides/core-config.md b/packages/choreo-test-app/app/content/guides/core-config.md index 562403bad85..6f2814113f5 100644 --- a/packages/choreo-test-app/app/content/guides/core-config.md +++ b/packages/choreo-test-app/app/content/guides/core-config.md @@ -35,4 +35,4 @@ Check nested configurations in the actual DOM structure. A component hierarchy t **glimmer-motion**: `MotionConfigContext`, `closestMotionConfig`, `MotionConfig`. -Read the implementation: [`motion-config.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion-config.gts). +Read the implementation: [`motion-config.gts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion-config.gts). diff --git a/packages/choreo-test-app/app/content/guides/core-drag-handles.md b/packages/choreo-test-app/app/content/guides/core-drag-handles.md index 24cfcf5add2..c9a05e32b83 100644 --- a/packages/choreo-test-app/app/content/guides/core-drag-handles.md +++ b/packages/choreo-test-app/app/content/guides/core-drag-handles.md @@ -38,4 +38,4 @@ Test the same gesture under the transforms the real product uses. A fixture at s **glimmer-motion**: `createDragControls`, `DragControls`, `correctParentTransform`, `transformViewBoxPoint`. -Read the implementation: [`drag-controls.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/gestures/drag-controls.ts), [`transform-page-point.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/gestures/transform-page-point.ts). +Read the implementation: [`drag-controls.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/gestures/drag-controls.ts), [`transform-page-point.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/gestures/transform-page-point.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-drag.md b/packages/choreo-test-app/app/content/guides/core-drag.md index c233eb26507..c7863a2fcd3 100644 --- a/packages/choreo-test-app/app/content/guides/core-drag.md +++ b/packages/choreo-test-app/app/content/guides/core-drag.md @@ -44,4 +44,4 @@ If the object moves twice as far as the pointer under a scaled parent, the relea **ChoreoContext**: `c.gesture`. -Read the implementation: [`gesture.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/gesture.ts), [`helpers.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`gesture.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/gesture.ts), [`helpers.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/core-elements.md b/packages/choreo-test-app/app/content/guides/core-elements.md index d2e552ce4fe..613c00ef739 100644 --- a/packages/choreo-test-app/app/content/guides/core-elements.md +++ b/packages/choreo-test-app/app/content/guides/core-elements.md @@ -96,4 +96,4 @@ Keep SVG drawing properties on the same modifier so the engine can observe their **glimmer-motion**: `MotionProps`, `motion`. -Read the implementation: [`motion.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion.ts). +Read the implementation: [`motion.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-first-app.md b/packages/choreo-test-app/app/content/guides/core-first-app.md index 91a31ec1d42..2dc1d897d7a 100644 --- a/packages/choreo-test-app/app/content/guides/core-first-app.md +++ b/packages/choreo-test-app/app/content/guides/core-first-app.md @@ -68,4 +68,4 @@ node packages/choreo-gallery/scripts/verify-tutorials.mjs http://localhost:4600 The check reverses and removes during movement, checks final row identity, tests the spatial button, and compares film frames reached from different times. It is a small regression check, not a substitute for your application's keyboard, focus-restoration, and reduced-motion tests. In particular, decide where focus should move when the user removes the focused task. -The complete source is [task-board.gts](https://github.com/cardstack/choreo/blob/main/test-app/app/components/tutorials/task-board.gts). Next, reuse the same separation of state and motion in [your first spatial scene](/docs/spatial-first-scene), or consult [testing motion](/docs/core-testing) before adding more interactions. +The complete source is [task-board.gts](https://github.com/cardstack/boxel/blob/main/packages/choreo-test-app/app/components/tutorials/task-board.gts). Next, reuse the same separation of state and motion in [your first spatial scene](/docs/spatial-first-scene), or consult [testing motion](/docs/core-testing) before adding more interactions. diff --git a/packages/choreo-test-app/app/content/guides/core-host.md b/packages/choreo-test-app/app/content/guides/core-host.md index fb69bf86392..375f68a1175 100644 --- a/packages/choreo-test-app/app/content/guides/core-host.md +++ b/packages/choreo-test-app/app/content/guides/core-host.md @@ -46,4 +46,4 @@ A host whose work outlasts the engine's own animations, such as a timeline still **glimmer-motion**: `BusyProbe`, `registerBusyProbe`, `afterSettle`, `instantLayoutTransition`, `layoutLoopDetected`, `requestSettle`, `resetLayoutLoopGuard`, `snapshotAll`, `MotionEl`, `MotionModifier`, `flushPendingMounts`, `MotionNode`, `MotionParticipant`, `ParticipantArgs`, `ParticipantHost`, `closestParticipantHost`, `defineParticipantArg`, `PARTICIPANT_HOST_ATTRIBUTE`, `setParticipantHost`, `postRender`, `setPostRender`. -Read the implementation: [`activity.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/activity.ts), [`layout.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts), [`motion.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/motion.ts), [`node.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/node.ts), [`scheduler.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scheduler.ts). +Read the implementation: [`activity.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/activity.ts), [`layout.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts), [`motion.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/motion.ts), [`node.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/node.ts), [`scheduler.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scheduler.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-layout.md b/packages/choreo-test-app/app/content/guides/core-layout.md index ea533a6de06..28e71283591 100644 --- a/packages/choreo-test-app/app/content/guides/core-layout.md +++ b/packages/choreo-test-app/app/content/guides/core-layout.md @@ -68,4 +68,4 @@ LayoutGroup also supplies the render observation that lets the binding capture o **glimmer-motion**: `layoutChange`, `closestLayoutGroup`, `LayoutGroup`, `snapshotOnRender`. -Read the implementation: [`layout.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout.ts), [`layout-group.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/layout-group.gts). +Read the implementation: [`layout.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout.ts), [`layout-group.gts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/layout-group.gts). diff --git a/packages/choreo-test-app/app/content/guides/core-page-transitions.md b/packages/choreo-test-app/app/content/guides/core-page-transitions.md index 15a405ba8b6..4ccabe08179 100644 --- a/packages/choreo-test-app/app/content/guides/core-page-transitions.md +++ b/packages/choreo-test-app/app/content/guides/core-page-transitions.md @@ -39,4 +39,4 @@ A successful navigation is more than a smooth camera-like move. The departing vi **glimmer-motion**: `ViewTransitionBuilder`, `ViewTransitionOptions`, `ViewTransitionUpdate`, `animateView`, `viewTransition`. -Read the implementation: [`view-transition.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/view-transition.ts). +Read the implementation: [`view-transition.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/view-transition.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-presence.md b/packages/choreo-test-app/app/content/guides/core-presence.md index 2d73171b844..c2a99b66c53 100644 --- a/packages/choreo-test-app/app/content/guides/core-presence.md +++ b/packages/choreo-test-app/app/content/guides/core-presence.md @@ -57,4 +57,4 @@ Give each item a stable key so the presence model can distinguish a changed item **glimmer-motion**: `Presence`, `PresenceHandle`. -Read the implementation: [`presence.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/presence.gts), [`presence-types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/presence-types.ts). +Read the implementation: [`presence.gts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/presence.gts), [`presence-types.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/presence-types.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-reorder.md b/packages/choreo-test-app/app/content/guides/core-reorder.md index 19f24c30a40..886ca928f2e 100644 --- a/packages/choreo-test-app/app/content/guides/core-reorder.md +++ b/packages/choreo-test-app/app/content/guides/core-reorder.md @@ -32,4 +32,4 @@ Also test scrolling near the edge of a long list, dragging within a transformed **glimmer-motion**: `ReorderGroup`, `ReorderItem`, `ReorderAxis`, `ReorderContextProps`. -Read the implementation: [`group.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/reorder/group.gts), [`item.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/reorder/item.gts), [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/reorder/types.ts). +Read the implementation: [`group.gts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/reorder/group.gts), [`item.gts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/reorder/item.gts), [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/reorder/types.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-scroll.md b/packages/choreo-test-app/app/content/guides/core-scroll.md index 42fbd78e03d..6d9205c19df 100644 --- a/packages/choreo-test-app/app/content/guides/core-scroll.md +++ b/packages/choreo-test-app/app/content/guides/core-scroll.md @@ -34,4 +34,4 @@ Test short pages, nested scroll containers, a resized viewport, and a target alr **glimmer-motion**: `scroll`, `scrollInfo`, `ScrollInfo`, `ScrollOffset`, `ScrollOptions`, `InViewOptions`, `inView`, `ScrollValues`, `UseInViewOptions`, `UseScrollOptions`, `InView`, `scrollProgress`, `useInView`, `useScroll`. -Read the implementation: [`scroll.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/scroll.ts). `scroll()`, `scrollInfo()` and `inView()` are Motion's own, re-exported from `framer-motion/dom`; `scroll.ts` adds the modifiers that bind them to elements. +Read the implementation: [`scroll.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/scroll.ts). `scroll()`, `scrollInfo()` and `inView()` are Motion's own, re-exported from `framer-motion/dom`; `scroll.ts` adds the modifiers that bind them to elements. diff --git a/packages/choreo-test-app/app/content/guides/core-springs.md b/packages/choreo-test-app/app/content/guides/core-springs.md index 97ae43cf446..b5180add596 100644 --- a/packages/choreo-test-app/app/content/guides/core-springs.md +++ b/packages/choreo-test-app/app/content/guides/core-springs.md @@ -33,4 +33,4 @@ Finally, distinguish an analytically sampled animation from a live simulation. A **glimmer-motion**: `SpringArgs`, `spring`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`helpers.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`helpers.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-targets.md b/packages/choreo-test-app/app/content/guides/core-targets.md index b9f00f703fc..1f9748efc10 100644 --- a/packages/choreo-test-app/app/content/guides/core-targets.md +++ b/packages/choreo-test-app/app/content/guides/core-targets.md @@ -40,4 +40,4 @@ When debugging, first check ownership. If state changes correctly but movement d **glimmer-motion**: `perValue`, `styles`, `to`. -Read the implementation: [`helpers.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts). +Read the implementation: [`helpers.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-tempo.md b/packages/choreo-test-app/app/content/guides/core-tempo.md index 57d57fa0830..3f14f7a2e65 100644 --- a/packages/choreo-test-app/app/content/guides/core-tempo.md +++ b/packages/choreo-test-app/app/content/guides/core-tempo.md @@ -36,4 +36,4 @@ Restore global tempo when a temporary inspection tool is destroyed. Otherwise a **glimmer-motion**: `motionSpeed`, `onMotionSpeed`, `scaleTransition`, `setMotionSpeed`. -Read the implementation: [`speed.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/speed.ts). +Read the implementation: [`speed.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/speed.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-test-geometry.md b/packages/choreo-test-app/app/content/guides/core-test-geometry.md index 2c425d00480..568dc594235 100644 --- a/packages/choreo-test-app/app/content/guides/core-test-geometry.md +++ b/packages/choreo-test-app/app/content/guides/core-test-geometry.md @@ -41,4 +41,4 @@ Include an external scale and a clipped parent in fixtures for spatial or elevat **@cardstack/choreo/test-support**: `orphanCount`, `live`, `liveAll`, `strandedTransforms`. -Read the implementation: [`test-support/index.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts), [`choreo/test-support/index.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts). +Read the implementation: [`test-support/index.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts), [`choreo/test-support/index.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-test-timing.md b/packages/choreo-test-app/app/content/guides/core-test-timing.md index 1f04d664bda..2f35d814647 100644 --- a/packages/choreo-test-app/app/content/guides/core-test-timing.md +++ b/packages/choreo-test-app/app/content/guides/core-test-timing.md @@ -46,4 +46,4 @@ For an interruption test, trigger the second action before calling animationsSet **@cardstack/choreo/test-support**: `setupChoreo`, `advanceGate`, `seekTo`. -Read the implementation: [`activity.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/activity.ts), [`test-support/index.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/test-support/index.ts), [`choreo/test-support/index.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/test-support/index.ts). +Read the implementation: [`activity.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/activity.ts), [`test-support/index.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/test-support/index.ts), [`choreo/test-support/index.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/test-support/index.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-testing.md b/packages/choreo-test-app/app/content/guides/core-testing.md index da4c1171140..829c13e9ddb 100644 --- a/packages/choreo-test-app/app/content/guides/core-testing.md +++ b/packages/choreo-test-app/app/content/guides/core-testing.md @@ -54,7 +54,7 @@ Replace `my-app` with your application's module prefix. This example tests the m A completion test is only part of the behavior. Also trigger a second action while movement is in progress and verify the final application state. For Choreo scenes, `orphanCount()` and `strandedTransforms()` help detect leaving elements or transforms that were not cleaned up. -The [test-support reference](https://github.com/cardstack/choreo#test-support) describes the available assertions and inspection helpers. +The [test-support reference](https://github.com/cardstack/boxel/tree/main/packages/glimmer-motion#testing) describes the available assertions and inspection helpers. ## Choosing a Test Boundary diff --git a/packages/choreo-test-app/app/content/guides/core-tweens.md b/packages/choreo-test-app/app/content/guides/core-tweens.md index 46173f993d7..d24335a8992 100644 --- a/packages/choreo-test-app/app/content/guides/core-tweens.md +++ b/packages/choreo-test-app/app/content/guides/core-tweens.md @@ -42,4 +42,4 @@ For recorded output, a timed keyframe path is particularly useful because its st **glimmer-motion**: `TweenArgs`, `ease`, `tween`. -Read the implementation: [`easings.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/easings.ts), [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`helpers.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts). +Read the implementation: [`easings.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/easings.ts), [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`helpers.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts). diff --git a/packages/choreo-test-app/app/content/guides/core-variants.md b/packages/choreo-test-app/app/content/guides/core-variants.md index c4fc6f4a571..ed5e13aae26 100644 --- a/packages/choreo-test-app/app/content/guides/core-variants.md +++ b/packages/choreo-test-app/app/content/guides/core-variants.md @@ -44,4 +44,4 @@ Verify both initial mount and later state changes. Also verify that removing the **glimmer-motion**: `stagger`. -Read the implementation: [`helpers.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/helpers.ts). +Read the implementation: [`helpers.ts`](https://github.com/cardstack/boxel/blob/main/packages/glimmer-motion/src/helpers.ts). diff --git a/packages/choreo-test-app/app/content/guides/film-adjustments.md b/packages/choreo-test-app/app/content/guides/film-adjustments.md index 95d364dba2f..c1ec9dabaa9 100644 --- a/packages/choreo-test-app/app/content/guides/film-adjustments.md +++ b/packages/choreo-test-app/app/content/guides/film-adjustments.md @@ -36,4 +36,4 @@ Review atmosphere changes with a fixed camera as well as in the full edit. That **FilmVocabulary**: `f.clip`, `f.picture`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts), [`clips.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts), [`adjust.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts), [`clips.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts), [`adjust.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-attachments.md b/packages/choreo-test-app/app/content/guides/film-attachments.md index c1aafe5f62b..1f40167776e 100644 --- a/packages/choreo-test-app/app/content/guides/film-attachments.md +++ b/packages/choreo-test-app/app/content/guides/film-attachments.md @@ -33,4 +33,4 @@ Seek before, within, and after the window, then repeat those requests in reverse **ChoreoContext**: `c.Attach`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-audio-mix.md b/packages/choreo-test-app/app/content/guides/film-audio-mix.md index f2a751b9d27..ee04bcb9d2e 100644 --- a/packages/choreo-test-app/app/content/guides/film-audio-mix.md +++ b/packages/choreo-test-app/app/content/guides/film-audio-mix.md @@ -36,4 +36,4 @@ Listen at ordinary device volume and confirm the guide remains intelligible over **FilmVocabulary**: `f.Voice`, `f.sound`. -Read the implementation: [`adjust.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts), [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts). +Read the implementation: [`adjust.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts), [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-audio.md b/packages/choreo-test-app/app/content/guides/film-audio.md index c2e95c0872d..3cf81aaf386 100644 --- a/packages/choreo-test-app/app/content/guides/film-audio.md +++ b/packages/choreo-test-app/app/content/guides/film-audio.md @@ -33,4 +33,4 @@ The gallery's narration helper is application-level orchestration. It complement Keep lower thirds in a readable overlay region, clear of the controls being demonstrated. Use short labels for the capability and captions for the spoken content when available. Text should remain readable at the final delivery size, including on a phone. -The [Film reference](https://github.com/cardstack/choreo/blob/main/docs/film.md) covers the built-in type, voice, and sound controls. +The [Film reference](https://choreo.boxel.site/docs/film.md) covers the built-in type, voice, and sound controls. diff --git a/packages/choreo-test-app/app/content/guides/film-clips.md b/packages/choreo-test-app/app/content/guides/film-clips.md index c5aadf54ba1..7c84346824e 100644 --- a/packages/choreo-test-app/app/content/guides/film-clips.md +++ b/packages/choreo-test-app/app/content/guides/film-clips.md @@ -41,4 +41,4 @@ Test a clip that starts late, a clip that spans the next beat, and a held clip r **@cardstack/choreo/film**: `Clip`, `ClipEnd`, `ClipKind`, `clipLanes`, `ClipSpec`, `ClipState`, `clipWindow`, `resolveClip`, `resolveClips`, `ResolvedClip`. -Read the implementation: [`clip.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clip.gts), [`clips.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/clips.ts). +Read the implementation: [`clip.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clip.gts), [`clips.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/clips.ts). diff --git a/packages/choreo-test-app/app/content/guides/film-clock.md b/packages/choreo-test-app/app/content/guides/film-clock.md index 230a0a6a623..93ba0e5f459 100644 --- a/packages/choreo-test-app/app/content/guides/film-clock.md +++ b/packages/choreo-test-app/app/content/guides/film-clock.md @@ -46,7 +46,7 @@ Seeking the motion run alone cannot reopen a panel that application state has re For each frame, prepare the owned score, apply the state for that time, await the Glimmer render, and reassert the run's time before capture. The exact render barrier belongs to the host application. -Test by seeking forward and backward in a nonsequential order. If the same timestamp produces different content, some state is still depending on playback history. See the [recording contract](https://github.com/cardstack/choreo/blob/main/docs/demo-recording.md) for the complete lifecycle. +Test by seeking forward and backward in a nonsequential order. If the same timestamp produces different content, some state is still depending on playback history. See the [recording contract](https://choreo.boxel.site/docs/demo-recording.md) for the complete lifecycle. ## Selecting the Appropriate Transport diff --git a/packages/choreo-test-app/app/content/guides/film-first-export.md b/packages/choreo-test-app/app/content/guides/film-first-export.md index abee3dab3a4..9f35deaccac 100644 --- a/packages/choreo-test-app/app/content/guides/film-first-export.md +++ b/packages/choreo-test-app/app/content/guides/film-first-export.md @@ -53,4 +53,4 @@ Run the starter verification script from the first tutorial as well. It captures For narration, replace the generated cue track with approved audio and align its clip offsets to the same score. Keep browser autoplay permission separate from offline audio mixing. For chapters, picture renderers, and joins, continue with [Building a Film](/docs/film-shots) and [Picture readiness](/docs/film-picture). This tutorial demonstrates the complete smallest recording pipeline so those additions have a working baseline. -The complete sources are [recordable-scene.gts](https://github.com/cardstack/choreo/blob/main/test-app/app/components/tutorials/recordable-scene.gts) and [record-tutorial.mjs](https://github.com/cardstack/choreo/blob/main/packages/choreo-gallery/scripts/record-tutorial.mjs). Keep output media outside tracked source files unless you intentionally want to distribute an example recording. +The complete sources are [recordable-scene.gts](https://github.com/cardstack/boxel/blob/main/packages/choreo-test-app/app/components/tutorials/recordable-scene.gts) and [record-tutorial.mjs](https://github.com/cardstack/boxel/blob/main/packages/choreo-gallery/scripts/record-tutorial.mjs). Keep output media outside tracked source files unless you intentionally want to distribute an example recording. diff --git a/packages/choreo-test-app/app/content/guides/film-graph-extension.md b/packages/choreo-test-app/app/content/guides/film-graph-extension.md index 2c8b519a05e..045cdb0a8d6 100644 --- a/packages/choreo-test-app/app/content/guides/film-graph-extension.md +++ b/packages/choreo-test-app/app/content/guides/film-graph-extension.md @@ -39,4 +39,4 @@ Test the compiled graph before testing the pixels. Assert which beats receive th **@cardstack/choreo/film**: `Adjustment`, `Filter`, `AttachNode`, `CompiledGraph`, `compileGraph`, `EyeNode`, `GraphNode`, `GroupNode`, `JoinNode`, `Patch`, `PatchNode`, `ShotNode`, `ToNode`, `VoiceNode`, `collectGraph`, `GraphNodeComponent`, `GraphProvider`, `PatchComponent`. -Read the implementation: [`adjust.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/adjust.gts), [`compile.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/compile.ts), [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts). +Read the implementation: [`adjust.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/adjust.gts), [`compile.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/compile.ts), [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-graph.md b/packages/choreo-test-app/app/content/guides/film-graph.md index 22f99088f25..995b264cc47 100644 --- a/packages/choreo-test-app/app/content/guides/film-graph.md +++ b/packages/choreo-test-app/app/content/guides/film-graph.md @@ -39,4 +39,4 @@ The `FilmVocabulary` and `VOCABULARY` exports expose the same typed component se **FilmVocabulary**: `f.Chapter`, `f.Sequence`, `f.Spine`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts), [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts), [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-interface.md b/packages/choreo-test-app/app/content/guides/film-interface.md index 031b6c0f957..e724552f6ab 100644 --- a/packages/choreo-test-app/app/content/guides/film-interface.md +++ b/packages/choreo-test-app/app/content/guides/film-interface.md @@ -37,4 +37,4 @@ Keep debugging metadata out of the public viewing shell. Runtime build identifie **@cardstack/choreo/film**: `Burst`, `Menu`, `MenuEntry`, `PlaybarSegment`, `Player`, `Rail`, `RailMark`, `EndCard`, `Gate`. -Read the implementation: [`player.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/player.gts), [`rail.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/rail.gts), [`titles.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/titles.gts). +Read the implementation: [`player.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/player.gts), [`rail.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/rail.gts), [`titles.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/titles.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-joins.md b/packages/choreo-test-app/app/content/guides/film-joins.md index 190b3c9d452..f0094c1b41d 100644 --- a/packages/choreo-test-app/app/content/guides/film-joins.md +++ b/packages/choreo-test-app/app/content/guides/film-joins.md @@ -36,4 +36,4 @@ Review several frames before, during, and after the join. Confirm the outgoing s **FilmVocabulary**: `f.Join`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts), [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts), [`joins.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/joins.gts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts), [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts), [`joins.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/joins.gts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-media-graph.md b/packages/choreo-test-app/app/content/guides/film-media-graph.md index 92abb074423..657dd15af7c 100644 --- a/packages/choreo-test-app/app/content/guides/film-media-graph.md +++ b/packages/choreo-test-app/app/content/guides/film-media-graph.md @@ -36,4 +36,4 @@ For export, verify the actual source frame after a direct seek. A visually corre **FilmVocabulary**: `f.Attach`, `f.Freeze`, `f.Insert`, `f.Inset`, `f.Video`. -Read the implementation: [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts). +Read the implementation: [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-picture.md b/packages/choreo-test-app/app/content/guides/film-picture.md index 2971f4ae248..b9162cbc083 100644 --- a/packages/choreo-test-app/app/content/guides/film-picture.md +++ b/packages/choreo-test-app/app/content/guides/film-picture.md @@ -37,4 +37,4 @@ Test a direct cold load, a missing subordinate asset, a frame request before ord **@cardstack/choreo/film**: `IframePicture`, `IframePictureSignature`, `PictureSpec`, `Picture`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts), [`picture.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/picture.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts), [`picture.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/picture.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-player.md b/packages/choreo-test-app/app/content/guides/film-player.md index 82fbead8bab..a2370c55fb0 100644 --- a/packages/choreo-test-app/app/content/guides/film-player.md +++ b/packages/choreo-test-app/app/content/guides/film-player.md @@ -38,4 +38,4 @@ Test pause during asynchronous preparation, a replacement run followed by sync, **choreo-player**: `ChoreoRun`, `ChoreoPlayer`, `ChoreoPlayerUpdateOptions`, `ChoreoPlayerOptions`, `createChoreoPlayer`. -Read the implementation: [`index.ts`](https://github.com/cardstack/choreo/blob/main/packages/choreo-player/src/index.ts). +Read the implementation: [`index.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo-player/src/index.ts). diff --git a/packages/choreo-test-app/app/content/guides/film-poses.md b/packages/choreo-test-app/app/content/guides/film-poses.md index ba09fc904f7..989843eca79 100644 --- a/packages/choreo-test-app/app/content/guides/film-poses.md +++ b/packages/choreo-test-app/app/content/guides/film-poses.md @@ -33,4 +33,4 @@ Review a shot in context with its predecessor and successor. A close-up can look **FilmVocabulary**: `f.Eye`, `f.Shot`, `f.To`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts), [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts), [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-schedule.md b/packages/choreo-test-app/app/content/guides/film-schedule.md index 99253f6e090..06a94763921 100644 --- a/packages/choreo-test-app/app/content/guides/film-schedule.md +++ b/packages/choreo-test-app/app/content/guides/film-schedule.md @@ -34,4 +34,4 @@ When publishing runtime information, derive it from this schedule. A hard-coded **@cardstack/choreo/film**: `TICK`, `beatStart`, `chapterHeads`, `Content`, `contents`, `Cue`, `cues`, `joinInto`, `Schedule`, `schedule`, `secsBefore`, `tailFor`, `totalSecs`, `Waypoint`, `waypoints`, `FilmClock`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts), [`film.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/film.gts), [`schedule.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/schedule.ts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts), [`film.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/film.gts), [`schedule.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/schedule.ts). diff --git a/packages/choreo-test-app/app/content/guides/film-seam-readiness.md b/packages/choreo-test-app/app/content/guides/film-seam-readiness.md index f5ccb766bea..c179f8e0fb8 100644 --- a/packages/choreo-test-app/app/content/guides/film-seam-readiness.md +++ b/packages/choreo-test-app/app/content/guides/film-seam-readiness.md @@ -34,4 +34,4 @@ Inspect a cold capture at the first transition and a repeated capture after asse **@cardstack/choreo/film**: `Seam`. -Read the implementation: [`seam.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/seam.ts). +Read the implementation: [`seam.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/seam.ts). diff --git a/packages/choreo-test-app/app/content/guides/film-shots.md b/packages/choreo-test-app/app/content/guides/film-shots.md index dc55f661053..5ffb1a65d88 100644 --- a/packages/choreo-test-app/app/content/guides/film-shots.md +++ b/packages/choreo-test-app/app/content/guides/film-shots.md @@ -19,7 +19,7 @@ A film's default block yields its composition vocabulary. A spine contains chapt Here, `f` is yielded by `Film`. The shot defines a starting camera pose, `f.To` supplies its destination, and `f.Type` adds a title. The camera units belong to the picture's camera model; they are not CSS pixels. -This excerpt is a score, not a standalone renderer. A complete host must also provide the picture and its assets. The [Film reference](https://github.com/cardstack/choreo/blob/main/docs/film.md) documents those host arguments and the picture contract. +This excerpt is a score, not a standalone renderer. A complete host must also provide the picture and its assets. The [Film reference](https://choreo.boxel.site/docs/film.md) documents those host arguments and the picture contract. ## Planning the Edit diff --git a/packages/choreo-test-app/app/content/guides/film-start.md b/packages/choreo-test-app/app/content/guides/film-start.md index 74cadf4ccad..505f371032f 100644 --- a/packages/choreo-test-app/app/content/guides/film-start.md +++ b/packages/choreo-test-app/app/content/guides/film-start.md @@ -49,7 +49,7 @@ Start with [Driving an External Clock](/docs/film-clock), then read [Building a **@cardstack/choreo/film**: `Film`, `FilmContext`, `FilmSignature`. -Read the implementation: [`film.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film.ts), [`film.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/film.gts). +Read the implementation: [`film.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film.ts), [`film.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/film.gts). ## A complete working tutorial diff --git a/packages/choreo-test-app/app/content/guides/film-transport.md b/packages/choreo-test-app/app/content/guides/film-transport.md index 9692dbf4994..6fb26fdd8dd 100644 --- a/packages/choreo-test-app/app/content/guides/film-transport.md +++ b/packages/choreo-test-app/app/content/guides/film-transport.md @@ -33,4 +33,4 @@ Test a cold request in the middle of the film, repeated requests for the same ti **@cardstack/choreo/film**: `FilmHandle`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts). diff --git a/packages/choreo-test-app/app/content/guides/film-typography.md b/packages/choreo-test-app/app/content/guides/film-typography.md index 72262d48d85..7b74254dd58 100644 --- a/packages/choreo-test-app/app/content/guides/film-typography.md +++ b/packages/choreo-test-app/app/content/guides/film-typography.md @@ -35,4 +35,4 @@ Test caption changes during backward seeking and at a clip boundary. A lingering **FilmVocabulary**: `f.Type`. -Read the implementation: [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts), [`overlays.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/overlays.gts), [`plate.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/plate.gts), [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/types.ts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts). +Read the implementation: [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts), [`overlays.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/overlays.gts), [`plate.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/plate.gts), [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/types.ts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts). diff --git a/packages/choreo-test-app/app/content/guides/film-utilities.md b/packages/choreo-test-app/app/content/guides/film-utilities.md index aecda0217f8..2c5322bc38e 100644 --- a/packages/choreo-test-app/app/content/guides/film-utilities.md +++ b/packages/choreo-test-app/app/content/guides/film-utilities.md @@ -37,4 +37,4 @@ Test edge values that your interface can produce: progress before the window, pr **@cardstack/choreo/film**: `clamp01`, `hex`, `lerp`, `luminance`, `mmss`, `RAD`, `rgba`, `smooth`. -Read the implementation: [`math.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/math.ts). +Read the implementation: [`math.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/math.ts). diff --git a/packages/choreo-test-app/app/content/guides/film-world-annotations.md b/packages/choreo-test-app/app/content/guides/film-world-annotations.md index 76a5e3ab1ba..a8efec3b708 100644 --- a/packages/choreo-test-app/app/content/guides/film-world-annotations.md +++ b/packages/choreo-test-app/app/content/guides/film-world-annotations.md @@ -36,4 +36,4 @@ Check the annotation's beginning, its readable interval, and its removal. Test b **FilmVocabulary**: `f.Lineup`, `f.Mark`, `f.Sky`, `f.Stamp`, `f.Trace`. -Read the implementation: [`nodes.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/film/graph/host.gts). +Read the implementation: [`nodes.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/nodes.gts), [`host.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/film/graph/host.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-anchors.md b/packages/choreo-test-app/app/content/guides/interactive-anchors.md index 55afd843a79..3af600cedd9 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-anchors.md +++ b/packages/choreo-test-app/app/content/guides/interactive-anchors.md @@ -46,4 +46,4 @@ Use the build-order demo to inspect the relationship between names, starts, dela **@cardstack/choreo**: `AnchorRef`, `after`, `at`. -Read the implementation: [`anchors.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/anchors.ts). +Read the implementation: [`anchors.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/anchors.ts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-arming.md b/packages/choreo-test-app/app/content/guides/interactive-arming.md index a1864f9e028..28071252211 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-arming.md +++ b/packages/choreo-test-app/app/content/guides/interactive-arming.md @@ -37,4 +37,4 @@ Test no-op navigation, aborted changes, repeated selections, and a successor run **@cardstack/choreo**: `Arming`, `ArmingOptions`, `ArmingRegion`, `createArming`. -Read the implementation: [`arming.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/arming.ts). +Read the implementation: [`arming.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/arming.ts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-beacons.md b/packages/choreo-test-app/app/content/guides/interactive-beacons.md index c42bd4135a3..843a7b1bbcd 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-beacons.md +++ b/packages/choreo-test-app/app/content/guides/interactive-beacons.md @@ -40,7 +40,7 @@ Update the source and destination state together. Splitting the removal and inse The [inbox demo](/inbox) demonstrates movement toward a destination. The [far match demo](/far) demonstrates an item changing regions. Compare their state changes before choosing a pattern. -Read the [nested Choreo reference](https://github.com/cardstack/choreo/blob/main/docs/nested-choreo.md) for selection boundaries and cross-region matching rules. +Read the [nested Choreo reference](https://choreo.boxel.site/docs/nested-choreo.md) for selection boundaries and cross-region matching rules. ## Owning the Destination @@ -52,4 +52,4 @@ A BeaconRef identifies measured geometry; it does not transfer ownership of the **ChoreoContext**: `c.beacon`. -Read the implementation: [`beacon.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/beacon.ts), [`beacons.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/beacons.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`beacon.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/beacon.ts), [`beacons.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/beacons.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-blocks.md b/packages/choreo-test-app/app/content/guides/interactive-blocks.md index 5c7d3c8c685..22e0c36e59b 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-blocks.md +++ b/packages/choreo-test-app/app/content/guides/interactive-blocks.md @@ -37,4 +37,4 @@ Gates belong to an ordered sequence and cannot be placed under a parallel contex **ChoreoContext**: `c.Parallel`, `c.Sequence`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-commands.md b/packages/choreo-test-app/app/content/guides/interactive-commands.md index f383e821587..38c560890ba 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-commands.md +++ b/packages/choreo-test-app/app/content/guides/interactive-commands.md @@ -37,4 +37,4 @@ Test direct seeks to moments after several commands, then seek backward and forw **ChoreoContext**: `c.Perform`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-composites.md b/packages/choreo-test-app/app/content/guides/interactive-composites.md index f00097499d2..68ac96d33ea 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-composites.md +++ b/packages/choreo-test-app/app/content/guides/interactive-composites.md @@ -43,4 +43,4 @@ Test the composite from outside the library: render two instances, anchor a late **@cardstack/choreo**: `StepArgs`, `StepArgsBase`, `StepComponent`, `toMs`, `Step`, `TimelineNode`. -Read the implementation: [`steps.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/steps.gts), [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts). +Read the implementation: [`steps.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/steps.gts), [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-crossings.md b/packages/choreo-test-app/app/content/guides/interactive-crossings.md index 62cea1708c0..6a7f9d2d66f 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-crossings.md +++ b/packages/choreo-test-app/app/content/guides/interactive-crossings.md @@ -36,4 +36,4 @@ Use browser view transitions only when a captured representation is acceptable. **ChoreoContext**: `c.Crossing`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-delivery.md b/packages/choreo-test-app/app/content/guides/interactive-delivery.md index 163eaf27a6e..5fdda34acd4 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-delivery.md +++ b/packages/choreo-test-app/app/content/guides/interactive-delivery.md @@ -30,4 +30,4 @@ Inspect the build-order and presentation examples for timing relationships aroun **@cardstack/choreo**: `DeliveryBy`, `DeliveryOrder`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-follow.md b/packages/choreo-test-app/app/content/guides/interactive-follow.md index 68a12a7d063..dc0bf6de14f 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-follow.md +++ b/packages/choreo-test-app/app/content/guides/interactive-follow.md @@ -44,4 +44,4 @@ Test the relationship throughout the flight and after interruption. Checking onl **ChoreoContext**: `c.Follow`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-gates.md b/packages/choreo-test-app/app/content/guides/interactive-gates.md index 6b9992f88b6..03a9c2585b7 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-gates.md +++ b/packages/choreo-test-app/app/content/guides/interactive-gates.md @@ -35,4 +35,4 @@ Test parked states with `animationsSettled()` and use `advanceGate()` where a te **ChoreoContext**: `c.Gate`, `c.advance`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-holds.md b/packages/choreo-test-app/app/content/guides/interactive-holds.md index 10cef1904f1..74f3fba685b 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-holds.md +++ b/packages/choreo-test-app/app/content/guides/interactive-holds.md @@ -33,4 +33,4 @@ Test a hold's beginning, its tail, and the state after its surrounding region ch **ChoreoContext**: `c.Hold`, `c.Wait`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-move.md b/packages/choreo-test-app/app/content/guides/interactive-move.md index 5e35c39bdea..8df17503bff 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-move.md +++ b/packages/choreo-test-app/app/content/guides/interactive-move.md @@ -29,4 +29,4 @@ Watch the first frame, interruption, and landing. The first frame should reprodu **ChoreoContext**: `c.Move`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-property-steps.md b/packages/choreo-test-app/app/content/guides/interactive-property-steps.md index e2e271c118b..4f2f26b44ac 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-property-steps.md +++ b/packages/choreo-test-app/app/content/guides/interactive-property-steps.md @@ -34,4 +34,4 @@ Review both selected and unselected participants. A query that accidentally incl **ChoreoContext**: `c.Spring`, `c.Tween`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-queries.md b/packages/choreo-test-app/app/content/guides/interactive-queries.md index 39b19176514..027a4d55b20 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-queries.md +++ b/packages/choreo-test-app/app/content/guides/interactive-queries.md @@ -34,4 +34,4 @@ Give each logical subject a stable identity and use roles for choreography categ **ChoreoContext**: `c.all`, `c.counterpart`, `c.id`, `c.inserted`, `c.kept`, `c.moved`, `c.onstage`, `c.received`, `c.removed`, `c.role`, `c.still`. -Read the implementation: [`changeset.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/changeset.ts), [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`changeset.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/changeset.ts), [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-raise-scroll.md b/packages/choreo-test-app/app/content/guides/interactive-raise-scroll.md index c5bdb93164b..20c1c33ab58 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-raise-scroll.md +++ b/packages/choreo-test-app/app/content/guides/interactive-raise-scroll.md @@ -35,4 +35,4 @@ Keep these operations separate from a 3D camera. Raising a DOM participant chang **ChoreoContext**: `c.Raise`, `c.Scroll`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-registry.md b/packages/choreo-test-app/app/content/guides/interactive-registry.md index e241f7e48ef..13fd356f027 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-registry.md +++ b/packages/choreo-test-app/app/content/guides/interactive-registry.md @@ -34,4 +34,4 @@ Test lookup before mount, after replacement, and after teardown. Verify that des **@cardstack/choreo**: `ChoreoHost`, `choreoHostAt`, `choreoHostById`, `ChoreoProvider`, `closestChoreo`. -Read the implementation: [`registry.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/registry.ts). +Read the implementation: [`registry.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/registry.ts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-run.md b/packages/choreo-test-app/app/content/guides/interactive-run.md index 3a9267aa501..cc7d20655d8 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-run.md +++ b/packages/choreo-test-app/app/content/guides/interactive-run.md @@ -39,4 +39,4 @@ The key invariant is that state changes remain responsive while the visual expla **ChoreoContext**: `c.run`. -Read the implementation: [`run.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/run.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`run.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/run.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-start.md b/packages/choreo-test-app/app/content/guides/interactive-start.md index 5fa36b409fd..0caaabe7edf 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-start.md +++ b/packages/choreo-test-app/app/content/guides/interactive-start.md @@ -58,4 +58,4 @@ The [sequence demo](/sequence) makes that ordering visible. Continue with [Writi **@cardstack/choreo**: `ChoreoContext`, `Choreo`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-tethers.md b/packages/choreo-test-app/app/content/guides/interactive-tethers.md index 679a0ab70a8..e03a9e60d38 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-tethers.md +++ b/packages/choreo-test-app/app/content/guides/interactive-tethers.md @@ -40,4 +40,4 @@ Like other derived geometry, a tether performs per-frame work on the main thread **ChoreoContext**: `c.Tether`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/interactive-timelines.md b/packages/choreo-test-app/app/content/guides/interactive-timelines.md index 7df86ff07ca..579c4946607 100644 --- a/packages/choreo-test-app/app/content/guides/interactive-timelines.md +++ b/packages/choreo-test-app/app/content/guides/interactive-timelines.md @@ -40,7 +40,7 @@ Durations are expressed in seconds with `@duration`. Springs can determine their Keep the ordering in the timeline. Scheduling the next action with `setTimeout` creates a second clock that can drift when you change a duration, pause playback, or interrupt the interaction. -Try the [interrupt demo](/interrupt) to see a new state change arrive before the previous movement has completed. For the full step vocabulary, read the [construct reference](https://github.com/cardstack/choreo/blob/main/docs/choreo-constructs.md). +Try the [interrupt demo](/interrupt) to see a new state change arrive before the previous movement has completed. For the full step vocabulary, read the [construct reference](https://choreo.boxel.site/docs/choreo-constructs.md). ## Where to Go Deeper diff --git a/packages/choreo-test-app/app/content/guides/spatial-camera3d.md b/packages/choreo-test-app/app/content/guides/spatial-camera3d.md index ff540b46cf6..fed29ccfd67 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-camera3d.md +++ b/packages/choreo-test-app/app/content/guides/spatial-camera3d.md @@ -34,4 +34,4 @@ Verify a cold start, a paused midpoint, a direct seek, and an interrupted target **ChoreoContext**: `c.Camera3D`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/spatial-coordinates.md b/packages/choreo-test-app/app/content/guides/spatial-coordinates.md index d76cea6c598..b286f46b9c5 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-coordinates.md +++ b/packages/choreo-test-app/app/content/guides/spatial-coordinates.md @@ -33,4 +33,4 @@ Test round trips as arithmetic, then test the visible interface under the actual **@cardstack/choreo**: `PlanePoint`, `appliedCamera`, `toLocal`, `toPage`, `Rect`. -Read the implementation: [`space.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/space.ts), [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts). +Read the implementation: [`space.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/space.ts), [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts). diff --git a/packages/choreo-test-app/app/content/guides/spatial-dom.md b/packages/choreo-test-app/app/content/guides/spatial-dom.md index 76fac0fa653..f247530f11c 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-dom.md +++ b/packages/choreo-test-app/app/content/guides/spatial-dom.md @@ -31,6 +31,6 @@ At a close reading position, aim for a useful native CSS scale. Inspect the resu Check a real control after moving the camera. Its visible position and hit target must agree. Test scrolling, dragging, and keyboard focus, as well as ordinary clicks. If a model should obscure part of the screen, verify that the visual and input behavior agree about that overlap. -The [long take demo](/long-take) combines a device view with a live screen. The [DOM in 3D reference](https://github.com/cardstack/choreo/blob/main/docs/dom-in-3d.md) explains the projection and occlusion decisions in more detail. +The [long take demo](/long-take) combines a device view with a live screen. The [DOM in 3D reference](https://choreo.boxel.site/docs/dom-in-3d.md) explains the projection and occlusion decisions in more detail. Check the relationship after a viewport resize as well as a camera move. Both the renderer projection and the DOM plane dimensions need to reflect the new frame before the next interaction. diff --git a/packages/choreo-test-app/app/content/guides/spatial-first-scene.md b/packages/choreo-test-app/app/content/guides/spatial-first-scene.md index 14f80e89695..2bc82ca86c0 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-first-scene.md +++ b/packages/choreo-test-app/app/content/guides/spatial-first-scene.md @@ -50,4 +50,4 @@ When adding a real 3D device, share the renderer's projection and object matrice Before adding assets, keep this example's useful checks: the visible button and its hit target agree after movement, changing application state does not reset the camera, resizing does not clip essential controls, and leaving the route stops its work. The browser verification command in the first tutorial checks that the angled button remains usable and that the transition changes the plane transform. -The complete source is [spatial-card.gts](https://github.com/cardstack/choreo/blob/main/test-app/app/components/tutorials/spatial-card.gts). Once that host boundary is clear, continue to [recording a scene](/docs/film-first-export) to learn how an external clock can request completed frames. +The complete source is [spatial-card.gts](https://github.com/cardstack/boxel/blob/main/packages/choreo-test-app/app/components/tutorials/spatial-card.gts). Once that host boundary is clear, continue to [recording a scene](/docs/film-first-export) to learn how an external clock can request completed frames. diff --git a/packages/choreo-test-app/app/content/guides/spatial-frame.md b/packages/choreo-test-app/app/content/guides/spatial-frame.md index 8911b5e51f7..d475faeb53a 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-frame.md +++ b/packages/choreo-test-app/app/content/guides/spatial-frame.md @@ -35,4 +35,4 @@ Measure the subject in its real expanded layout. A card that fits when collapsed **ChoreoContext**: `c.Aim`, `c.Camera`, `c.Frame`, `c.camera`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts), [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts), [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/spatial-paths.md b/packages/choreo-test-app/app/content/guides/spatial-paths.md index 3b3a0b14fbf..03ac755866d 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-paths.md +++ b/packages/choreo-test-app/app/content/guides/spatial-paths.md @@ -29,4 +29,4 @@ Review the result at the distribution frame rate. Check velocity and curvature t **@cardstack/choreo**: `Camera3DWaypoint`. -Read the implementation: [`types.ts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo/types.ts). +Read the implementation: [`types.ts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/types.ts). diff --git a/packages/choreo-test-app/app/content/guides/spatial-relative.md b/packages/choreo-test-app/app/content/guides/spatial-relative.md index bb6464367a0..a40d25348ed 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-relative.md +++ b/packages/choreo-test-app/app/content/guides/spatial-relative.md @@ -32,4 +32,4 @@ Test a relative step after more than one possible preceding frame. The benefit o **ChoreoContext**: `c.Pan`, `c.SlowZoom`. -Read the implementation: [`choreo.gts`](https://github.com/cardstack/choreo/blob/main/packages/glimmer-motion/src/choreo.gts). +Read the implementation: [`choreo.gts`](https://github.com/cardstack/boxel/blob/main/packages/choreo/src/choreo.gts). diff --git a/packages/choreo-test-app/app/content/guides/spatial-start.md b/packages/choreo-test-app/app/content/guides/spatial-start.md index 563db9cfce3..3d74f1ad241 100644 --- a/packages/choreo-test-app/app/content/guides/spatial-start.md +++ b/packages/choreo-test-app/app/content/guides/spatial-start.md @@ -24,7 +24,7 @@ In Choreo's mockup demos, a WebGL scene draws the device and a DOM plane supplie Choreo's `c.Camera3D` directs camera values through the timeline. The renderer consumes those values; it continues to own lighting, geometry, and projection. This boundary lets a camera movement be paused or sought without introducing a second animation loop for the shot. -Open the [mockup demo](/mockup) and switch to 3D to see the screen remain live as the device moves. The [DOM in 3D reference](https://github.com/cardstack/choreo/blob/main/docs/dom-in-3d.md) explains the matrix mapping used by the examples. +Open the [mockup demo](/mockup) and switch to 3D to see the screen remain live as the device moves. The [DOM in 3D reference](https://choreo.boxel.site/docs/dom-in-3d.md) explains the matrix mapping used by the examples. ## Exploring the Gallery diff --git a/packages/choreo-test-app/app/lib/compositor.ts b/packages/choreo-test-app/app/lib/compositor.ts index c40a423da73..5869ef401fe 100644 --- a/packages/choreo-test-app/app/lib/compositor.ts +++ b/packages/choreo-test-app/app/lib/compositor.ts @@ -1,5 +1,5 @@ /** - * The C1 composition host (notes/choreo-composition.md, Phase C1) — + * The C1 composition host (packages/choreo-gallery/realm/design/choreo-composition.md, Phase C1) — * application-side code on purpose. It owns the scene-level model a * directed composition needs and consumes only public surfaces: * `choreo-player` for the run transport, and actor "ports" the @@ -66,7 +66,7 @@ export interface ActorPorts { /** * A clip: an activation and a source-time window over one actor - * (notes/choreo-composition.md, Phase C3). Pure parent→source arithmetic: + * (packages/choreo-gallery/realm/design/choreo-composition.md, Phase C3). Pure parent→source arithmetic: * * source = sourceIn + (parent − at) × rate * window = (sourceOut − sourceIn) / rate seconds of parent time @@ -208,7 +208,7 @@ export class Compositor { * Preview passes `parameters: false`: a parameter channel is capture * reconstruction — it stands a demo's own engine at a still, which * would fight the very playback the preview exists to show. play() is - * GPU; renderAt(t) is a still (notes/choreo-composition.md, scope + * GPU; renderAt(t) is a still (packages/choreo-gallery/realm/design/choreo-composition.md, scope * decisions). */ async foldTo( diff --git a/packages/choreo-test-app/app/lib/dial.ts b/packages/choreo-test-app/app/lib/dial.ts index 45d23589473..8f39a601405 100644 --- a/packages/choreo-test-app/app/lib/dial.ts +++ b/packages/choreo-test-app/app/lib/dial.ts @@ -4,7 +4,7 @@ * dialkit ships a framework-free core (`dialkit/store`, 29KB, importing * neither motion nor react) plus four independent UI ports. There is no Ember * port, and the point of this file is to find out what one would cost — see - * notes/dialkit.md. Everything below is the bridge and nothing else; the panel + * packages/choreo-gallery/realm/design/dialkit.md. Everything below is the bridge and nothing else; the panel * that draws it is `dial-panel.gts`. * * ## The whole question, in one class diff --git a/packages/choreo-test-app/app/lib/drift.ts b/packages/choreo-test-app/app/lib/drift.ts index 0d335e3e78d..15e7d567f7b 100644 --- a/packages/choreo-test-app/app/lib/drift.ts +++ b/packages/choreo-test-app/app/lib/drift.ts @@ -1,7 +1,7 @@ /** * A car, as arithmetic — and the part of Drift that is deliberately not Choreo. * - * `notes/drift.md` says to write this first, against hardcoded numbers, because + * `packages/choreo-gallery/realm/design/drift.md` says to write this first, against hardcoded numbers, because * it is the fastest way to find out whether the car is fun before any of the * tuning surface exists. It also says, at length, why it cannot be a score: * @@ -388,7 +388,7 @@ export const slipAngleOf = (car: Car) => * thing entirely — a target that moves every frame and a lens that never gets * there. Compiling a region sixty times a second to express that would be a * misuse of the score and a bad advertisement for it, so the shot lives here, - * with the physics it is chasing. `notes/drift.md` names the split; this is it. + * with the physics it is chasing. `packages/choreo-gallery/realm/design/drift.md` names the split; this is it. * * The lead is what makes it read as a camera operator rather than a leash: the * target is not the car, it is where the car will be in `LEAD` seconds, so the @@ -841,7 +841,7 @@ export function autopilot( /* --------------------------------------------------------- personalities */ /** - * The four characters, from `notes/drift.md`. + * The four characters, from `packages/choreo-gallery/realm/design/drift.md`. * * These are constants and not saved presets on purpose. A character is a thing * the stage ships and can always be got back to; a preset is a thing you made. diff --git a/packages/choreo-test-app/app/lib/films/sagrada.ts b/packages/choreo-test-app/app/lib/films/sagrada.ts index cff09890b7e..94c2037466d 100644 --- a/packages/choreo-test-app/app/lib/films/sagrada.ts +++ b/packages/choreo-test-app/app/lib/films/sagrada.ts @@ -17,7 +17,7 @@ import { hex, RAD } from '@cardstack/choreo/film/math'; /** * SAGRADA — a film, at `/sagrada`. * - * The second reference film for the film construct (notes/film-construct.md), + * The second reference film for the film construct (packages/choreo-gallery/realm/design/film-construct.md), * and the first one cut ON it: everything here is the shot list, the * script, the chapters, the year clock, the traces sampled off the model, * and the front and back matter. The engine — the chased lens, the joins, @@ -167,7 +167,7 @@ export const VO_GAIN: Record = { * files in `public/sagrada/vo/`. The kinetic type is paced against the VOICE, * not against the beat: the last cue should land as the line is finishing, * whether the read is four seconds or thirteen. Estimating this went wrong - * once already (see notes/towers-vo.md) — so it is measured, and a re-record + * once already (see packages/choreo-gallery/realm/design/towers-vo.md) — so it is measured, and a re-record * means re-measuring. A beat missing from here paces against its own length. */ export const VO_SECS: Record = { diff --git a/packages/choreo-test-app/app/lib/films/towers.ts b/packages/choreo-test-app/app/lib/films/towers.ts index 510ee9d07cf..9aea6378bdd 100644 --- a/packages/choreo-test-app/app/lib/films/towers.ts +++ b/packages/choreo-test-app/app/lib/films/towers.ts @@ -10,7 +10,7 @@ import { hex, RAD } from '@cardstack/choreo/film/math'; /** * TOWERS — a film, at `/towers`. * - * The first reference film for the film construct (notes/film-construct.md), + * The first reference film for the film construct (packages/choreo-gallery/realm/design/film-construct.md), * now cut ON it: what is left here is the shot list, the script, the * chapters, the six climates, the traces sampled off the keep, and the * front and back matter in the film's own Japanese voice. The engine — @@ -226,7 +226,7 @@ export const VO_GAIN: Record = {}; * files in `public/towers/vo/`. The kinetic type is paced against the VOICE, * not against the beat: the last cue should land as the line is finishing, * whether the read is four seconds or thirteen. Estimating this went wrong - * once already (see notes/towers-vo.md) — so it is measured, and a re-record + * once already (see packages/choreo-gallery/realm/design/towers-vo.md) — so it is measured, and a re-record * means re-measuring. A beat missing from here paces against its own length. */ export const VO_SECS: Record = { diff --git a/packages/choreo-test-app/app/lib/sylva/scene.ts b/packages/choreo-test-app/app/lib/sylva/scene.ts index 26550261d42..7654f9a790f 100644 --- a/packages/choreo-test-app/app/lib/sylva/scene.ts +++ b/packages/choreo-test-app/app/lib/sylva/scene.ts @@ -19,7 +19,7 @@ * * WHY THIS SCENE. It was picked for its geometry, not its looks: a moss root * with an arch and a hollow under it is a shape that can stand in FRONT of a - * DOM card, which is exactly the case `docs/dom-in-3d.md` lists as unproven. + * DOM card, which is exactly the case `packages/choreo-gallery/realm/docs/dom-in-3d.md` lists as unproven. * A flat backdrop could not test occlusion at all. * * The one lucky alignment: its camera is already authored in pixels — @@ -3170,7 +3170,7 @@ export function createSylva(hosts: SylvaHosts): Sylva { * branch it is naming the moment the composition reflows. * * The proxy is the whole occlusion answer, and it is the piece - * `docs/dom-in-3d.md` calls unproven: a plane at the card's pose with + * `packages/choreo-gallery/realm/docs/dom-in-3d.md` calls unproven: a plane at the card's pose with * `NoBlending`, which writes its own alpha straight into the framebuffer and * so cuts a card-shaped hole for the DOM underneath. Being ordinary * geometry, it is depth-tested like everything else — so moss standing diff --git a/packages/choreo-test-app/app/routes/application.ts b/packages/choreo-test-app/app/routes/application.ts index 63fa58668ee..28c6e11f4b0 100644 --- a/packages/choreo-test-app/app/routes/application.ts +++ b/packages/choreo-test-app/app/routes/application.ts @@ -13,7 +13,7 @@ import { beginCrossing } from 'test-app/lib/crossing'; * the thirteen load-bearing subtleties they needed. The transition belongs * to the library now: `` in the application template treats * the route swap as one render pass, and its timeline says the whole move. - * The region stays router-agnostic by design (docs/choreo-constructs.md §9), + * The region stays router-agnostic by design (packages/choreo-gallery/realm/docs/choreo-constructs.md §9), * so the one thing left for the route layer is telling the app a crossing * has begun — see app/lib/crossing.ts. */ diff --git a/packages/choreo-test-app/app/styles/app.css b/packages/choreo-test-app/app/styles/app.css index 06504a51839..44037f005c0 100644 --- a/packages/choreo-test-app/app/styles/app.css +++ b/packages/choreo-test-app/app/styles/app.css @@ -12992,7 +12992,7 @@ html[data-gm-dragging] * { package's stylesheet. THE HANDLE IS THE EXCEPTION, and it is the one correction this spike makes - to the "the stylesheet is free" claim in notes/dialkit.md. dialkit's theme + to the "the stylesheet is free" claim in packages/choreo-gallery/realm/design/dialkit.md. dialkit's theme gives `.dialkit-slider-handle` only `top: 50%`; the centring translate and the resting scale live in the React component's inline `style` and `animate` props (`y: '-50%'`, `scaleX: isActive ? 1 : 0.25`). A port that diff --git a/packages/choreo-test-app/public/asset/sagrada/README.md b/packages/choreo-test-app/public/asset/sagrada/README.md index 951ea8946e4..cf3d2081471 100644 --- a/packages/choreo-test-app/public/asset/sagrada/README.md +++ b/packages/choreo-test-app/public/asset/sagrada/README.md @@ -7,8 +7,8 @@ This renderer requires WebGL2. Directional, hemisphere and ambient light intensities retain the old legacy-light PI factor explicitly. Film shots and score timing are not part of this renderer migration. -The façade pass is documented in `docs/sagrada-web-model.md` at the repo -root. `facade-craft.js` builds merged sculptural surface detail; +The façade pass is documented in +`packages/choreo-gallery/realm/design/sagrada-web-model.md`. `facade-craft.js` builds merged sculptural surface detail; `relief-atlas.js` is an optional, on-demand height-field recipe. The `textures/` files replace the old inline base64 table. This is still a procedural interpretation, not a surveyed reconstruction. diff --git a/packages/choreo-test-app/public/asset/towers-model.html b/packages/choreo-test-app/public/asset/towers-model.html index 61d741e93aa..b457f65d1e6 100644 --- a/packages/choreo-test-app/public/asset/towers-model.html +++ b/packages/choreo-test-app/public/asset/towers-model.html @@ -17,7 +17,7 @@ master/voice bus mixer with narration on the graph, winter (settled snow and a pinned blizzard on the shot's clock), lightning on cue, the page's own UI hidden from the first paint under html[data-hosted], and the - lightning flash plane hidden when unlit. docs/towers-porting-delta.md + lightning flash plane hidden when unlit. packages/choreo-gallery/realm/design/towers-porting-delta.md keeps the list of what upstream can do that the film does not use yet. --> diff --git a/packages/choreo-test-app/public/asset/towers/vo/README.md b/packages/choreo-test-app/public/asset/towers/vo/README.md index 713158f504e..04830b9f564 100644 --- a/packages/choreo-test-app/public/asset/towers/vo/README.md +++ b/packages/choreo-test-app/public/asset/towers/vo/README.md @@ -8,7 +8,7 @@ One audio file per beat, named for the beat's id: ... The full list — every beat's window, word count and line — is in -[`notes/towers-vo.md`](../../../../../notes/towers-vo.md), together with the voice direction. That file is +[`towers-vo.md`](../../../../../choreo-gallery/realm/design/towers-vo.md), together with the voice direction. That file is GENERATED from the beats in the film score and `test-app/app/lib/films/towers.ts`; regenerate it rather than editing it, or the narration and the shot list drift apart. diff --git a/packages/choreo-test-app/tests/unit/drift-test.ts b/packages/choreo-test-app/tests/unit/drift-test.ts index adf43ca47f2..08383074dab 100644 --- a/packages/choreo-test-app/tests/unit/drift-test.ts +++ b/packages/choreo-test-app/tests/unit/drift-test.ts @@ -1,7 +1,7 @@ /** * The car, before there is a car to look at. * - * `docs/drift.md` argues that the driving loop is not a Choreo score and has to + * `packages/choreo-gallery/realm/design/drift.md` argues that the driving loop is not a Choreo score and has to * be written by hand. The corollary is that nothing else in the repo is * checking it: no region compiles it, no changeset covers it, and a bug in it * looks exactly like a car that needs tuning. So the model gets its own tests, diff --git a/packages/choreo-test-app/vite.config.mjs b/packages/choreo-test-app/vite.config.mjs index e28bb849128..7b0b2b33ced 100644 --- a/packages/choreo-test-app/vite.config.mjs +++ b/packages/choreo-test-app/vite.config.mjs @@ -48,7 +48,7 @@ vendorDraco(); /** * DIALKIT'S STYLESHEET COMES FROM THE PACKAGE, not from the repository. * - * The dial spike (docs/dialkit.md) leans on the fact that all four of + * The dial spike (packages/choreo-gallery/realm/design/dialkit.md) leans on the fact that all four of * dialkit's UI ports render the same `dialkit-*` class names against one * shared `theme.css` — so an Ember port that emits the same markup inherits * the whole visual design. That only pays off if the stylesheet stays the diff --git a/packages/choreo/README.md b/packages/choreo/README.md index e9ea2d33fd9..0086436aa1a 100644 --- a/packages/choreo/README.md +++ b/packages/choreo/README.md @@ -1,9 +1,18 @@ -# @cardstack/choreo +
-**Motion, choreographed.** `` watches a region's render passes, hands each one's changeset -(inserted, removed and kept participants, with their bounds before and after) to a timeline declared -inside it, and plays that timeline on the [Motion](https://motion.dev) engine. It is built on -[`glimmer-motion`](../glimmer-motion), whose `{{motion}}` elements are the participants. +

@cardstack/choreo

+ +**Motion, choreographed.** + +`` watches a region's render pass, measures its **changeset** (what was inserted, removed +and kept, and where everything stood before and after) and plays a score you declare over it. +Magic Move crossings, gates you click through, wires drawn every frame, and values derived from +other elements' measurements are all interruptible mid-flight, by design. + +**Live gallery:** [**choreo.boxel.site**](https://choreo.boxel.site/): every demo, with its source +and its guide. + +
```gts import { Choreo } from '@cardstack/choreo'; @@ -11,29 +20,80 @@ import { motion } from 'glimmer-motion'; ``` +

+ The Choreo gallery +

+ +Choreo is built on [glimmer-motion](https://github.com/cardstack/boxel/tree/main/packages/glimmer-motion#readme), +which binds [Motion](https://motion.dev)'s framework-free engine to Glimmer. Its `{{motion}}` +elements are a region's participants, and Motion's engine plays every step. The third package, +[@cardstack/choreo-player](https://github.com/cardstack/boxel/tree/main/packages/choreo-player#readme), +is a dependency-free headless transport for clocking Choreo runs from outside: a video renderer, a +capture worker, a scrub UI. + +**New here?** [The guide](https://choreo.boxel.site/docs/guide.md) teaches the binding and then +the region from a Glimmer card. The [documentation](https://choreo.boxel.site/docs/README.md) +goes on to the model, the full construct reference, the open step vocabulary, nested regions, +splices, cameras and ``. + +- [Why choreography?](#why-choreography) +- [Install](#install) · [Entry points](#entry-points) +- [The design decisions](#the-design-decisions) +- [A tour of the vocabulary](#a-tour-of-the-vocabulary) +- [Where it earns its keep](#where-it-earns-its-keep) +- [Interruption, tested](#interruption-tested) +- [External clocks](#external-clocks) · [DOM in 3D](#dom-in-3d-ccamera3d) · [The film](#the-film-film) +- [API](#api) · [How it plugs into glimmer-motion](#how-it-plugs-into-glimmer-motion) · [Architecture](#architecture) + +## Why choreography? + +Element-level animation answers "how does this element move?", and for one element that is the +right question. But most of what a product ships is _scenes_: a card opens into a page, a row grows +into a detail panel, a slide builds in steps, a dropped card springs into its slot while its +neighbours make room. In a scene, elements don't have independent motion. They have +**relationships**: this one leaves _before_ that one flies; this badge rides _that_ card; this panel +grows out of _that_ row; everything freezes _while_ the hero crosses. + +Per-element APIs make you rebuild those relationships by hand: measure rects imperatively, spawn +clone overlays, chain `setTimeout`s, hide the real element while a copy flies, and hope nobody +clicks mid-flight. Every one of those mechanisms turned up, and was replaced by a declared timeline, +when a real product workspace was ported onto this library: the clones, the `afterRender` +measurement hooks, the hand-matched timer pairs. + +The changeset is what makes the declarative form possible. The region measures **everything, on +both sides of every pass**, so a timeline never needs a hand-measured rect. A step names _which +sprites_ (by id, by role, or by what happened to them: `inserted`, `removed`, `moved`, `received`) +and _what to do_, and the geometry is already there. Every value is a function of the run's clock +over measured endpoints, so a second click mid-flight simply re-measures and re-aims. Interruption +isn't an edge case to defend against. It is the normal case the whole model is built around. + ## Install ``` pnpm add @cardstack/choreo glimmer-motion ``` -Peers: `glimmer-motion` at exactly the same version (the two packages release together), `motion-dom` / -`motion-utils` in the same ranges glimmer-motion declares, so both packages share one engine, -`ember-modifier`, `@glimmer/component`, `ember-source >= 5.4`. A v2 addon with -TypeScript types and Glint signatures. +Peers: `glimmer-motion` at exactly the same version (the two packages release together), and +`motion-dom` / `motion-utils` in the ranges glimmer-motion declares, so both packages share one +engine. Also `ember-modifier`, `@glimmer/component` and `ember-source >= 5.4`. It is a v2 addon +with TypeScript types and Glint signatures. + +Cards in a [Boxel](https://github.com/cardstack/boxel) realm import `@cardstack/choreo` and +`glimmer-motion` by their package names. The Boxel host provides both, so a realm carries no copy +of either. ## Entry points @@ -41,18 +101,493 @@ TypeScript types and Glint signatures. | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `@cardstack/choreo` | `Choreo` and its yielded vocabulary, `beacon`, anchors (`at`, `after`), `createArming`, easings, the region registry, plane-space helpers, `StepComponent` and the timeline types | | `@cardstack/choreo/film` | the film construct: `Film`, its clips, joins, overlays, player and graph | -| `@cardstack/choreo/test-support` | `setupChoreo(hooks)`, `advanceGate()`, `seekTo()`, `live()`, `orphanCount()`, `strandedTransforms()` | +| `@cardstack/choreo/test-support` | `setupChoreo(hooks)`, `advanceGate()`, `seekTo()`, `live()`, `liveAll()`, `orphanCount()`, `strandedTransforms()` | Deep imports (`@cardstack/choreo/compile`, `@cardstack/choreo/film/math`, …) are the same modules. +## The design decisions + +Twelve decisions make Choreo feel the way it does. Tests enforce each one, and most were paid for +the hard way. + +- **The scene is the unit.** A region owns a subtree; participants opt in with + `{{motion id= role=}}`; each render pass is one changeset, one timeline, one run. Two regions are + two scenes with separate clocks. Nesting is legal, because an inner region measures its own pass + correctly even while an outer one is flying the box it lives in. +- **Leavers belong to the timeline.** A removed participant stays on screen, locked where it stood, + for exactly as long as the score names it. The region parks it in an orphan layer and releases it + when its row ends. There's no `` inside a scene, no clones, and no "keep this in state a + bit longer" flags. +- **Identity pairs across renders.** An inserted id that replaces a removed one _claims_ it: one + flight, two skins, crossfaded over the moving box. The same bare id in two different regions pairs + across them (**far matching**), and the arrival flies from the departure's box as one continuous + move. Pairing also makes "back" trail-correct for free: a closing page flies to wherever its + identity truly renders next. +- **z-index is a window; elevation is a layer.** `c.Hold` sets any property for the span of its + block and hands it back, which retires the whole "timed z-index flag" genre. `c.Raise` is for what + z-index can't say: promotion to a real layer above every stacking context and overflow clip in the + region, for the window, with the slot held by a placeholder. +- **A gate is a pause; back is a hold.** `c.Gate` parks the run until `advance()` (Keynote's click), + `@delay` opens it by itself, and `advance()` mid-segment is the click-through: complete instantly, + park at the gate. `run.retreat()` is Keynote's back rule as a verb. It lands parked at the previous + gate with everything ahead re-closed, and **holds**: nothing plays, and no `@delay` gate opens by + itself, until the user advances and the build replays forward. +- **Interruption is the doctrine.** A sprite mid-flight reads as _moved_ on every re-pass, and its + `initial` is the painted box, so a replacement run starts from what is on screen, with velocities + handed over. A replacement pass may not drop a flight either: anything the prior run was driving + that the new score doesn't name gets a synthesized continuation to its rest. Without that rule, + any unrelated re-render could snap a flying element home in one frame. +- **Unnamed means no animation.** A reflow the score doesn't name lands instantly, and apps rely on + that. The continuity rule above is scoped to what was _flying_; layout stays layout. +- **A beacon is a place, not an identity.** `{{beacon 'trash'}}` publishes a box, and `@from` / + `@to` borrow it as one end of a flight. The bin never joins a changeset and never deforms, and the + pass measures it fresh each time, _after_ the layout it sits in has re-solved. There's nothing to + predict about where the slot will be. +- **Derived values never read the page.** `c.Follow` computes one sprite's properties from another's + _measurements_. Each source arrives as `{ from, to, now }`: resting boxes from the pass, plus a + current position composed from the values the run is already driving. The follower's own box + arrives as `rest`, which can't contain what the follower writes. A follower is pure by + construction, so it can't feed its own output back or force a style recalculation mid-move. The + obvious API (live boxes) shipped first and failed five ways; + [the post-mortem](https://choreo.boxel.site/docs/postmortem-follow.md) is part of the documentation. +- **Some ink stands.** An open-ended `c.Tether` draws its wire every frame and every still, + indefinitely: a standing run that reports settled while it ticks. Tether, raise and scroll cues + compare by identity across re-renders, so a busy page can't tear a standing wire down and fade it + back in sixty times a second. +- **The vocabulary is open.** Blocks answer to the anchor system (`@name` a block, then + `@at={{at 'carry'}}` a step against it), and a composite step is a published seam. Subclass + `StepComponent`, return a node tree, and your app speaks its own words (``, ``, + ``), with `generic: true` children that yield to anything more specific in the same score. +- **Time is a value.** `run.time` is settable in both directions, setting it across a gate parks + there, and a still is a computed frame, not a paused accumulation. That is what makes a score + scrubbable, a presentation reversible, and the whole thing renderable from an external clock. + +## A tour of the vocabulary + +One scene, ten statements. The fixture is the same shape throughout: participants declared with +`{{motion id= role=}}`, and a timeline beside them. + +**1 · The changeset, sequenced.** Steps select what _happened_: `c.inserted` / `c.removed` / +`c.kept` / `c.moved` / `c.still` / `c.all` (optionally by role), `c.role`, `c.id`, +`c.counterpart` / `c.received` for the two halves of a pair, and `c.onstage(query)` to touch only +what a viewport can see. + +```gts + + + + + +``` + +A sequence can follow a spring. Its length is computed with the engine's own generator, not guessed. + +**2 · z-index as a window.** + +```gts + + + + +``` + +The hold spans the block and then releases. `@fill={{true}}` keeps it, and `@duration` gives it its +own window. It works for any property, not just z. + +**3 · The crossing: Keynote's Magic Move as one step.** For a view or route swap where the same +identities exist on both sides: + +```gts + + {{#if (crossingActive)}} + + {{/if}} + {{outlet}} + +``` + +Leaves dissolve as the paired elements lift off, arrivals land near the settle, and only what a +viewport can see animates. The crossfade **carries color**: alpha becomes an actual solid +mid-flight, so the ground never leaks through the flying box, and is handed back to the stylesheet +on landing. Shape matching follows the **matching-snapshot rule**. Mark the real subject with +`data-choreo-substance`, or put `pack="content"` on type, so the shrink-wrap (the ink) is the subject +even when CSS stretched the frame. The entering subject is scaled uniformly _by width_ to fill the +exiting subject's width, tops pinned, and any vertical overflow is cropped at the bottom by a clip +window that travels with the box, so nothing ever stretches. `c.Crossing @size` defaults to +`'crop'`; `'scale'` and `true` are there when you mean them. A sibling step that names a role the +crossing would have touched takes that sprite away from it (the yield rule): a canned move with +per-element overrides, and no exclusion syntax. + +**4 · Gates, forward and back.** A presentation is gates all the way down: + +```gts + + + {{! opens itself: a preamble beat }} + + {{! waits for the click }} + +``` + +`c.run.advance()` opens the gate, or completes the segment and parks (the click-through). +`c.run.retreat()` steps one build back and _holds_: gates ahead close again, `@delay` gates don't open +by themselves, and the next advance replays the build. Any tween can deliver text by word or by +character (`@by`, `@order`, `@stagger`). + +**5 · A value derived, not tweened.** The badge rides the card: + +```ts +const pin = ({ rest, sources }: DeriveContext) => { + const card = sources[0]!.now; + return { x: card.x + card.width - (rest.x + rest.width) }; +}; +``` + +```gts + +``` + +`@read` runs every frame and every scrubbed still, sees only the pass's measurements, and is right +on frames nobody planned, including the frame after a mid-flight retarget. It may write transform, +opacity and filter, but never layout. + +**6 · Wires.** Geometry drawn continuously between two sprites: + +```gts + +``` + +`@path` receives both boxes in region space each frame and returns SVG path data, and the region +hosts the layer. No `@duration` means a standing wire, drawn for as long as the score stands and +redrawn through anything that moves either end. + +**7 · A place, not an identity.** + +```gts +… + +``` + +`@from` works the same way: out of the compose button, or from where a drag let go (`c.gesture` +borrows the live pointer). The bin never morphs, and the pass measures it after layout has settled, +so a destination that a re-solve just moved is simply _read_. + +**8 · Elevation and scroll as steps.** + +```gts + + + + +``` + +The scroll animates the sprite's own scroll container and yields to the user's wheel. The flash's +_timing_ lives in the timeline, while its _fade_ stays in the stylesheet's transition. `c.Raise` +promotes a sprite to the region's elevated layer for its window, above every stacking context and +clip, and `c.Camera` drives the region's own frame (zoom to a sprite, hold others steady) on the +same clock as everything else. + +**9 · Anchors and composite steps.** Name a block, aim at it, and say your app's own words once: + +```gts +class Carry extends StepComponent { + node(): TimelineNode { + const { of, name, spring = carry } = this.args; + return { + at: this.args.at, + children: [ + { generic: true, kind: 'move', of, spring }, + { generic: true, kind: 'hold', of, props: { zIndex: 3 } }, + ], + kind: 'parallel', + name, + }; + } +} +``` + +```gts + + + + +``` + +`at('carry')` / `after('carry')` place steps against the named block, and the composite's `generic` +children yield their sprites to anything more specific. `c.Crossing` itself is written against +this same public seam, so the canned step has no privileges. + +**10 · Regions and far matching.** Two ``s are two scenes. Give an element the same bare id +in both, and a pass that removes it from one while inserting it in the other pairs them at a render +barrier (measure, match, run, with no frame painted between), and the receiver flies from the +sender's box in page space. Scoping the id is how you turn it off +([nested-choreo.md](https://choreo.boxel.site/docs/nested-choreo.md)). + +Arming, _when_ a timeline should exist, is a helper of its own, because the same ids exist on +ordinary passes too: + +```ts +import { createArming } from '@cardstack/choreo'; + +const crossing = createArming(); +crossing.begin(region); // watches the run, survives mid-flight replacement, stands down on settle +crossing.active(); // tracked: render the timeline only while true +``` + +## Where it earns its keep + +Each row is a shipped pattern, from the gallery or from the product-workspace port that drove the +vocabulary: + +| The scene | The score | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| Card ⇄ page navigation (gallery ⇄ demo, tile ⇄ document) | `` + `c.Crossing`: real elements, no snapshots, live content never freezes | +| Master–detail: a row grows into a panel | `c.Move @from={{c.beacon row}}` `@size='scale'`: the real panel flies, and the solver's re-layout is simply measured | +| Drag-to-shelve: release point → slot | counterpart pairing: the painted box at release _is_ the start | +| A card transmutes into its schema node | a beacon flight out of a closing sheet, still measurable mid-exit | +| Slide decks with builds | `c.Gate` / `@delay` / `advance()` / `retreat()`: the Keynote rules | +| Annotations wired to their anchors | standing `c.Tether`s, redrawn through every reflow and every flight | +| "Jump to it and flash it" | `c.Scroll` + `c.Hold`: one clock, instead of a scroll racing a classList write racing a timer | +| A badge riding a flying card; a shadow reading lift | `c.Follow`: right on the frames a tween would have had to predict | +| Timed highlight and z states (`justDropped`, pulse flags) | `c.Hold` windows: the timer-and-flag genre, retired | +| Scrubbing, replays, video export | `run.time` + gates + `@cardstack/choreo-player` | +| A live UI inside a device, on camera | `c.Camera3D`: the DOM is a plane in the scene, and the shot seeks like any other score | +| A leaderboard that reorders when a result lands | `c.inserted` / `c.moved` / `c.removed` over ONE tracked write: no row is told where to go | +| A simulation running beside a score | the loop owns the physics and the chase camera, and the region owns the scene change. Knowing which is which is the skill | + +## Interruption, tested + +The doctrine is that a second click mid-flight is _normal input_. These mechanics make it true: + +- `initial` is the painted box on every re-pass. +- Velocities hand over to the replacement run. +- Orphans aloft keep their run through unrelated re-renders. +- Unnamed flying sprites get continuations. +- A fast keep declines pure-noise passes outright. It fingerprints the _composed_ offset chain, so a + moved wrapper that is itself no participant still reads as the reflow it is. + +The tests work the same way. They watch the primary element **per frame, in absolute coordinates**, +and normalize each step by the sample gap: a starved rAF observer merges engine frames, while a real +teleport happens inside one. They also bound the motion against the measured travel, because a +co-moving pin can stay perfect through a jump a person can't miss. An interruption soak hammers the +gallery's real demos with clicks at 0, 60 and 220 ms. + +```ts +import { + animationsSettled, + bounds, + velocityOf, +} from 'glimmer-motion/test-support'; +import { + advanceGate, + live, + orphanCount, + seekTo, + setupChoreo, + strandedTransforms, +} from '@cardstack/choreo/test-support'; +``` + +| export | what it is for | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `setupChoreo(hooks)` | glimmer-motion's `setupMotion(hooks)`, plus the state a region leaves behind: beacons, the far-match barrier, gesture samples | +| `orphanCount()` | leavers currently parked in an orphan layer | +| `strandedTransforms()` | elements wearing a transform nobody is animating | +| `live(sel)` / `liveAll(sel)` | `querySelector` that skips mid-flight ghosts in the orphan layer: a person never clicks one, so a selector mustn't either | +| `advanceGate()` / `seekTo(s)` | drive a parked run; scrub to a second | + +`animationsSettled()` from glimmer-motion waits for `` runs as well as the engine's own +animations, because a region registers its in-flight runs as a busy probe. It is explicit rather +than a blocking test waiter on purpose. The interruption tests' whole method is to click again while +something is still in flight, and a waiter would silently turn each of those clicks into a wait for +completion. + +## External clocks + +[`@cardstack/choreo-player`](https://github.com/cardstack/boxel/tree/main/packages/choreo-player#readme) +is a dependency-free, headless transport for clocking public Choreo runs from outside: a +frame-by-frame video renderer, parallel capture workers, a custom scrub surface. Its run contract is +structural and five members small, so it has no runtime dependency on this package. + +```ts +import { createChoreoPlayer } from '@cardstack/choreo-player'; + +const player = createChoreoPlayer({ + duration: 12, + prepare: () => mountScore(), // the first external clock arms the demo + runs: () => (scoreRun ? [scoreRun] : []), // read per operation, never leaked early +}); + +await player.renderAt(4.25); // pause, set speed and absolute time, settle +await player.play(); +``` + +A Choreo still is a computed frame, so `renderAt(t)` is deterministic across workers on separate +pages: no wall clock, no accumulation, no frame counting. +[demo-recording.md](https://choreo.boxel.site/docs/demo-recording.md) is the field guide for keeping a +demo correct both interactively and under capture. + +## DOM in 3D: `c.Camera3D` + +A `` region can be a plane in a three.js scene: live DOM, still clickable and still +animating, with WebGL glass composited over it. The gallery's Mockup (a phone) and Long Take (a +laptop whose screen runs a second camera of its own) are built on it. + +```gts + + + + + + +``` + +`c.Camera3D` carries an **orbit pose in the terms a shoot uses**. `dolly` is distance as a multiple +of the host's own framing, `pitch` and `yaw` are degrees, and `x` / `y` are truck and pedestal in +fractions of the framed height, because dolly alone pushes a tall subject's head out of the picture. +`@by` adds to the pose in force instead of replacing it. + +**Choreo doesn't own a renderer.** The step is a pure function of the run's clock and hands the host +a five-number pose through `@onCamera3D`. The host applies the pose to three.js, to a CSS 3D stage, +or to anything else that can take one. So a 3D shot seeks like every other score (`run.time`, +gates, choreo-player), with no rAF and no accumulation of its own. +[dom-in-3d.md](https://choreo.boxel.site/docs/dom-in-3d.md) covers what the shipped cases cost and +what stays hard, and [planes-and-cameras.md](https://choreo.boxel.site/docs/planes-and-cameras.md) +the coordinate contract. + +## The film: `` + +Two films were cut by hand on the same engine before the construct was written, **Towers** and +**Sagrada Família**, and 69% of the second was the first. `` is that 69%, lifted: a headless +cutting room in which a 3D page takes the place of the video track, and everything else is a +component reading one clock. That covers the chased lens; the joins (wipe, dip, blend, iris, melt, +blur, luma, flash, defocus); the lower third and the plate; the timeline; the voice with its +measured reads; the transport; the front door; and the end card. + +```gts +import { Film, IframePicture } from '@cardstack/choreo/film'; + + + <:picture as |register|> + + + <:default as |f|> + + + + + + + + … + + + + <:gate as |f|>…the front matter, f.begin… + <:end as |f|>…the back matter, f.restart… + +``` + +A film is a GRAPH: a spine of chapters and shots with everything else attached to a shot, compiled +to the shot list the engine runs. The picture is a port (`Picture`), so a film can hand in something +other than an iframe. + +[film.md](https://choreo.boxel.site/docs/film.md) is the **as-built reference**: every argument and +block, the beat table, the clock and its one-tick cue offset, the two seek modes, the `Picture` port. +Its most important section for the rest of the library covers how `` composes with the other +constructs rather than replacing them. It is an ordinary consumer of ``: one +`c.Camera3D @through` for the entire picture, one `c.Perform` per beat anchored with `at('film')`, +the lower third on its own region, and in exact mode, two Choreo runs that the film drives by writing +`run.time` instead of playing them. + +## API + +### ``: the region + +| argument | what | +| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `@id` | the region's name, for nesting and far matching | +| `@route` | a route swap inside the region is one pass | +| `@scroll` | `'top'` or a thunk, applied inside the pass before the final bounds are measured | +| `@quiet` | pause the rest of the page for the run's span | +| `@onCamera3D` | receives the `c.Camera3D` pose each frame; `@camera3dFrom` is the pose the first run starts from | +| `@onPerform` | dispatches each `c.Perform` command as the clock comes to include it; `@onPerformReset` resets commanded state when the clock moves back before one | +| `@debug` | outlines, and a `console.table` per run | + +It yields the context: + +- **Steps:** `Sequence`, `Parallel`, `Gate`, `Wait`, `Tween`, `Spring`, `Move`, `Hold`, `Raise`, + `Scroll`, `Follow`, `Tether`, `Crossing`, `Attach`, `Perform`, and the camera's `Camera`, + `Camera3D`, `Aim`, `Frame`, `Pan` and `SlowZoom`. +- **Queries:** `inserted`, `removed`, `kept`, `moved`, `still`, `all`, `counterpart`, `received`, + `role`, `id` and `onstage`, plus `c.beacon` and `c.gesture` for places. +- **The run:** `c.run` (null between passes), with `advance()`, `retreat()`, `play()`, `pause()`, + `cancel()`, `time`, `speed`, `duration`, `parked`, `paused`, `standing`, `segment` and `finished`. + `c.advance` opens the parked gate from a template, and `c.camera` is where the region's frame stands. + +Timing is `@duration` / `@delay` in seconds, `@spring` for spring specs, and `@stagger` across a +query's sprites. `@name`, `@at={{at 'name'}}` and `after('name')` work on steps _and_ blocks. Any +property may be a function `(sprite, changeset) => value`. Sprites carry `initial` / `final` bounds in +`context`, `parent` and `page` space, a `delta`, and their `counterpart`. `c.Tween` also takes +normalized `@times` for keyframe arrays, so a loop can hold, scatter and regroup on an explicit rhythm +without remounting its elements. + +The open vocabulary is `StepComponent`, `toMs` and node literals +([step-vocabulary.md](https://choreo.boxel.site/docs/step-vocabulary.md)), and the +[construct reference](https://choreo.boxel.site/docs/choreo-constructs.md) has every step's +arguments. + +### `{{beacon}}` + +`{{beacon 'name'}}` publishes an element's box as a place that steps can fly from or to, through +`c.beacon 'name'`, without the element joining any changeset. + +### Participants + +A participant is a glimmer-motion element: `{{motion id= role=}}`. `id` is identity across renders +and the key for pairing; `role` is a group a step can select. Choreo adds one argument to +`{{motion}}`: `pack='content'` makes the shrink-wrapped ink, not the stretched box, the shape a +crossing matches. + ## How it plugs into glimmer-motion -A region is a glimmer-motion participant host: `{{motion}}` elements with an `id` or `role` join the -nearest region when they mount, and a region can claim a leaving element to keep it on screen for as long -as the timeline names it. The region's in-flight runs count toward glimmer-motion's settle checks through a -busy probe, and `setupChoreo(hooks)` is glimmer-motion's `setupMotion(hooks)` with Choreo's document-wide -state (beacons, the far-match barrier, gesture samples) added to the resets it runs. +A region is a glimmer-motion +[participant host](https://github.com/cardstack/boxel/tree/main/packages/glimmer-motion#participant-hosts). +`{{motion}}` elements with an `id` or `role` join the nearest region when they mount, and a region +can claim a leaving element to keep it on screen for as long as the timeline names it. The region's +in-flight runs count toward glimmer-motion's settle checks through a busy probe, and +`setupChoreo(hooks)` is glimmer-motion's `setupMotion(hooks)` with Choreo's document-wide state +(beacons, the far-match barrier, gesture samples) added to the resets it runs. + +## Architecture + +``` +glimmer-motion {{motion}}, MotionNode, the layout pipeline, the participant-host extension point + ▲ +choreo.gts : the region, a participant host; snapshots before the pass, measures after +changeset.ts inserted / removed / kept / moved, with bounds in context, parent and page space +measure.ts resting measurement: release, measure, reassert +compile.ts the timeline tree → cue rows on one clock +run.ts the run: time, speed, gates, retreat, continuity with the run it replaces +steps.gts the step components, and StepComponent: the seam composite steps are written on +anchors.ts at() / after(): steps against named blocks +deliver.ts text by word or character, ordered and staggered +beacon.ts, beacons.ts {{beacon}}: places, not identities +far.ts the render barrier that pairs one bare id across regions +arming.ts createArming: when a timeline should exist +space.ts, path.ts plane space and camera paths; splices +film/ : the graph, the schedule, joins, overlays, the picture port and the player +test-support/ setupChoreo, advanceGate, seekTo, live, orphanCount, strandedTransforms +``` ## License -MIT. +MIT. © 2026 Cardstack Foundation. + +## Credits + +The engine and its algorithms are [Motion](https://motion.dev)'s +([motiondivision/motion](https://github.com/motiondivision/motion), MIT). The choreography model's +ambitions, and the render-detector idea underneath it, are from +[boxel-motion](https://github.com/cardstack/boxel). diff --git a/packages/choreo/src/anchors.ts b/packages/choreo/src/anchors.ts index a64b0fb31e1..93824a0f21d 100644 --- a/packages/choreo/src/anchors.ts +++ b/packages/choreo/src/anchors.ts @@ -1,6 +1,6 @@ /** * Anchors — "with / after build N", as typed helpers rather than a string - * micro-DSL (docs/choreo-constructs.md §4.2). A step names itself with + * micro-DSL (packages/choreo-gallery/realm/docs/choreo-constructs.md §4.2). A step names itself with * `@name`; any later step may start against it with `@at`: * * diff --git a/packages/choreo/src/beacons.ts b/packages/choreo/src/beacons.ts index 560fbdcaa5f..f621010b6e9 100644 --- a/packages/choreo/src/beacons.ts +++ b/packages/choreo/src/beacons.ts @@ -24,7 +24,7 @@ * inbox — the trash lives in the chrome, the list lives in the outlet, and they * are different regions precisely so their changesets stay apart. A region-local * registry could not see across that boundary, which is the whole point. - * docs/nested-choreo.md has the reasoning. + * packages/choreo-gallery/realm/docs/nested-choreo.md has the reasoning. */ import { measureBounds } from './measure.ts'; import type { Bounds } from './types.ts'; diff --git a/packages/choreo/src/choreo.gts b/packages/choreo/src/choreo.gts index e573bfa2597..bcd0510d308 100644 --- a/packages/choreo/src/choreo.gts +++ b/packages/choreo/src/choreo.gts @@ -12,7 +12,7 @@ *
*
* - * docs/choreography.md has the whole design and the legacy it descends from. + * packages/choreo-gallery/realm/docs/choreography.md has the whole design and the legacy it descends from. */ import { registerDestructor } from '@ember/destroyable'; import type Owner from '@ember/owner'; diff --git a/packages/choreo/src/film/clips.ts b/packages/choreo/src/film/clips.ts index 27c6f37d0a9..a91d42ad9e9 100644 --- a/packages/choreo/src/film/clips.ts +++ b/packages/choreo/src/film/clips.ts @@ -3,7 +3,7 @@ * * A beat is a shot of the picture. A clip is something else laid over it * for a while: a video, a still, or a freeze of the picture itself. Its - * arithmetic is the compositor's (notes/choreo-composition.md, Phase C3), + * arithmetic is the compositor's (packages/choreo-gallery/realm/design/choreo-composition.md, Phase C3), * lifted whole because it was already right: * * source = in + (film − start) × rate diff --git a/packages/choreo/src/film/film.gts b/packages/choreo/src/film/film.gts index 18c64ad9baf..efbff7b4b85 100644 --- a/packages/choreo/src/film/film.gts +++ b/packages/choreo/src/film/film.gts @@ -69,7 +69,7 @@ import type { * is why a beat can be widened and the type, the voice, the transport * and the readout all re-time together. The picture is whatever the film * hands in through the `Picture` port; two reference films hand in a - * WebGL page in an iframe (notes/film-construct.md). + * WebGL page in an iframe (packages/choreo-gallery/realm/design/film-construct.md). * * THE CAMERA. The score authors the shot and the lens CHASES it: one * critically-damped stage here, the page's own chase as the second, so @@ -1563,7 +1563,7 @@ export class Film extends Component { /** * THE PLAYHEAD SNAPS TO A SHOT, because this film cannot seek: the * lens is an integrator and a run dropped into its own middle arrives - * with the wrong velocity (docs/choreo-splices.md). So the drag reads + * with the wrong velocity (packages/choreo-gallery/realm/docs/choreo-splices.md). So the drag reads * as time, the label names the shot under the hand, and the release * RE-CUTS from that shot's head — which is the same edit the chapter * buttons and the arrow keys make. @@ -3983,7 +3983,7 @@ export class Film extends Component { } film.freeze?.(false); this.freeze = held; - /* A WIPE IS A CUT with punctuation over it (docs/choreo-splices.md): + /* A WIPE IS A CUT with punctuation over it (packages/choreo-gallery/realm/docs/choreo-splices.md): the still of the outgoing shot sweeps off to reveal the incoming one, so the lens must already BE on the incoming shot. Without the cut, the sweep revealed the old shot still gliding toward the @@ -4354,7 +4354,7 @@ export class Film extends Component { * existing run would be the obvious alternative and it would be wrong: * the lens is an integrator whose pose depends on where it has been, so * a run dropped into the middle of itself arrives with the wrong - * velocity — the same reason `notes/sylva-one-world.md` calls the chaser + * velocity — the same reason `packages/choreo-gallery/realm/design/sylva-one-world.md` calls the chaser * seek-unsafe and means it. */ private goChapter = (delta: number) => { @@ -4496,7 +4496,7 @@ export class Film extends Component { * starts clean on a fresh one — the game-dialogue barge-in, which is * what a chapter skip actually is. ("Stop at the next word" is the * refinement this is built to take: an analyser watching for the - * inter-word trough before the fade — see docs/choreo-splices.md.) + * inter-word trough before the fade — see packages/choreo-gallery/realm/docs/choreo-splices.md.) */ /** diff --git a/packages/choreo/src/film/graph/nodes.gts b/packages/choreo/src/film/graph/nodes.gts index ca8bb95d8f0..2e35d529201 100644 --- a/packages/choreo/src/film/graph/nodes.gts +++ b/packages/choreo/src/film/graph/nodes.gts @@ -8,7 +8,7 @@ * tree. `node()` is pure and cheap: it reads args and returns plain data; * the host compiles the tree to the table (`compile.ts`). * - * The vocabulary is what the six sketches in docs/film-graph settled on: + * The vocabulary is what the six sketches in packages/choreo-gallery/realm/design/film-graph settled on: * `Spine` / `Chapter` / `Sequence` (groups, with defaults), `Shot` (the * head pose as its own args), `To`, `Eye`, `Join` (a sibling before the * shot it cuts into), `Type`, `Voice`, `Stamp`, `Sky`, `Mark`, `Trace`, diff --git a/packages/choreo/src/film/index.ts b/packages/choreo/src/film/index.ts index 340a61ec628..996beadb9aa 100644 --- a/packages/choreo/src/film/index.ts +++ b/packages/choreo/src/film/index.ts @@ -1,7 +1,7 @@ /** * `@cardstack/choreo/film` — the film construct: a headless cutting room in * which a 3D scene takes the place of the video track. See - * notes/film-construct.md for the two reference films it was lifted from. + * packages/choreo-gallery/realm/design/film-construct.md for the two reference films it was lifted from. */ export { Clip } from './clip.gts'; export { diff --git a/packages/choreo/src/film/types.ts b/packages/choreo/src/film/types.ts index b427d15e71f..648216a88ce 100644 --- a/packages/choreo/src/film/types.ts +++ b/packages/choreo/src/film/types.ts @@ -4,7 +4,7 @@ * * Two hand-built films (Towers, Sagrada) share these one for one; the * types are lifted from them rather than designed, which is the only - * honest way a construct gets its shape. See notes/film-construct.md. + * honest way a construct gets its shape. See packages/choreo-gallery/realm/design/film-construct.md. */ import type { FunctionBasedModifier } from 'ember-modifier'; diff --git a/packages/choreo/src/path.ts b/packages/choreo/src/path.ts index 3e83fda73d7..2d39856dcb2 100644 --- a/packages/choreo/src/path.ts +++ b/packages/choreo/src/path.ts @@ -126,7 +126,7 @@ function shotAt( /** * Sample the spline at overall progress p ∈ [0, 1] (uniform segments). * - * SPLICES (docs/choreo-splices.md): a waypoint marked `cut` starts a new + * SPLICES (packages/choreo-gallery/realm/docs/choreo-splices.md): a waypoint marked `cut` starts a new * SHOT. Each shot is a clamped spline of its own points — endpoint * tangents one-sided, so no shot's velocity crosses a seam — and each * shot's window runs from its first slot to the next seam, its points diff --git a/packages/choreo/src/run.ts b/packages/choreo/src/run.ts index 3f45e9ce083..5b8ff402d24 100644 --- a/packages/choreo/src/run.ts +++ b/packages/choreo/src/run.ts @@ -1586,7 +1586,7 @@ export class ChoreoRun implements Run { /** * Rebuild the camera fold from the score prefix before a seek's still - * (notes/external-clock-camera-seek-handoff.md). Forward playback folds + * (packages/choreo-gallery/realm/design/external-clock-camera-seek-handoff.md). Forward playback folds * each camera cue's landing into the run's cumulative camera as the * clock crosses it — but a random-access seek can jump clean over a * window, and a cue that never starts never folds: the clock reads `t` @@ -1809,7 +1809,7 @@ export class ChoreoRun implements Run { * comes back. * * Nothing here touches the DOM — that is the design, not an - * optimisation (docs/postmortem-follow.md). The resting boxes were + * optimisation (packages/choreo-gallery/realm/docs/postmortem-follow.md). The resting boxes were * measured by the pass with every moved value released, and a source's * `now` is its resting box composed with the motion values driving it * this frame: JS-side numbers, not a measurement. So a follower is pure diff --git a/packages/choreo/src/space.ts b/packages/choreo/src/space.ts index 55bf1c0918d..36a00c2a399 100644 --- a/packages/choreo/src/space.ts +++ b/packages/choreo/src/space.ts @@ -1,5 +1,5 @@ /** - * The coordinate contract (docs/planes-and-cameras.md). + * The coordinate contract (packages/choreo-gallery/realm/docs/planes-and-cameras.md). * * A plane's camera is pure arithmetic over frozen numbers: with * transform-origin pinned at 0 0, the applied transform is diff --git a/packages/choreo/src/steps.gts b/packages/choreo/src/steps.gts index 03a95672fcf..d49ccdf3019 100644 --- a/packages/choreo/src/steps.gts +++ b/packages/choreo/src/steps.gts @@ -593,7 +593,7 @@ export class Camera3D extends StepComponent< } } -/* ---- the direction vocabulary (notes/choreo-composition.md C5) ---- +/* ---- the direction vocabulary (packages/choreo-gallery/realm/design/choreo-composition.md C5) ---- Presets expand into the SAME seekable camera cue — sugar, never a new runtime primitive. Frame and Aim are absolute (computed from measured geometry at compile); Pan and SlowZoom are RELATIVE, resolved against @@ -880,7 +880,7 @@ export class Crossing extends StepComponent< * by the pass or composed from the run's own values, so it cannot read * back what it wrote last frame and it cannot force a style recalculation * mid-move — pure by construction, and correct under interruption because - * a replacement pass re-measures (docs/postmortem-follow.md). + * a replacement pass re-measures (packages/choreo-gallery/realm/docs/postmortem-follow.md). * * Three things it is not: it is not accelerated (a derived value is * computed on the main thread, every frame — the price, and the same one diff --git a/packages/choreo/src/types.ts b/packages/choreo/src/types.ts index 8424c765298..14ed7978640 100644 --- a/packages/choreo/src/types.ts +++ b/packages/choreo/src/types.ts @@ -1,6 +1,6 @@ /** * The vocabulary of a choreography — boxel-motion's Sprite / Changeset / - * AnimationDefinition, as this binding keeps them. See docs/choreography.md. + * AnimationDefinition, as this binding keeps them. See packages/choreo-gallery/realm/docs/choreography.md. */ import type { MotionParticipant } from 'glimmer-motion'; @@ -419,7 +419,7 @@ export interface Camera3DWaypoint { * and nothing interpolates across. Time is not redistributed, so cues * anchored to waypoint moments keep their clock. A cut on the FIRST * waypoint drops the pose-in-force seed: the score opens already inside - * its first shot. See docs/choreo-splices.md. + * its first shot. See packages/choreo-gallery/realm/docs/choreo-splices.md. */ cut?: boolean; dolly?: number; @@ -490,7 +490,7 @@ export interface TetherStep extends SubjectlessBase { * One source of a derived value: the boxes the PASS measured, in region * space, named for which box each one is — because the distinction * between resting and live geometry is the whole correctness argument - * (docs/postmortem-follow.md). `from` and `to` are resting layout, where + * (packages/choreo-gallery/realm/docs/postmortem-follow.md). `from` and `to` are resting layout, where * the stylesheet put the sprite on either side of this change; `now` is * `to` composed with the transform the run is driving this frame. None * of the three is read from the page while the run plays. @@ -552,7 +552,7 @@ export interface FollowStep extends StepBase { /** * ATTACH — drive another region's run over a window of this one's clock - * (docs/film-graph/CONSTRUCTS.md, construct 1). The child is named by its + * (packages/choreo-gallery/realm/design/film-graph/CONSTRUCTS.md, construct 1). The child is named by its * `@id`; while the window is open its run is paused and told the time, * `in + (now − start) × rate`, on every evaluate — so the child is a pure * function of the parent's clock and a seek into the window lands it diff --git a/packages/choreo/tests/integration/choreo/attach-test.gts b/packages/choreo/tests/integration/choreo/attach-test.gts index 76a2e11281f..6c34877da44 100644 --- a/packages/choreo/tests/integration/choreo/attach-test.gts +++ b/packages/choreo/tests/integration/choreo/attach-test.gts @@ -1,5 +1,5 @@ /** - * ATTACH — the driven run (docs/film-graph/CONSTRUCTS.md, construct 1). + * ATTACH — the driven run (packages/choreo-gallery/realm/design/film-graph/CONSTRUCTS.md, construct 1). * * A parent region carries `` in its score; the * child region named by `@id` has a score of its own. While the window is diff --git a/packages/choreo/tests/integration/choreo/camera-preset-test.gts b/packages/choreo/tests/integration/choreo/camera-preset-test.gts index 9a5608d6cc2..31a6d0d12a9 100644 --- a/packages/choreo/tests/integration/choreo/camera-preset-test.gts +++ b/packages/choreo/tests/integration/choreo/camera-preset-test.gts @@ -1,5 +1,5 @@ /** - * The camera presets (docs/choreo-composition.md, Phase C5): frame, aim, + * The camera presets (packages/choreo-gallery/realm/design/choreo-composition.md, Phase C5): frame, aim, * pan, slowZoom — direction vocabulary that expands into the SAME * seekable camera step, never a new runtime primitive. Frame must be the * fit camera to the pixel; Aim recentres with the zoom held; Pan and diff --git a/packages/choreo/tests/integration/choreo/camera-tether-test.gts b/packages/choreo/tests/integration/choreo/camera-tether-test.gts index 0e8519bd6f3..8967ff9c60f 100644 --- a/packages/choreo/tests/integration/choreo/camera-tether-test.gts +++ b/packages/choreo/tests/integration/choreo/camera-tether-test.gts @@ -1,5 +1,5 @@ /** - * c.Camera and c.Tether (docs/choreo-constructs.md §6.1, §6.3, §6.4). + * c.Camera and c.Tether (packages/choreo-gallery/realm/docs/choreo-constructs.md §6.1, §6.3, §6.4). */ import { Choreo, type ChoreoContext, type Rect } from '@cardstack/choreo'; import { setupChoreo } from '@cardstack/choreo/test-support'; diff --git a/packages/choreo/tests/integration/choreo/camera-transport-test.gts b/packages/choreo/tests/integration/choreo/camera-transport-test.gts index d32018ca4fa..e2b4419c1a9 100644 --- a/packages/choreo/tests/integration/choreo/camera-transport-test.gts +++ b/packages/choreo/tests/integration/choreo/camera-transport-test.gts @@ -1,5 +1,5 @@ /** - * The camera under an external transport (docs/external-clock-camera-seek-handoff.md). + * The camera under an external transport (packages/choreo-gallery/realm/design/external-clock-camera-seek-handoff.md). * * `run.time = t` must produce the same camera still as playing forward * from zero to `t`. The failure this guards against: a camera cue whose diff --git a/packages/choreo/tests/integration/choreo/choreo-test.gts b/packages/choreo/tests/integration/choreo/choreo-test.gts index 3d45629b521..ec5d7f69b8a 100644 --- a/packages/choreo/tests/integration/choreo/choreo-test.gts +++ b/packages/choreo/tests/integration/choreo/choreo-test.gts @@ -1,6 +1,6 @@ /** * : boxel-motion's region-scoped choreography on the motion-dom engine. - * docs/choreography.md is the spec these pin. + * packages/choreo-gallery/realm/docs/choreography.md is the spec these pin. */ import { beacon, type Changeset, Choreo, type Sprite } from '@cardstack/choreo'; import { setupChoreo } from '@cardstack/choreo/test-support'; @@ -105,7 +105,7 @@ module('Integration | choreo', function (hooks) { }); test('the first render never animates; an all-kept pass still runs its timeline', async function (assert) { - // docs/choreo-constructs.md §3.1: the share badge, the hot wire — a Hold + // packages/choreo-gallery/realm/docs/choreo-constructs.md §3.1: the share badge, the hot wire — a Hold // with a lifetime fired by an event that inserts, removes and moves // nothing. Steps that select CHANGE stay quiet on such a pass. let holds = 0; diff --git a/packages/choreo/tests/integration/choreo/crossing-test.gts b/packages/choreo/tests/integration/choreo/crossing-test.gts index d0b608dce08..00ebe656567 100644 --- a/packages/choreo/tests/integration/choreo/crossing-test.gts +++ b/packages/choreo/tests/integration/choreo/crossing-test.gts @@ -1,5 +1,5 @@ /** - * @route and c.Crossing (docs/choreo-constructs.md §4.7): a subtree swap as + * @route and c.Crossing (packages/choreo-gallery/realm/docs/choreo-constructs.md §4.7): a subtree swap as * one pass — leaves fade first, the paired flight carries, arrivals land * near the settle, and live content never freezes, because nothing is * snapshotted. diff --git a/packages/choreo/tests/integration/choreo/delivery-test.gts b/packages/choreo/tests/integration/choreo/delivery-test.gts index 4f8fa92f199..6ecf76ff1d9 100644 --- a/packages/choreo/tests/integration/choreo/delivery-test.gts +++ b/packages/choreo/tests/integration/choreo/delivery-test.gts @@ -1,5 +1,5 @@ /** - * Delivery — @by / @order / @stagger (docs/choreo-constructs.md §4.3). + * Delivery — @by / @order / @stagger (packages/choreo-gallery/realm/docs/choreo-constructs.md §4.3). * The split is a costume: the sprite's own text nodes come back exactly, * and the sprite lands on the delivery's end values. */ diff --git a/packages/choreo/tests/integration/choreo/gates-test.gts b/packages/choreo/tests/integration/choreo/gates-test.gts index 9adc8af2292..564d4446a7b 100644 --- a/packages/choreo/tests/integration/choreo/gates-test.gts +++ b/packages/choreo/tests/integration/choreo/gates-test.gts @@ -1,5 +1,5 @@ /** - * Gates and the run handle (docs/choreo-constructs.md §4.1, §4.6). + * Gates and the run handle (packages/choreo-gallery/realm/docs/choreo-constructs.md §4.1, §4.6). * A gate parks the run; advance() opens it; mid-segment advance is * Keynote's click-through — every property lands on its segment-end * value. A parked run is a still, and settled. diff --git a/packages/choreo/tests/integration/choreo/lane-spike-test.gts b/packages/choreo/tests/integration/choreo/lane-spike-test.gts index b80875edf19..177ca943939 100644 --- a/packages/choreo/tests/integration/choreo/lane-spike-test.gts +++ b/packages/choreo/tests/integration/choreo/lane-spike-test.gts @@ -1,7 +1,7 @@ /** * SPIKE 1 — a lane: steps written in a PARENT that play in a CHILD region. * - * The question the film graph hangs on (docs/film-graph/PLAN.md, spike 1): + * The question the film graph hangs on (packages/choreo-gallery/realm/design/film-graph/PLAN.md, spike 1): * can a parent contribute nodes to a child region's timeline so that the * child's queries resolve in the child's changeset, the child's run can * be driven from outside, and an unrelated re-render of the parent does diff --git a/packages/choreo/tests/integration/choreo/move-extras-test.gts b/packages/choreo/tests/integration/choreo/move-extras-test.gts index 9527257e83e..784d7ab348b 100644 --- a/packages/choreo/tests/integration/choreo/move-extras-test.gts +++ b/packages/choreo/tests/integration/choreo/move-extras-test.gts @@ -1,6 +1,6 @@ /** * Move's flights and skins — @path, @rotate, @swap - * (docs/choreo-constructs.md §4.4, §6.3). + * (packages/choreo-gallery/realm/docs/choreo-constructs.md §4.4, §6.3). */ import { Choreo } from '@cardstack/choreo'; import { setupChoreo } from '@cardstack/choreo/test-support'; diff --git a/packages/choreo/tests/integration/choreo/perform-test.gts b/packages/choreo/tests/integration/choreo/perform-test.gts index 19c6914fdb7..7458f099e54 100644 --- a/packages/choreo/tests/integration/choreo/perform-test.gts +++ b/packages/choreo/tests/integration/choreo/perform-test.gts @@ -1,6 +1,6 @@ /** * `` — the semantic command fold, promoted into core - * (docs/choreo-composition.md §C4; the compositor host's cue fold is the + * (packages/choreo-gallery/realm/design/choreo-composition.md §C4; the compositor host's cue fold is the * proof that opened this gate). * * The law under every case: the set of commands at or before the clock IS diff --git a/packages/choreo/tests/integration/choreo/plane-flight-test.gts b/packages/choreo/tests/integration/choreo/plane-flight-test.gts index 78a503f8264..010785c8e2a 100644 --- a/packages/choreo/tests/integration/choreo/plane-flight-test.gts +++ b/packages/choreo/tests/integration/choreo/plane-flight-test.gts @@ -1,6 +1,6 @@ /** * A sprite between planes when the planes disagree about zoom - * (docs/planes-and-cameras.md, build-order step 2). + * (packages/choreo-gallery/realm/docs/planes-and-cameras.md, build-order step 2). * * Far matching hands the receiver the sender's PAGE box — the only space * two regions share — but the receiver's flight is written in ITS plane's diff --git a/packages/choreo/tests/integration/choreo/raise-scroll-test.gts b/packages/choreo/tests/integration/choreo/raise-scroll-test.gts index 8657c19d257..e25da36a6bd 100644 --- a/packages/choreo/tests/integration/choreo/raise-scroll-test.gts +++ b/packages/choreo/tests/integration/choreo/raise-scroll-test.gts @@ -1,5 +1,5 @@ /** - * c.Raise and c.Scroll (docs/choreo-constructs.md §6.1, §6.3). + * c.Raise and c.Scroll (packages/choreo-gallery/realm/docs/choreo-constructs.md §6.1, §6.3). * A raise escapes stacking contexts and clips on a real layer, with the * slot held by a placeholder; a scroll occupies the sequence and yields * to the user's own wheel. diff --git a/packages/choreo/tests/integration/choreo/space-test.ts b/packages/choreo/tests/integration/choreo/space-test.ts index a2920e4d5b8..b1d720de9a2 100644 --- a/packages/choreo/tests/integration/choreo/space-test.ts +++ b/packages/choreo/tests/integration/choreo/space-test.ts @@ -1,5 +1,5 @@ /** - * The coordinate contract (docs/planes-and-cameras.md): toPage/toLocal + * The coordinate contract (packages/choreo-gallery/realm/docs/planes-and-cameras.md): toPage/toLocal * over CameraState are pure, exact inverses, and need no DOM — which is * the entire point. Every cross-plane flight, hit test, and drag reduces * to these two functions, so they are proven here as arithmetic, not as diff --git a/packages/choreo/tests/integration/choreo/world-dock-test.gts b/packages/choreo/tests/integration/choreo/world-dock-test.gts index e1547bff5ea..eef1cb4bdf1 100644 --- a/packages/choreo/tests/integration/choreo/world-dock-test.gts +++ b/packages/choreo/tests/integration/choreo/world-dock-test.gts @@ -1,6 +1,6 @@ /** * The world dock: a far-match flight received into a camera'd world whose - * score is mid-flight (docs/planes-and-cameras.md, the open case). + * score is mid-flight (packages/choreo-gallery/realm/docs/planes-and-cameras.md, the open case). * * The receiving plane is not a held pose here — it is a WORLD: a region * with its own running score whose camera is in force when the dock diff --git a/packages/choreo/tests/integration/film/join-presentation-test.gts b/packages/choreo/tests/integration/film/join-presentation-test.gts index f2881885d0c..8d4d1f746ee 100644 --- a/packages/choreo/tests/integration/film/join-presentation-test.gts +++ b/packages/choreo/tests/integration/film/join-presentation-test.gts @@ -5,7 +5,7 @@ * privilege: a score names it with ``, * the compiler names it for the film and registers it, and `` * plays it like any of the twelve. This is the acceptance test the - * constructs doc asked for (docs/film-graph/CONSTRUCTS.md, "the join is + * constructs doc asked for (packages/choreo-gallery/realm/design/film-graph/CONSTRUCTS.md, "the join is * not an engine construct"). */ import { diff --git a/packages/glimmer-motion/README.md b/packages/glimmer-motion/README.md index 757f0520194..1c6462ea7e7 100644 --- a/packages/glimmer-motion/README.md +++ b/packages/glimmer-motion/README.md @@ -1,10 +1,11 @@ -# glimmer-motion +# glimmer-motion The [Motion](https://motion.dev) engine for Ember. `{{motion}}`, ``, ``, Reorder and drag, verified by ports of Motion's own test suites. ``, a changeset and a timeline over a whole render pass, is a separate package built on this -one: [`@cardstack/choreo`](../choreo). +one: [`@cardstack/choreo`](https://github.com/cardstack/boxel/tree/main/packages/choreo#readme). +Both are demonstrated, with their source, in the [Choreo gallery](https://choreo.boxel.site/). ```gts import { motion, Presence, LayoutGroup, to, spring } from 'glimmer-motion'; @@ -42,8 +43,8 @@ Peers: `motion-dom` / `motion-utils` (pinned together), `ember-modifier`, `@glim at a version outside glimmer-motion's ranges puts a second copy of the engine on the page. A v2 addon — Embroider and Vite apps consume it directly, with TypeScript types and Glint signatures. -New here? The [guide](https://github.com/cardstack/choreo/blob/main/docs/guide.md) teaches this -from a Glimmer card rather than from a React translation table. +New here? The [guide](https://choreo.boxel.site/docs/guide.md) teaches this from a Glimmer card +rather than from a React translation table. ## Features @@ -60,10 +61,10 @@ from a Glimmer card rather than from a React translation table. `` tree defaults, `scrollProgress()` / `InView` over Motion's `scroll()` / `inView()` - **Reorder** — `` / ``, axis `x` / `y` / `xy` (detected), auto-scroll - **Glimmer** — `.gts`, Glint signatures, named exports, plain-function template helpers - (`to` / `spring` / `tween` / `styles` / `start`), and `prefers-reduced-motion` honoured by default -- **Testing** — `glimmer-motion/test-support`: `animationsSettled()`, `bounds()`, `shape()`, - `setupMotion(hooks)`, and `registerMotionReset()` for a layer that keeps document-wide state of - its own. No `sleep()` in a motion test + (`to` / `styles` / `spring` / `tween` / `inertia` / `stagger` / `ease` / `perValue`), and + `prefers-reduced-motion` honoured by default +- **Testing** — `glimmer-motion/test-support` ([below](#testing)): settle on real activity instead + of `sleep()`, and measure in test-root coordinates - **Re-hostable** — host hooks are ~40 lines plus the components; everything above that line is framework-free @@ -204,6 +205,75 @@ one. | `useDragControls()` | `createDragControls()` | | `Reorder.Group` / `.Item` | `` / `` | +### Three rules React does not need + +React's model hides three things that Glimmer's doesn't, and each surfaced while porting a real app +onto this binding. They are the only places where the port isn't a mechanical translation. + +**1. Motion owns a motion element's inline style, so pass CSS through the modifier, not a `style` +attribute.** In React the `style` prop belongs to Motion: it merges what you write with the +transforms it renders. In Glimmer a bound `style="…"` attribute is yours, and Glimmer rewrites the +whole declaration whenever the bound value changes, wiping out the transform Motion just wrote. The +symptom is quiet: a card that stays put while the drag logic runs perfectly around it. + +```gts +{{! ✗ the next re-render erases the drag transform }} +
+ +{{! ✓ Motion applies these itself, custom properties included }} +
+``` + +**2. A leaving child stays live, so read exiting content from the yielded item.** React keeps the +_element tree_ it captured before the diff, so a leaving child can't re-render. A Glimmer block +re-runs from live tracked state for as long as the leaver is on screen. Anything the exit needs (the +symbol the panel was showing, the rect the modal flew from) has to ride on the item `` +yields, not be read back out of state that has already moved on. + +**3. Declare what you want tweened, because a value in the stylesheet is invisible to the engine.** +A layout animation moves and resizes by transform, and the engine corrects `borderRadius` and +`boxShadow` against the scale in force, but only for values it holds. A radius that lives in CSS +isn't one, and a square tile growing into a wide hero comes out with oval corners. Hand the value to +Motion and every frame is corrected. + +```gts +{{! ✗ the engine can't correct what it doesn't know about: .card { border-radius: 14px } }} +
+ +{{! ✓ corrected per frame, against whatever scale the layout animation is applying }} +
+``` + +## Testing + +```ts +import { animationsSettled, bounds, setupMotion } from 'glimmer-motion/test-support'; + +module('the card', function (hooks) { + setupRenderingTest(hooks); + setupMotion(hooks); + + test('it lands', async function (assert) { + await render(); + await animationsSettled(); + assert.strictEqual(bounds(find('.card')!).x, 120); + }); +}); +``` + +| export | what it is for | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `setupMotion(hooks)` | resets what outlives an owner (the projection root, the layout-loop guard, motion speed, and every registered reset) around each test, and fails a test that ran a layout loop | +| `animationsSettled(opts?)` | resolves when every motion element, layout animation and registered busy probe has stopped, and names what was still moving when it times out | +| `bounds(el)` / `shape(el)` / `boundsAndShape(el)` | rects relative to the test root; the cumulative 2×2 transform | +| `velocityOf(el, prop)` | how fast a value is moving right now | +| `isMotionIdle()` / `whatIsBusy()` | the probe underneath, for a custom wait | +| `registerMotionReset(fn)` / `resetMotion()` | add a reset of your own, for a layer that keeps document-wide state; run them all | + +`animationsSettled()` is explicit rather than a test waiter on purpose. A test that clicks again +while something is still in flight is testing interruption, and a waiter would silently turn that +click into a wait for completion. + ## How it works Motion's React library is an engine (`motion-dom`) plus glue. This package is only the glue, redone for @@ -221,9 +291,33 @@ The pan/drag session, the gesture and animation features and the Reorder utiliti modules too, inlined at build time from framer-motion's build. `VENDORED.md` lists them and the two places glimmer-motion's drag deliberately differs. -The full story — why the engine is untouched, how the binding was built by porting Motion's test suites and -what that surfaced — is in the -[repository README](https://github.com/cardstack/choreo#readme). +### How it was made + +The binding was built by porting Motion's own test suites, in order, and letting them drive it: the +animate prop, variants, AnimatePresence, LayoutGroup, keyframes and delay, the style prop and +unmount, then the `layout-*` and `drag-*` Cypress specs, then Reorder. Each fixture was rebuilt as a +Glimmer component with its literal expected pixel values kept, and nothing was declared done until +the upstream test was green in a real Chrome. Whatever failed was a place where the binding's +ordering differed from React's, and almost all of them were timing rules: + +- Glimmer installs modifiers children-first, so a child can't find its parent's visual element when + its modifier first runs. First runs queue, then construct in document order and mount post-order + after the pass. Sibling order matters, because the engine builds its stagger index from it. +- There is no `getSnapshotBeforeUpdate`. A render detector, a getter that consumes a volatile tag + and so re-evaluates on every render pass, lets `` snapshot every projection node before + the DOM changes, the way React re-rendering each motion component would. +- The engine's update microtask must not run between Glimmer's render and the mounts, or it clears + the snapshots and layout animations become identity transitions. There is one settle per pass, + after the mounts. +- An element created already-absent must still mount _present_ and then leave, or its exit is + swallowed. +- AnimatePresence renders a leaving child from its last present element, but a live Glimmer block + doesn't, so the modifier freezes an exiting element's props. +- `initial` values must be in the DOM before the projection first measures. React has them in the + style attribute synchronously, so the binding renders them synchronously at mount. + +Those ports are this package's test suite, `tests/` in this package. Where a literal changed, the +reason is inline. ## Not ported (yet) diff --git a/packages/host/tests/integration/choreo-gallery-crossing-test.gts b/packages/host/tests/integration/choreo-gallery-crossing-test.gts index 95d1c1410fe..779e12b8f25 100644 --- a/packages/host/tests/integration/choreo-gallery-crossing-test.gts +++ b/packages/host/tests/integration/choreo-gallery-crossing-test.gts @@ -1,6 +1,6 @@ /** * The gallery ⇄ demo crossing, driven through the gallery card — the hero - * use case of docs/choreo-constructs.md §4.7, asserted end to end: the + * use case of packages/choreo-gallery/realm/docs/choreo-constructs.md §4.7, asserted end to end: the * timeline arms when a tile is followed, the region's pass populates the * orphan layer with exactly the flying cast, the landing is clean, and * nothing is left stranded at a seeded opacity. Everything visual is diff --git a/packages/host/tests/integration/choreo-gallery-escort-test.gts b/packages/host/tests/integration/choreo-gallery-escort-test.gts index 3703703b5b0..f4e877e5b59 100644 --- a/packages/host/tests/integration/choreo-gallery-escort-test.gts +++ b/packages/host/tests/integration/choreo-gallery-escort-test.gts @@ -1,6 +1,6 @@ /** * Escort — the acceptance page for the opened step vocabulary - * (docs/step-vocabulary.md): a composite step the APP said for itself, + * (packages/choreo-gallery/realm/docs/step-vocabulary.md): a composite step the APP said for itself, * named and anchored against, with two values read from the flight it * names. * diff --git a/packages/host/tests/integration/choreo-gallery-presentation-test.gts b/packages/host/tests/integration/choreo-gallery-presentation-test.gts index a998277ec7f..369a4a94a5f 100644 --- a/packages/host/tests/integration/choreo-gallery-presentation-test.gts +++ b/packages/host/tests/integration/choreo-gallery-presentation-test.gts @@ -1,5 +1,5 @@ /** - * Presentation — gates as presenter mode (docs/choreo-constructs.md §8.1). + * Presentation — gates as presenter mode (packages/choreo-gallery/realm/docs/choreo-constructs.md §8.1). * A three-build slide: auto kicker, click-through mid-path, then the pulse. */ import { click, focus, triggerKeyEvent, waitUntil } from '@ember/test-helpers'; diff --git a/packages/host/tests/integration/choreo-gallery-wires-test.gts b/packages/host/tests/integration/choreo-gallery-wires-test.gts index 6a0a144f817..7eff1e349f5 100644 --- a/packages/host/tests/integration/choreo-gallery-wires-test.gts +++ b/packages/host/tests/integration/choreo-gallery-wires-test.gts @@ -1,5 +1,5 @@ /** - * Wires — c.Tether (docs/choreo-constructs.md §6.1, §8.1). + * Wires — c.Tether (packages/choreo-gallery/realm/docs/choreo-constructs.md §6.1, §8.1). * A morning note in three drafts: marks in the prose, comments in the * margin, cubics derived every frame. Versions reflow the copy; the * selected pair shows its thread (first comment on by default). From 18842f5464c8c2e66141811a5bbf2c67d1aa35f8 Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Mon, 12 Oct 2026 00:08:01 -0400 Subject: [PATCH 2/3] Check Film imports and URL fragments, and fix the README examples check-realm follows `export *`, so imports from @cardstack/choreo/film are checked; skips the design records' imports, which sketch APIs never built; and checks the heading a choreo.boxel.site or monorepo URL's fragment names. The glimmer-motion README's testing example, velocityOf row and first rule, and the choreo README's example now match the source. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018tqpxGBVBUCe7nACwttGag --- packages/choreo-gallery/README.md | 8 +- packages/choreo-gallery/tools/check-realm.mjs | 79 ++++++++++++++++--- packages/choreo/README.md | 2 +- packages/glimmer-motion/README.md | 11 +-- 4 files changed, 79 insertions(+), 21 deletions(-) diff --git a/packages/choreo-gallery/README.md b/packages/choreo-gallery/README.md index 745cab9eb6b..95d9321074b 100644 --- a/packages/choreo-gallery/README.md +++ b/packages/choreo-gallery/README.md @@ -115,11 +115,13 @@ against the libraries it documents: - every guide meets the prose floor, has a preamble, and links only to guides that exist; - `docs/` and `design/` link only to realm files and headings that exist; -- every import written in the docs, the guides and the package READMEs names +- every import written in `docs/`, the guides and the package READMEs names an export its package has, so none still reaches Choreo or Film through - `glimmer-motion`; + `glimmer-motion` (`design/` sketches APIs that were never built, so its + imports aren't checked); - every `choreo.boxel.site` URL names a file in the realm, and every link into - the monorepo names a file that exists. + the monorepo names a file that exists, along with the heading any `#fragment` + names. The guides themselves, and the guide pages that embed the demos, are still `choreo-test-app` sources; the check reads them from there. diff --git a/packages/choreo-gallery/tools/check-realm.mjs b/packages/choreo-gallery/tools/check-realm.mjs index 8030026b3ba..575e0a1f1a9 100644 --- a/packages/choreo-gallery/tools/check-realm.mjs +++ b/packages/choreo-gallery/tools/check-realm.mjs @@ -11,9 +11,12 @@ * guides state their motivation and goals, and internal guide links * resolve. * - The docs and design records in the realm link only to files and - * headings that exist; every import written in them, in the guides and in - * the package READMEs names an export its package has; and every - * choreo.boxel.site URL names a file in the realm. + * headings that exist; every import written in the docs, the guides and + * the package READMEs names an export its package has (the design + * records sketch APIs that were never built, so their imports aren't + * checked); and every choreo.boxel.site URL names a file in the realm, + * and every link into the monorepo a file that exists, down to the + * heading a `#fragment` names. * * Reads sources and JSON only, so it needs no build. */ @@ -335,7 +338,11 @@ const relative = (file) => path.relative(root, file); const withoutFences = (markdown) => markdown.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, ''); -/** the ids GitHub (and the host's renderer) give a document's headings */ +/** + * The ids GitHub gives a document's headings. The host computes the same + * slugs but renders each id with a `user-content-` prefix + * (runtime-common/marked-sync.ts), as GitHub does in its HTML. + */ function headingIds(markdown) { const ids = new Set(); const seen = new Map(); @@ -449,7 +456,30 @@ function exportsOf(file) { const names = new Set(); for (const node of tree.statements) { if (ts.isExportDeclaration(node) && !node.exportClause) { - return undefined; // `export *`: anything may come through + // `export * from './x'`: follow it. From another package, anything may + // come through. + const specifier = node.moduleSpecifier.text; + const base = path.resolve(path.dirname(file), specifier); + const target = specifier.startsWith('.') + ? [base, `${base}.ts`, `${base}.gts`].find( + (candidate) => + fs.existsSync(candidate) && fs.statSync(candidate).isFile(), + ) + : undefined; + const inner = target && exportsOf(target); + if (!inner) { + return undefined; + } + inner.forEach((name) => names.add(name)); + continue; + } + if ( + ts.isExportDeclaration(node) && + node.exportClause && + ts.isNamespaceExport(node.exportClause) + ) { + names.add(node.exportClause.name.text); + continue; } if ( ts.isExportDeclaration(node) && @@ -482,8 +512,10 @@ const importedDocs = [ ...markdownFiles(guidesDir), ...packageReadmes, ]; +const designDir = path.join(realm, 'design') + path.sep; const badImports = []; -for (const file of importedDocs) { +// the design records sketch APIs that were never built +for (const file of importedDocs.filter((doc) => !doc.startsWith(designDir))) { const markdown = fs.readFileSync(file, 'utf8'); for (const [statement, clause, specifier] of markdown.matchAll( /\bimport\s+(?:type\s+)?([\w$]+\s*,\s*\{[^}]*\}|\{[^}]*\}|[\w$]+|\*\s+as\s+[\w$]+)\s+from\s+['"]([^'"]+)['"]/g, @@ -522,12 +554,31 @@ for (const file of importedDocs) { } assert.deepEqual(badImports, [], 'Imports the packages do not provide'); +/** a `#fragment` on a URL to a Markdown file, or to a directory's README */ +function namesNoHeading(target, fragment) { + if (!fragment) { + return false; + } + const isDirectory = + fs.existsSync(target) && fs.statSync(target).isDirectory(); + // GitHub anchors a directory page's README at `#readme` + if (isDirectory && fragment === 'readme') { + return false; + } + const doc = isDirectory ? path.join(target, 'README.md') : target; + return ( + doc.endsWith('.md') && + fs.existsSync(doc) && + !headingIds(fs.readFileSync(doc, 'utf8')).has(decodeURIComponent(fragment)) + ); +} + /** the hosted gallery is this realm, so its URLs name realm files */ const siteURLs = []; for (const file of importedDocs) { const markdown = fs.readFileSync(file, 'utf8'); - for (const [url, pathname] of markdown.matchAll( - /https:\/\/choreo\.boxel\.site\/([^\s)"'<>`#?]*)/g, + for (const [url, pathname, fragment] of markdown.matchAll( + /https:\/\/choreo\.boxel\.site\/([^\s)"'<>`#?]*)(?:#([^\s)"'<>`]+))?/g, )) { const target = path.join(realm, decodeURIComponent(pathname)); const exists = @@ -536,6 +587,8 @@ for (const file of importedDocs) { fs.existsSync(`${target}.json`); if (!exists) { siteURLs.push(`${relative(file)}: ${url}`); + } else if (namesNoHeading(target, fragment)) { + siteURLs.push(`${relative(file)}: ${url} names no heading`); } } } @@ -551,14 +604,16 @@ assert.deepEqual( * Choreo's archived repository. */ const repoLinks = []; -const designDir = path.join(realm, 'design') + path.sep; for (const file of importedDocs) { const markdown = fs.readFileSync(file, 'utf8'); - for (const [url, repoPath] of markdown.matchAll( - /https:\/\/github\.com\/cardstack\/boxel\/(?:blob|tree)\/main\/([^\s)"'<>`#?]+)/g, + for (const [url, repoPath, fragment] of markdown.matchAll( + /https:\/\/github\.com\/cardstack\/boxel\/(?:blob|tree)\/main\/([^\s)"'<>`#?]+)(?:#([^\s)"'<>`]+))?/g, )) { - if (!fs.existsSync(path.join(root, repoPath))) { + const target = path.join(root, repoPath); + if (!fs.existsSync(target)) { repoLinks.push(`${relative(file)}: ${url} names no file`); + } else if (namesNoHeading(target, fragment)) { + repoLinks.push(`${relative(file)}: ${url} names no heading`); } } if (!file.startsWith(designDir)) { diff --git a/packages/choreo/README.md b/packages/choreo/README.md index 0086436aa1a..17ad0ec74d6 100644 --- a/packages/choreo/README.md +++ b/packages/choreo/README.md @@ -467,7 +467,7 @@ measured reads; the transport; the front door; and the end card. ```gts import { Film, IframePicture } from '@cardstack/choreo/film'; - + <:picture as |register|> diff --git a/packages/glimmer-motion/README.md b/packages/glimmer-motion/README.md index 1c6462ea7e7..747a0b639f1 100644 --- a/packages/glimmer-motion/README.md +++ b/packages/glimmer-motion/README.md @@ -217,11 +217,12 @@ whole declaration whenever the bound value changes, wiping out the transform Mot symptom is quiet: a card that stays put while the drag logic runs perfectly around it. ```gts -{{! ✗ the next re-render erases the drag transform }} +{{! ✗ the next re-render erases the drag transform. accentStyle = '--accent: …' }}
-{{! ✓ Motion applies these itself, custom properties included }} -
+{{!-- ✓ Motion applies these itself, custom properties included. + The modifier takes an object: accentVars = { '--accent': … } --}} +
``` **2. A leaving child stays live, so read exiting content from the yielded item.** React keeps the @@ -256,7 +257,7 @@ module('the card', function (hooks) { test('it lands', async function (assert) { await render(); await animationsSettled(); - assert.strictEqual(bounds(find('.card')!).x, 120); + assert.strictEqual(bounds(find('.card')!).left, 120); }); }); ``` @@ -266,7 +267,7 @@ module('the card', function (hooks) { | `setupMotion(hooks)` | resets what outlives an owner (the projection root, the layout-loop guard, motion speed, and every registered reset) around each test, and fails a test that ran a layout loop | | `animationsSettled(opts?)` | resolves when every motion element, layout animation and registered busy probe has stopped, and names what was still moving when it times out | | `bounds(el)` / `shape(el)` / `boundsAndShape(el)` | rects relative to the test root; the cumulative 2×2 transform | -| `velocityOf(el, prop)` | how fast a value is moving right now | +| `velocityOf(el)` | async: how fast the element is moving right now, as `{ x, y }` in px/s, sampled across two frames | | `isMotionIdle()` / `whatIsBusy()` | the probe underneath, for a custom wait | | `registerMotionReset(fn)` / `resetMotion()` | add a reset of your own, for a layer that keeps document-wide state; run them all | From 970cada35256f86fe17e0dc96ff3876e822be046 Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Mon, 12 Oct 2026 16:47:29 -0400 Subject: [PATCH 3/3] Parse the docs with the host's Markdown pipeline, and keep the reference proof check-realm reads headings and links with marked and marked-gfm-heading-id, the pair the host renders with, so setext headings, repeats, entities and code (fenced, indented or inline) come out as rendered. Imports resolve through each package's exports map, default imports and exports are checked, `export *` from a directory is followed and one from another package is reported. Malformed escapes are reported rather than thrown, a fragment on a relative directory link is checked, the archived-repo match is anchored, and files and headings are read once. The choreo-player reference proof and release gate move to realm/design/, and the README says what that measurement was. documentation-site.md names the checks and tools where they live now. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018tqpxGBVBUCe7nACwttGag --- packages/choreo-gallery/package.json | 2 + .../choreo-gallery/realm/design/README.md | 2 + .../design/choreo-player-reference-proof.md | 35 ++ .../realm/design/documentation-site.md | 14 +- packages/choreo-gallery/tools/check-realm.mjs | 349 ++++++++++-------- packages/choreo-player/README.md | 6 +- pnpm-lock.yaml | 6 + 7 files changed, 256 insertions(+), 158 deletions(-) create mode 100644 packages/choreo-gallery/realm/design/choreo-player-reference-proof.md diff --git a/packages/choreo-gallery/package.json b/packages/choreo-gallery/package.json index 759e5569cc8..7eb68956724 100644 --- a/packages/choreo-gallery/package.json +++ b/packages/choreo-gallery/package.json @@ -43,6 +43,8 @@ "eslint": "^9.39.4", "glimmer-motion": "workspace:*", "globals": "^15.15.0", + "marked": "catalog:", + "marked-gfm-heading-id": "catalog:", "playwright": "1.61.0", "prettier": "catalog:", "prettier-plugin-ember-template-tag": "catalog:", diff --git a/packages/choreo-gallery/realm/design/README.md b/packages/choreo-gallery/realm/design/README.md index 16ea6ffe5ca..7734e64f364 100644 --- a/packages/choreo-gallery/realm/design/README.md +++ b/packages/choreo-gallery/realm/design/README.md @@ -54,6 +54,8 @@ during the work. [towers-quality.md](towers-quality.md) - [sagrada-web-model.md](sagrada-web-model.md) — the Sagrada picture's procedural model pass +- [choreo-player-reference-proof.md](choreo-player-reference-proof.md) — the + Playhead render with one and ten workers, and the 1.0 release gate - [gallery/](gallery/README.md) — the 3D gallery room and its narrated tours: room design, tile capacity, tour script and pacing, performance diff --git a/packages/choreo-gallery/realm/design/choreo-player-reference-proof.md b/packages/choreo-gallery/realm/design/choreo-player-reference-proof.md new file mode 100644 index 00000000000..31d4074215b --- /dev/null +++ b/packages/choreo-gallery/realm/design/choreo-player-reference-proof.md @@ -0,0 +1,35 @@ +# The choreo-player reference proof + +> From `docs/choreo-player.md` in Choreo's own repository, where the fixture +> lives. It records a measurement made once, and the gate that was planned +> to repeat it for a 1.0 release. Nothing in the Boxel monorepo runs either. + +## Reference proof + +`videos/playhead-recording` is the installation and render fixture. It: + +- builds the real Glimmer gallery application; +- copies the published-shape `choreo-player/hyperframes` module into the + composition; +- mounts the real Playhead Glimmer component with no added border; +- binds native `hf-seek` to its awaited frame transaction; +- pins HyperFrames 0.8.14 locally; and +- renders 1920×1080, 30fps, with ten workers. + +The proof was compared with a same-build one-worker render. FFmpeg measured +54.15 dB average PSNR and 0.999583 SSIM, and found no post-opening frame that +matched the opening frame at any worker boundary. + +## 1.0 release gate + +Before publishing: + +1. Build and run the package contract tests. +2. Run `pnpm pack` and inspect the tarball contents. +3. Install the tarball in the reference fixture. +4. Run the complete interactive Ember suite. +5. Run HyperFrames check and render with one and ten workers. +6. Scan every worker boundary for an opening-frame insertion. + +The npm artifact should contain only `dist`, `README.md`, `LICENSE`, and +`package.json`. diff --git a/packages/choreo-gallery/realm/design/documentation-site.md b/packages/choreo-gallery/realm/design/documentation-site.md index 95313077dd0..1dae165a550 100644 --- a/packages/choreo-gallery/realm/design/documentation-site.md +++ b/packages/choreo-gallery/realm/design/documentation-site.md @@ -1,7 +1,9 @@ # Choreo Documentation Website > The design of the test app's documentation site, which the gallery realm's -> guides take over. Paths name the test app as it was. +> guides take over. Paths into the test app name it as it was. The tools and +> checks are named where they live now, in `packages/choreo-gallery`; the realm +> check (`pnpm lint:realm`) replaced `docs:check`. The guides are part of `test-app`, sharing its brand mark, theme, navigation, and deployment. Open `/docs`; `/docs/:topic_id` is a guide, `/playground/:demo_id` is a live tuning workspace, and `/_widgets` opens the 3D gallery. @@ -55,7 +57,7 @@ Publish the complete build and required static media using the chosen host. Do n ## API Coverage and Depth -`packages/choreo-gallery/tools/api-inventory.json` maps 343 exported names and yielded vocabulary members to their concept guides. Each registered guide has at least 300 explanatory prose words, excluding code and API tables. Related aliases and supporting types share their behavior chapter. `scripts/check-guide-coverage.mjs` compares the inventory with the TypeScript declarations and checks depth, preambles, section goals, and internal guide links. Run `pnpm docs:check` after an API or documentation change. +`packages/choreo-gallery/tools/api-inventory.json` maps the exported names and yielded vocabulary members to their concept guides. Each registered guide has at least 300 explanatory prose words, excluding code and API tables. Related aliases and supporting types share their behavior chapter. `packages/choreo-gallery/tools/check-realm.mjs` compares the inventory with the packages' exports and checks depth, preambles, section goals, and internal guide links. Run `pnpm lint:realm` in `packages/choreo-gallery` after an API or documentation change. The inventory appears at `/docs/core-api-inventory`. Keep it synchronized when adding a public API. Do not automatically map a new export to a broad introduction simply to satisfy the check: review whether its concept needs a dedicated treatment. @@ -76,27 +78,27 @@ Use the same binding name wherever the same source variable is shared. Use disti Timing-only edits use the workbench’s `live-demo-motion.ts` adapter after Glimmer’s `postRender` hook. It retimes the existing Motion visual elements and preserves repeating animations’ phase; it does not remount the demo. -Run `node scripts/check-demo-live-controls.mjs` against the dev server to +Run `node packages/choreo-gallery/tools/check-demo-live-controls.mjs` against the dev server to verify live duration and target edits, DOM identity, and compact control rows. Each catalog demo includes two authored looks in DialKit’s Versions menu. `demo-presets.ts` maps each look to explicit demo variable names and values. Version 1 restores the source defaults; choosing a look keeps the demo mounted. Add presets only after their variables are registered, and keep all numeric -values inside their declared ranges. Run `node scripts/check-demo-presets.mjs` +values inside their declared ranges. Run `node packages/choreo-gallery/tools/check-demo-presets.mjs` against the development server to exercise both looks and restoration. Teaching coverage is tracked in `test-app/app/content/demo-lessons.json`. Each catalog example has a concept explanation, an experiment, a pitfall, a composition idea and a concept-guide link. The shared GuideDemo renders these beside the example code. Seven focused walkthroughs are registered in `demo-guides.ts`. -`docs:check` verifies that all catalog examples remain embedded and have complete +`pnpm lint:realm` verifies that all catalog examples remain embedded and have complete teaching records; prose correctness still requires source review. [docs-studies-plan.md](docs-studies-plan.md) specifies the planned docs-only spatial and film studies. Keep these separate from shipped demo coverage until implemented. The 3D room and its tile frames force a dark palette without overwriting the -visitor’s saved theme. `scripts/check-guide-lessons.mjs` checks real embedded +visitor’s saved theme. `packages/choreo-gallery/tools/check-guide-lessons.mjs` checks real embedded controls and this gallery theme behaviour against the development server. ## Complete tutorials diff --git a/packages/choreo-gallery/tools/check-realm.mjs b/packages/choreo-gallery/tools/check-realm.mjs index 575e0a1f1a9..4f120f41a97 100644 --- a/packages/choreo-gallery/tools/check-realm.mjs +++ b/packages/choreo-gallery/tools/check-realm.mjs @@ -25,6 +25,8 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import { Marked } from 'marked'; +import { getHeadingList, gfmHeadingId } from 'marked-gfm-heading-id'; import ts from 'typescript'; const root = path.resolve( @@ -334,42 +336,73 @@ function markdownFiles(dir) { .map((file) => path.join(dir, file)); } const relative = (file) => path.relative(root, file); -/** fenced code, which may quote link or import syntax it doesn't mean */ -const withoutFences = (markdown) => - markdown.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, ''); +/** each file is read, and each document's headings worked out, once */ +function memoized(fn) { + const cache = new Map(); + return (key) => { + if (!cache.has(key)) { + cache.set(key, fn(key)); + } + return cache.get(key); + }; +} +const read = memoized((file) => fs.readFileSync(file, 'utf8')); +/** undefined for a malformed escape, rather than a URIError */ +function decode(text) { + try { + return decodeURIComponent(text); + } catch { + return undefined; + } +} /** - * The ids GitHub gives a document's headings. The host computes the same - * slugs but renders each id with a `user-content-` prefix - * (runtime-common/marked-sync.ts), as GitHub does in its HTML. + * The ids a document's headings get, from the parser and slugger the host + * uses (runtime-common/marked-sync.ts), so setext headings, entities and + * repeats come out as they render. The host prefixes each id with + * `user-content-`, as GitHub does in its HTML. */ -function headingIds(markdown) { - const ids = new Set(); - const seen = new Map(); - for (const [, text] of withoutFences(markdown).matchAll( - /^#{1,6}\s+(.*?)\s*#*\s*$/gm, - )) { - // a code span keeps its text as written, `` and `/_sylva` included; - // outside one, tags, link targets and emphasis underscores drop out - const base = text - .split(/(`[^`]*`)/) - .map((part, i) => - i % 2 - ? part.slice(1, -1) - : part - .replace(/<[^>]+>/g, '') - .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') - .replace(/(? { + headingParser.parse(read(file)); + return new Set(getHeadingList().map((heading) => heading.id)); +}); +/** + * The link and image targets in a document's prose. Code, fenced, indented + * or inline, isn't prose, so a link it quotes isn't one. + */ +const linkParser = new Marked(); +function linkTargets(file) { + const targets = []; + linkParser.walkTokens(linkParser.lexer(read(file)), (token) => { + if (token.type === 'link' || token.type === 'image') { + targets.push(token.href); + } + }); + return targets; +} +/** + * What is wrong with a `#fragment` on a link to `target`, a Markdown file or a + * directory (whose README.md GitHub shows), if anything. + */ +function fragmentProblem(target, fragment) { + if (!fragment) { + return undefined; } - return ids; + const id = decode(fragment); + if (id === undefined) { + return 'has a malformed escape'; + } + const isDirectory = + fs.existsSync(target) && fs.statSync(target).isDirectory(); + // GitHub anchors a directory page's README at `#readme` + if (isDirectory && id === 'readme') { + return undefined; + } + const doc = isDirectory ? path.join(target, 'README.md') : target; + return doc.endsWith('.md') && fs.existsSync(doc) && !headingIds(doc).has(id) + ? 'names no heading' + : undefined; } const realmDocs = [ @@ -386,31 +419,28 @@ const brokenLinks = []; for (const file of [...realmDocs, ...packageReadmes]) { // a realm doc is published with the realm, so it may link only inside it const inRealm = file.startsWith(realm + path.sep); - const markdown = fs.readFileSync(file, 'utf8'); - for (const [, target] of withoutFences(markdown).matchAll( - /\]\(\s*]+)>?(?:\s+"[^"]*")?\s*\)/g, - )) { + for (const target of linkTargets(file)) { if (/^[a-z][a-z+.-]*:/i.test(target)) { continue; } const [pathPart, fragment] = target.split('#'); + const decoded = decode(pathPart); + if (decoded === undefined) { + brokenLinks.push(`${relative(file)}: ${target} has a malformed escape`); + continue; + } const resolved = pathPart - ? path.resolve(path.dirname(file), decodeURIComponent(pathPart)) + ? path.resolve(path.dirname(file), decoded) : file; // an extensionless link is a card: `../demos/fold` is demos/fold.json const exists = fs.existsSync(resolved) || fs.existsSync(`${resolved}.json`); + const problem = exists && fragmentProblem(resolved, fragment); if (inRealm && (!resolved.startsWith(realm + path.sep) || !exists)) { brokenLinks.push(`${relative(file)}: ${target} is not a realm file`); } else if (!exists) { brokenLinks.push(`${relative(file)}: ${target} names no file`); - } else if ( - fragment && - resolved.endsWith('.md') && - !headingIds(fs.readFileSync(resolved, 'utf8')).has( - decodeURIComponent(fragment), - ) - ) { - brokenLinks.push(`${relative(file)}: ${target} names no heading`); + } else if (problem) { + brokenLinks.push(`${relative(file)}: ${target} ${problem}`); } } } @@ -421,92 +451,111 @@ assert.deepEqual(brokenLinks, [], 'Broken links in the docs'); * glimmer-motion into @cardstack/choreo, so a name imported from a package * has to be one that package exports today. */ -const packageSources = { - 'glimmer-motion': 'packages/glimmer-motion/src', - '@cardstack/choreo': 'packages/choreo/src', - '@cardstack/choreo-player': 'packages/choreo-player/src', +const packageDirs = { + 'glimmer-motion': 'packages/glimmer-motion', + '@cardstack/choreo': 'packages/choreo', + '@cardstack/choreo-player': 'packages/choreo-player', }; const retiredSpecifiers = new Set(['choreo', 'choreo-player']); -function moduleFile(specifier) { - const name = Object.keys(packageSources) +const packageOf = (specifier) => + Object.keys(packageDirs) .sort((a, b) => b.length - a.length) .find((pkg) => specifier === pkg || specifier.startsWith(`${pkg}/`)); - if (!name) { - return undefined; +/** + * The source module a specifier reaches through its package's `exports` map, + * as a consumer resolves it: an exact subpath, else a `*` pattern. + */ +function moduleFile(specifier) { + const name = packageOf(specifier); + const dir = path.join(root, packageDirs[name]); + const { exports } = JSON.parse(read(path.join(dir, 'package.json'))); + const subpath = `.${specifier.slice(name.length)}`; + let target = exports[subpath]; + if (!target) { + for (const [key, value] of Object.entries(exports)) { + const [prefix, suffix] = key.split('*'); + if ( + suffix !== undefined && + subpath.length >= prefix.length + suffix.length && + subpath.startsWith(prefix) && + subpath.endsWith(suffix) + ) { + const star = subpath.slice( + prefix.length, + subpath.length - suffix.length, + ); + target = JSON.parse(JSON.stringify(value).replaceAll('*', star)); + break; + } + } } - const base = path.join( - root, - packageSources[name], - specifier.slice(name.length + 1) || 'index', - ); - return [ - `${base}.ts`, - `${base}.gts`, - path.join(base, 'index.ts'), - path.join(base, 'index.gts'), - ].find((candidate) => fs.existsSync(candidate)); + const source = target?.['developing:choreo']; + const file = source && path.join(dir, source); + return file && fs.existsSync(file) ? file : undefined; } +/** + * A module's export names, `default` included. `unknown` names a re-export + * whose names can't be listed, so imports from the module can't be checked. + */ function exportsOf(file) { const tree = ts.createSourceFile( file, - fs.readFileSync(file, 'utf8'), + read(file), ts.ScriptTarget.Latest, true, ); const names = new Set(); + const hasModifier = (node, kind) => + node.modifiers?.some((modifier) => modifier.kind === kind); for (const node of tree.statements) { if (ts.isExportDeclaration(node) && !node.exportClause) { - // `export * from './x'`: follow it. From another package, anything may - // come through. + // `export * from './x'`: follow it const specifier = node.moduleSpecifier.text; const base = path.resolve(path.dirname(file), specifier); const target = specifier.startsWith('.') - ? [base, `${base}.ts`, `${base}.gts`].find( + ? [ + base, + `${base}.ts`, + `${base}.gts`, + path.join(base, 'index.ts'), + path.join(base, 'index.gts'), + ].find( (candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isFile(), ) : undefined; - const inner = target && exportsOf(target); - if (!inner) { - return undefined; + const inner = target + ? exportsOf(target) + : { unknown: `export * from '${specifier}'` }; + if (inner.unknown) { + return inner; } - inner.forEach((name) => names.add(name)); - continue; - } - if ( - ts.isExportDeclaration(node) && - node.exportClause && - ts.isNamespaceExport(node.exportClause) - ) { - names.add(node.exportClause.name.text); - continue; - } - if ( - ts.isExportDeclaration(node) && - node.exportClause && - ts.isNamedExports(node.exportClause) - ) { - for (const item of node.exportClause.elements) { - names.add(item.name.text); - } - } else if ( - node.modifiers?.some( - (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword, - ) - ) { - if (node.name) { - names.add(node.name.text); + inner.names.forEach((name) => names.add(name)); + } else if (ts.isExportDeclaration(node)) { + if (ts.isNamespaceExport(node.exportClause)) { + names.add(node.exportClause.name.text); + } else { + for (const item of node.exportClause.elements) { + names.add(item.name.text); + } } - if (ts.isVariableStatement(node)) { + } else if (ts.isExportAssignment(node) && !node.isExportEquals) { + names.add('default'); + } else if (hasModifier(node, ts.SyntaxKind.ExportKeyword)) { + if (hasModifier(node, ts.SyntaxKind.DefaultKeyword)) { + names.add('default'); + } else if (ts.isVariableStatement(node)) { for (const item of node.declarationList.declarations) { names.add(item.name.getText(tree)); } + } else if (node.name) { + names.add(node.name.text); } } } - return names; + return { names }; } -const exportCache = new Map(); +const moduleExports = memoized(exportsOf); const importedDocs = [ ...realmDocs, ...markdownFiles(guidesDir), @@ -516,8 +565,7 @@ const designDir = path.join(realm, 'design') + path.sep; const badImports = []; // the design records sketch APIs that were never built for (const file of importedDocs.filter((doc) => !doc.startsWith(designDir))) { - const markdown = fs.readFileSync(file, 'utf8'); - for (const [statement, clause, specifier] of markdown.matchAll( + for (const [statement, clause, specifier] of read(file).matchAll( /\bimport\s+(?:type\s+)?([\w$]+\s*,\s*\{[^}]*\}|\{[^}]*\}|[\w$]+|\*\s+as\s+[\w$]+)\s+from\s+['"]([^'"]+)['"]/g, )) { const where = `${relative(file)}: ${statement.replace(/\s+/g, ' ')}`; @@ -525,70 +573,66 @@ for (const file of importedDocs.filter((doc) => !doc.startsWith(designDir))) { badImports.push(`${where} — no such package`); continue; } - const isOurs = Object.keys(packageSources).some( - (pkg) => specifier === pkg || specifier.startsWith(`${pkg}/`), - ); - if (!isOurs) { + if (!packageOf(specifier)) { continue; } const module = moduleFile(specifier); if (!module) { - badImports.push(`${where} — no such module`); + badImports.push(`${where} — ${specifier} is not an entry point`); continue; } - if (!exportCache.has(module)) { - exportCache.set(module, exportsOf(module)); + const exported = moduleExports(module); + if (exported.unknown) { + badImports.push( + `${where} — ${relative(module)} has \`${exported.unknown}\`, so its exports can't be checked`, + ); + continue; } - const exported = exportCache.get(module); - const named = clause.match(/\{([^}]*)\}/)?.[1] ?? ''; - for (const item of named.split(',')) { - const name = item - .trim() - .replace(/^type\s+/, '') - .split(/\s+as\s+/)[0]; - if (name && exported && !exported.has(name)) { - badImports.push(`${where} — ${specifier} has no export ${name}`); + // `X` and `X, { … }` take the default export; `* as X` takes the module + const imported = clause.startsWith('*') + ? [] + : [ + ...(/^[\w$]/.test(clause) ? ['default'] : []), + ...(clause.match(/\{([^}]*)\}/)?.[1] ?? '') + .split(',') + .map( + (item) => + item + .trim() + .replace(/^type\s+/, '') + .split(/\s+as\s+/)[0], + ) + .filter(Boolean), + ]; + for (const name of imported) { + if (!exported.names.has(name)) { + badImports.push( + `${where} — ${specifier} has no ${name === 'default' ? 'default export' : `export ${name}`}`, + ); } } } } assert.deepEqual(badImports, [], 'Imports the packages do not provide'); -/** a `#fragment` on a URL to a Markdown file, or to a directory's README */ -function namesNoHeading(target, fragment) { - if (!fragment) { - return false; - } - const isDirectory = - fs.existsSync(target) && fs.statSync(target).isDirectory(); - // GitHub anchors a directory page's README at `#readme` - if (isDirectory && fragment === 'readme') { - return false; - } - const doc = isDirectory ? path.join(target, 'README.md') : target; - return ( - doc.endsWith('.md') && - fs.existsSync(doc) && - !headingIds(fs.readFileSync(doc, 'utf8')).has(decodeURIComponent(fragment)) - ); -} - /** the hosted gallery is this realm, so its URLs name realm files */ const siteURLs = []; for (const file of importedDocs) { - const markdown = fs.readFileSync(file, 'utf8'); - for (const [url, pathname, fragment] of markdown.matchAll( + for (const [url, pathname, fragment] of read(file).matchAll( /https:\/\/choreo\.boxel\.site\/([^\s)"'<>`#?]*)(?:#([^\s)"'<>`]+))?/g, )) { - const target = path.join(realm, decodeURIComponent(pathname)); + const decoded = decode(pathname); + const target = + decoded === undefined ? undefined : path.join(realm, decoded); const exists = pathname === '' || - fs.existsSync(target) || - fs.existsSync(`${target}.json`); + (target !== undefined && + (fs.existsSync(target) || fs.existsSync(`${target}.json`))); + const problem = exists && target && fragmentProblem(target, fragment); if (!exists) { siteURLs.push(`${relative(file)}: ${url}`); - } else if (namesNoHeading(target, fragment)) { - siteURLs.push(`${relative(file)}: ${url} names no heading`); + } else if (problem) { + siteURLs.push(`${relative(file)}: ${url} ${problem}`); } } } @@ -605,20 +649,25 @@ assert.deepEqual( */ const repoLinks = []; for (const file of importedDocs) { - const markdown = fs.readFileSync(file, 'utf8'); + const markdown = read(file); for (const [url, repoPath, fragment] of markdown.matchAll( /https:\/\/github\.com\/cardstack\/boxel\/(?:blob|tree)\/main\/([^\s)"'<>`#?]+)(?:#([^\s)"'<>`]+))?/g, )) { - const target = path.join(root, repoPath); - if (!fs.existsSync(target)) { + const decoded = decode(repoPath); + const target = decoded === undefined ? undefined : path.join(root, decoded); + if (!target || !fs.existsSync(target)) { repoLinks.push(`${relative(file)}: ${url} names no file`); - } else if (namesNoHeading(target, fragment)) { - repoLinks.push(`${relative(file)}: ${url} names no heading`); + continue; + } + const problem = fragmentProblem(target, fragment); + if (problem) { + repoLinks.push(`${relative(file)}: ${url} ${problem}`); } } if (!file.startsWith(designDir)) { + // `cardstack/choreo` itself, not a repository whose name starts with it for (const [url] of markdown.matchAll( - /https:\/\/github\.com\/cardstack\/choreo[^\s)"'<>`]*/g, + /https:\/\/github\.com\/cardstack\/choreo(?![\w.-])[^\s)"'<>`]*/g, )) { repoLinks.push(`${relative(file)}: ${url} is the archived repository`); } diff --git a/packages/choreo-player/README.md b/packages/choreo-player/README.md index 3222963e5bb..c00f366da08 100644 --- a/packages/choreo-player/README.md +++ b/packages/choreo-player/README.md @@ -129,5 +129,7 @@ barrier, and returns an event-listener cleanup function. It does not import HyperFrames or inspect the DOM. Parallel safety is a property of `renderFrameAt()`, not a promise made by this -package. The reference Playhead fixture is verified with both one and ten -workers and scanned for opening-frame flashes at every worker boundary. +package. The reference Playhead fixture, in Choreo's original repository, was +rendered once with one and once with ten workers, and scanned for opening-frame +flashes at every worker boundary: see +[the reference proof](https://choreo.boxel.site/design/choreo-player-reference-proof.md). diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c9ccb3d216..85286de6d45 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2497,6 +2497,12 @@ importers: globals: specifier: ^15.15.0 version: 15.15.0 + marked: + specifier: 'catalog:' + version: 12.0.2 + marked-gfm-heading-id: + specifier: 'catalog:' + version: 3.2.0(marked@12.0.2) playwright: specifier: 1.61.0 version: 1.61.0