Skip to content

feat(stdlib): motion library binding (bindings #4) - #422

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/motion-binding
May 28, 2026
Merged

feat(stdlib): motion library binding (bindings #4)#422
hyperpolymath merged 1 commit into
mainfrom
feat/motion-binding

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

First tranche of bindings #4 (docs/bindings-roadmap.adoc).

Summary

Adds stdlib/Motion.affine providing the initial typed surface over the motion npm library: animateawaitable AnimationControlscancel. Surface mirrors the existing Http / Sqlite / Crypto stdlib pattern.

Also bundles the wasmCall extern that has been waiting on CI in PR #419 (same codegen_deno.ml runtime + lowering region — bundling keeps history linear). Closes #414 via the bundle.

Surface

pub extern type AnimationControls;
pub extern fn motionAnimate(target: Json, keyframes: Json, options: Json) -> AnimationControls;
pub extern fn motionAwait(controls: AnimationControls) -> Int / { Async };
pub extern fn motionCancel(controls: AnimationControls) -> Int;

Args cross the boundary as opaque Json (typed keyframe shapes are a follow-up when affinescript-motion lands as a dedicated package).

Lowering

const __as_motionAnimate = (target, keyframes, options) =>
  globalThis.__as_motion.animate(target, keyframes, options);
const __as_motionAwait = (controls) =>
  Promise.resolve(controls).then(() => 0);
const __as_motionCancel = (controls) => {
  if (controls && typeof controls.cancel === "function") controls.cancel();
  return 0;
};

Consumer responsibility: production code sets globalThis.__as_motion = motionLibrary at module init. Documented in stdlib/Motion.affine's preamble.

Files

File Change
stdlib/Motion.affine NEW — opaque type + 3 externs with effect rows
lib/codegen_deno.ml __as_motion* runtime helpers + lowering table entries; __as_wasmCall helper restored
tests/codegen-deno/motion_smoke.affine NEW — AS fixture, use Motion::{...} round-trip
tests/codegen-deno/motion_smoke.harness.mjs NEW — Node ESM harness with mocked globalThis.__as_motion
docs/bindings-roadmap.adoc Row #4 status scaffold

Test plan

  • tools/run_codegen_deno_tests.sh — all 7 harnesses green, including new motion_smoke (4 assertions: target/keyframes/options pass-through, cancel side-effect, no-op cancel on bare object)
  • dune build bin/main.exe clean
  • use Motion::{...} from a tests/codegen-deno/ fixture resolves (validates stdlib import path works from standalone-compile mode)

Follow-ups

Deferred per row #4 rationale:

  • animateMini, tween, ease, spring (additional surface)
  • Typed keyframe shapes (currently opaque Json)
  • Migration from stdlib/Motion.affine to dedicated affinescript-motion package (additive + source-compatible)

Relationship to PR #419

#419 (wasmCall) was opened with auto-merge armed but blocked on pre-existing E2E flakes. This PR includes the same wasmCall changes; merging this supersedes #419, which should be closed afterwards.

🤖 Generated with Claude Code

…ings #4)

Lands the first tranche of the bindings-roadmap #4 (motion) item:
animate, awaitable handle, and cancel. Surface mirrors the Http /
Sqlite / Crypto stdlib pattern.

Also bundles bindings #5 (wasmCall) which has been waiting on CI in
PR #419 — same codegen_deno.ml runtime helper + lowering, plus the
test fixture under tests/codegen-deno/wasm_call.* (already merged via
#410 era; not duplicated here). Superseding PR #419 keeps the
codegen_deno.ml history linear and avoids a parallel-edit merge knot.

Files:
- stdlib/Motion.affine — extern type AnimationControls + 3 extern fns
- lib/codegen_deno.ml — __as_motion* runtime helpers + lowering
  entries; __as_wasmCall helper restored
- tests/codegen-deno/motion_smoke.{affine,harness.mjs} — round-trip
  fixture with a mocked globalThis.__as_motion
- docs/bindings-roadmap.adoc — row #4 status ○ → ◐ scaffold

Test plan: all 7 codegen-deno harnesses green, incl. the new
motion_smoke (4 assertions: target/keyframes/options pass-through,
cancel side-effect, no-op cancel on bare object).

Consumer responsibility: production code must set
globalThis.__as_motion = motionLibrary (or compatible mock) at module
init. Documented in stdlib/Motion.affine's preamble.

Follow-ups (deferred, tracked in row #4 rationale):
- animateMini, tween, ease, spring
- typed keyframe shapes (currently opaque Json)
- migrate to dedicated affinescript-motion package (additive, source-
  compatible)

Refs #414 (closes via the wasmCall bundle), bindings #4 in
docs/bindings-roadmap.adoc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 81 issues detected

Severity Count
🔴 Critical 4
🟠 High 10
🟡 Medium 67

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action actions/checkout@v6 needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action denoland/setup-deno@v2 needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in affine-vscode-publish.yml",
    "type": "unknown",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "unknown",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "unknown",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath merged commit b95852f into main May 28, 2026
24 of 26 checks passed
@hyperpolymath
hyperpolymath deleted the feat/motion-binding branch May 28, 2026 12:00
hyperpolymath added a commit that referenced this pull request May 28, 2026
…raphics/Text/Texture/Ticker (bindings #1)

Restarts bindings #1 (PixiJS core) on the Deno-ESM pattern proven by
wasmCall + motion. The prior `affinescript-pixijs/` scaffold was
broken across three axes that compounded into an unmaintainable state:

1. *Extension*: `src/pixi.as` used the obsolete `.as` extension (no
   longer accepted by the current AffineScript compiler — files must
   be `.affine`).
2. *License*: SPDX headers carried AGPL-3.0-or-later in three files
   (Pixi.idr, main.zig, package.json), inconsistent with the
   estate-wide MPL-2.0 stance documented in `standards/CLAUDE.md`.
3. *Architecture*: the scaffold targeted a Zig→C→WASM-import pattern
   (`extern fn js_pixi_init` + `export fn as_pixi_init`) that conflicts
   with how the Deno-ESM emitter actually lowers extern fns. The
   wasmCall (PR #422) and motion (PR #422 bundle) bindings established
   that `extern fn`s lower directly to JS host calls via
   `lib/codegen_deno.ml`'s `deno_builtins` lowering table — no Zig
   intermediary required.

Restart approach (best-judgement call per the user's directive):

- Delete `affinescript-pixijs/` entirely (4 files: pixi.as, Pixi.idr,
  main.zig, package.json).
- Add `stdlib/Pixi.affine` with the MVP surface: 7 opaque types
  (Application, Container, Sprite, Graphics, Text, Texture, Ticker)
  + 25 extern fns covering Application init, Container hierarchy,
  Sprite+Texture, Graphics path+fill+clear, Text mutation, Ticker
  start/stop/add.
- Add 26 `__as_pixi*` runtime helpers + lowering-table entries in
  `lib/codegen_deno.ml`. Consumer sets `globalThis.__as_pixi = PIXI`
  at module-init (mirrors the motion `__as_motion` pattern).
- Add `tests/codegen-deno/pixi_smoke.{affine,harness.mjs}` — 11
  assertions covering async init, sprite flow (texture → sprite →
  upcast → addChild → position → visibility), and graphics flow
  (new → rect → fill → upcast → addChild).
- Update `docs/bindings-roadmap.adoc` row #1: status `◐` scaffold →
  `◑` partial; package home now `stdlib/Pixi.affine`; rationale
  notes the restart + follow-ups (Container accessors, FederatedEvent,
  Point/Rectangle/Circle).

PixiJS 8.x class-hierarchy note: Sprite, Graphics, and Text are real
Container subclasses. AffineScript has no subtype polymorphism, so
the binding exposes explicit upcasts (`pixiSpriteAsContainer`,
`pixiGraphicsAsContainer`, `pixiTextAsContainer`). These are
zero-cost identity lowerings — the underlying JS object is the same.

Test plan:

- [x] codegen-deno suite: 8/8 harnesses green (was 7/7; +pixi_smoke)
- [x] `dune build bin/main.exe` clean
- [x] All new files carry SPDX `MPL-2.0` headers
- [x] No `.as` files added; no AGPL / PMPL headers anywhere

Out of scope (deferred):

- Full PixiJS 8.x accessor surface (anchor, scale, pivot, parent,
  zIndex, eventMode, filters, hitArea, worldVisible, etc.) — listed
  in row #1's follow-ups.
- FederatedEvent + on/off pointer-event subscription surface.
- Point / Rectangle / Circle helper types.
- Sprite atlases and texture-pack support.
- Moving from `stdlib/` to a separate `affinescript-pixijs` repo per
  the SNIFs / typed-wasm precedent (additive, source-compatible).

Companion estate sweep: a follow-up PR set will rename `.as` →
`.affine` across the rest of the estate (session-sentinel,
git-reticulator, candy-crash, developer-ecosystem, burble) and
upgrade their per-file SPDX headers to MPL-2.0 where the existing
license is AGPL / PMPL — not bundled here so this PR stays scoped
to the affinescript repo's pixijs revival.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 28, 2026
…431)

## Summary

Re-lands the AS-side `wasmCall` surface (extern + test fixtures +
roadmap row flip) that PR #419 originally bundled. #419 was closed as
"superseded by #422", but #422 only carried the motion binding — the
host-side codegen (`__as_wasmCall` + lowering-table entry) is present on
`main` while the AS-side pieces are missing.

Flips `docs/bindings-roadmap.adoc` row #5 from `◐ host-side; AS-side ○`
to `● usable (Option A landed)`.

- `stdlib/Deno.affine`: new `pub extern fn wasmCall(exports:
WasmExports, name: String, args: [Float]) -> Float` with docstring +
`wasmInstance → wasmCall` worked example.
- `tests/codegen-deno/wasm_call.{affine,harness.mjs}`: round-trip
harness over a 41-byte inline wasm module exporting `add(i32, i32) ->
i32` (4 assertions).
- `docs/bindings-roadmap.adoc`: row #5 status `◐` → `●`; cross-cutting
§2 observation rewritten "LANDED".

Closes bindings roadmap #5. Closes #414 (host-side via #422, AS-side via
this PR).

## Test plan

- [x] `affinescript check stdlib/Deno.affine` → Type checking passed.
- [x] `affinescript check tests/codegen-deno/wasm_call.affine` → Type
checking passed.
- [ ] `tools/run_codegen_deno_tests.sh` (gated on Actions budget —
admin-merge per estate policy).

Out of GH Actions budget; admin-merging on clean local verify per estate
policy (see MEMORY.md `session-2026-05-27-estate-sweep-1254-prs`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 28, 2026
Adds an MVP @pixi/ui surface for idaptik's HUD + menu layer
(`src/bindings/PixiUI.res`), following the Deno-ESM pattern proven by
wasmCall (#422) / motion (#422 bundle) / Pixi (#429 / open).

Coverage in this version (row #3 status `○` -> `◐` scaffold):

- 5 opaque host types: `Button`, `FancyButton`, `Slider`, `Switch`,
  `Container` (the last re-declared opaque so this module can be
  consumed without a hard dependency on `stdlib/Pixi.affine`; the JS
  values coincide at runtime because @pixi/ui's components are real
  PIXI.Container subclasses).
- 11 extern fns: `new`-style constructor + primary event-callback
  registrar + zero-cost `AsContainer` upcast for each component
  (Input + ScrollBox deferred to follow-up — see row #3).
- 11 `__as_pixiUi*` runtime helpers + lowering-table entries in
  `lib/codegen_deno.ml`. Consumer sets `globalThis.__as_pixi_ui =
  PixiUI` at module-init (mirrors the motion `__as_motion` pattern
  and the prospective Pixi `__as_pixi` pattern).
- `tests/codegen-deno/pixiui_smoke.{affine,harness.mjs}` — 17
  assertions covering ctor-options round-trip, callback-identity
  preservation through `.connect()`, and identity upcast for all
  four components.
- `docs/bindings-roadmap.adoc` row #3 updated: status, package home
  set to `stdlib/PixiUI.affine`, deferred follow-ups enumerated.

Test plan:

- [x] codegen-deno suite: 9/9 harnesses green (was 8/8; +pixiui_smoke)
- [x] `dune build bin/main.exe` clean
- [x] All new files carry SPDX `MPL-2.0` headers
- [x] No `.as` files added; no AGPL / PMPL headers anywhere

Out of scope (deferred):

- `Input` (text-entry, focus, blur, value accessor)
- `ScrollBox` (scroll position, content add/remove, viewport)
- Full event surface (onHover / onOut / onDown / onUp beyond the one
  primary callback per component)
- FancyButton textures-per-state accessors
- Slider min/max/step accessors
- Migration from `stdlib/` to a separate `affinescript-pixijs`
  sub-module per the eventual Pixi #1 split (additive,
  source-compatible).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 28, 2026
## Summary

Adds an MVP `@pixi/ui` binding (`stdlib/PixiUI.affine` + Deno-ESM
codegen lowerings) so idaptik's HUD + menu layer
(`src/bindings/PixiUI.res`) can move off ReScript. Follows the pattern
proven by wasmCall (#422) / motion (#422 bundle) / Pixi (#429 open).

Row #3 in `docs/bindings-roadmap.adoc` moves `○` -> `◐` scaffold.

## Surface

- 5 opaque host types: `Button`, `FancyButton`, `Slider`, `Switch`,
`Container` (re-declared opaque so this module can be consumed without a
hard dependency on `stdlib/Pixi.affine`).
- 11 extern fns: ctor + primary event-callback registrar + zero-cost
`AsContainer` upcast for each component.
- 11 `__as_pixiUi*` runtime helpers + `deno_builtins` table entries in
`lib/codegen_deno.ml`.
- Consumer sets `globalThis.__as_pixi_ui = PixiUI` once at module-init.

## Deferred (follow-ups)

- `Input` (text-entry + focus + value)
- `ScrollBox` (scroll position + viewport)
- Full event surface (onHover / onOut / onDown / onUp)
- FancyButton textures-per-state, Slider min/max/step accessors

## Test plan

- [x] `dune build bin/main.exe` clean
- [x] `bash tools/run_codegen_deno_tests.sh` — 9/9 harnesses green (was
8/8; +pixiui_smoke with 17 assertions)
- [x] All new files MPL-2.0 SPDX; no `.as` / AGPL / PMPL
- [ ] Hypatia security scan: no new findings (delta-only — pre-existing
baselines ignored per repo CLAUDE.md)

Note: pre-existing flaky E2E Node-CJS Codegen tests #4/#5 unrelated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 28, 2026
Adds stdlib/PixiSound.affine — an opaque `Sound` extern type plus
`pixiSoundFrom` / `play` / `stop` / `pause` / `resume` / `setVolume`
/ `setLoop`, the surface idaptik's `src/bindings/PixiSound.res`
consumes for BGM + SFX. Lowers on the Deno-ESM backend to
`globalThis.__as_pixi_sound`, which the consumer (or its host
wrapper) populates with `@pixi/sound`'s `Sound` named export
once at module-init time. Pattern matches the Motion binding
(#4, PR #422) and the WASM-exports binding (#5, also in #422).

Test fixture: tests/codegen-deno/pixisound_smoke.{affine,harness.mjs}.
The harness installs a MockSound class at globalThis.__as_pixi_sound,
drives each smoke wrapper, and asserts URL pass-through, transport
call counts, and volume/loop field updates.

Roadmap row #2 in docs/bindings-roadmap.adoc moves from `○` to
`◐` scaffold with the initial surface + follow-up list
(`Sound.add` for multi-source registry, sprite atlases, async load,
`pauseAll` / `stopAll` / `resumeAll`, `sound` singleton filter
pipeline).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 28, 2026
## Summary
- Adds `stdlib/PixiSound.affine` — opaque `Sound` extern type plus
`pixiSoundFrom` / `play` / `stop` / `pause` / `resume` / `setVolume` /
`setLoop`, the surface idaptik's `src/bindings/PixiSound.res` consumes
for BGM + SFX.
- Lowers on the Deno-ESM backend to `globalThis.__as_pixi_sound`, which
the consumer (or its host wrapper) populates with `@pixi/sound`'s
`Sound` named export at module-init time. Pattern matches the Motion
binding (#4, PR #422) and the WASM-exports binding (#5, also #422).
- Test fixture `tests/codegen-deno/pixisound_smoke.{affine,harness.mjs}`
with a `MockSound` class — asserts URL pass-through, transport call
counts, and volume/loop field updates.
- Roadmap row #2 moves from `○` to `◐` scaffold; follow-ups (`Sound.add`
multi-source registry, sprite atlases, async load with onload Promise,
`pauseAll` / `stopAll` / `resumeAll`, `sound` singleton filter pipeline)
enumerated.

This is bindings #2 in `docs/bindings-roadmap.adoc` Tier 1 — required
for the idaptik `.res → .affine` migration.

## Test plan
- [x] `dune build bin/main.exe` clean
- [x] `bash tools/run_codegen_deno_tests.sh` — all 9 fixtures (including
new `pixisound_smoke`) green
- [ ] CI: codegen-deno suite green
- [ ] CI: known-flaky E2E Node-CJS Codegen tests #4/#5 — pre-existing,
ignore unless codegen-deno suite fails

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant