Skip to content

Commit 8d569f8

Browse files
committed
Prepare Posecode 0.2 for third-party embeds
1 parent c9c4088 commit 8d569f8

58 files changed

Lines changed: 1000 additions & 406 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ jobs:
2525
- name: Typecheck
2626
run: npm run typecheck
2727

28+
- name: Build publishable packages
29+
run: npm run build:packages
30+
2831
- name: Build playground
2932
run: npm run build
3033

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.2.0] - 2026-07-15
11+
1012
### Added
1113

14+
- **Canonical timing modes**: `flow`, `settle`, `drive`, `snap`, and `linear`
15+
now describe continuous motion, deliberate rests, acceleration, and sharp
16+
arrivals. The v0.1 easing spellings remain accepted as deprecated aliases.
17+
- **Third-party validation CLI**: `npx posecode-parser@0.2.0 validate <path>`
18+
recursively checks `.posecode` libraries, with `--strict` and `--json` modes
19+
for CI.
20+
- **Embed compatibility metadata**: the CDN bundle exports `version`,
21+
`languageVersion`, and `validatePosecode()`. Player ready/error events now
22+
include structured version and diagnostic details, while host elements expose
23+
a machine-readable loading/ready/error state.
24+
- **Release contract checks**: CI builds the publishable package chain so a
25+
source-only language change cannot land without a valid CDN bundle.
1226
- **Realistic human figure**: the playground, landing hero, and `<posecode-player>` embeds now render a fully rigged, textured human character (hands with articulated fingers, sneakers, face) instead of the procedural capsule mannequin. All solving (FK, ground-lock, pins, reach-IK) still runs on the driver skeleton, rebuilt to the character's exact proportions and retargeted bone-for-bone every frame; the procedural figure remains as an automatic fallback (and via `?figure=classic` / `character="off"`).
1327
- **Self-collision resolution**: a capsule-based de-penetration pass keeps forearms/hands out of the torso, head, and legs (and shins out of each other), clamped to healthy ROM, so limbs no longer pass through the body mid-movement.
1428
- **Solid props**: props now declare blocking faces (the wall's surface, the chair's backrest and seat edge, the box's near face) and a contact pass keeps the body out of them — translating the whole figure along the face normal, or bending the offending leg's hip clear (ROM-clamped). Limbs pinned/gripped/reached to a prop anchor stay exempt as declared support. A new `solid-props` eval invariant (independent geometry re-derivation) guards every prop movement against this bug class.

README.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ A `.posecode` file describes movement as timed phases with targeted joint action
171171

172172
| 1. Write `.posecode` | 2. Render the movement |
173173
| :--- | :--- |
174-
| **`posecode`** `exercise "Body-weight squat"`<br />**`rig`** `humanoid`<br />**`pose`** `start = standing`<br /><br />**`step`** `"Descend" 1.6s ease-in-out`:<br />&nbsp;&nbsp;`hips: flex 80`<br />&nbsp;&nbsp;`knees: flex 95`<br />&nbsp;&nbsp;`ankles: dorsiflex 14`<br />&nbsp;&nbsp;`ground-lock: feet`<br />&nbsp;&nbsp;`cue "Sit the hips back"`<br /><br />**`step`** `"Drive up" 1.2s ease-out`:<br />&nbsp;&nbsp;`hips: flex 0`<br />&nbsp;&nbsp;`knees: flex 0`<br />&nbsp;&nbsp;`ankles: dorsiflex 0`<br />&nbsp;&nbsp;`ground-lock: feet`<br /><br />**`repeat`** `8` | <img src="docs/media/squat.gif" width="340" alt="Squat animation" /> |
174+
| **`posecode`** `exercise "Body-weight squat"`<br />**`rig`** `humanoid`<br />**`pose`** `start = standing`<br /><br />**`step`** `"Descend" 1.6s settle`:<br />&nbsp;&nbsp;`hips: flex 80`<br />&nbsp;&nbsp;`knees: flex 95`<br />&nbsp;&nbsp;`ankles: dorsiflex 14`<br />&nbsp;&nbsp;`ground-lock: feet`<br />&nbsp;&nbsp;`cue "Sit the hips back"`<br /><br />**`step`** `"Drive up" 1.2s drive`:<br />&nbsp;&nbsp;`hips: flex 0`<br />&nbsp;&nbsp;`knees: flex 0`<br />&nbsp;&nbsp;`ankles: dorsiflex 0`<br />&nbsp;&nbsp;`ground-lock: feet`<br /><br />**`repeat`** `8` | <img src="docs/media/squat.gif" width="340" alt="Squat animation" /> |
175175

176176
---
177177

@@ -355,33 +355,31 @@ npm install posecode-render
355355
Example:
356356

357357
```ts
358-
import { parsePosecode } from "posecode-parser";
359-
import { PosecodeRenderer } from "posecode-render";
358+
import { parse } from "posecode-parser";
359+
import { createViewer } from "posecode-render";
360360

361361
const source = `
362-
exercise "Lateral raise"
363-
rig humanoid
364-
pose start = standing
362+
posecode exercise "Lateral raise"
363+
rig humanoid
364+
pose start = standing
365365
366-
step "Raise" 1.4s ease-in-out:
367-
shoulders: abduct 90
366+
step "Raise" 1.4s settle:
367+
shoulders: abduct 90
368368
`;
369369

370-
const result = parsePosecode(source);
370+
const { ir, errors, warnings } = parse(source);
371371

372-
if (!result.ok) {
373-
console.error(result.errors);
372+
if (!ir || errors.length > 0) {
373+
console.error(errors);
374374
} else {
375-
const renderer = new PosecodeRenderer({
376-
container: document.querySelector("#viewer"),
377-
});
378-
379-
renderer.load(result.document);
380-
renderer.play();
375+
console.warn(warnings);
376+
const viewer = createViewer(document.querySelector("#viewer"));
377+
viewer.load(ir);
378+
viewer.play();
381379
}
382380
```
383381

384-
> Adjust this example to match the current exported APIs before submission.
382+
The `#viewer` element is an HTML `<canvas>`.
385383

386384
---
387385

SECURITY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ We actively support and fix security issues in the following versions of Posecod
66

77
| Version | Supported |
88
| ------- | --------- |
9-
| v0.1.x | Yes |
9+
| v0.2.x | Yes |
10+
| v0.1.x | Security fixes only |
1011

1112
---
1213

editors/vscode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"displayName": "Posecode",
55
"description": "Language support for the Posecode (.posecode) kinematic motion DSL: syntax highlighting, range-of-motion diagnostics, completion, and hover.",
6-
"version": "0.1.0",
6+
"version": "0.2.0",
77
"license": "MIT",
88
"engines": {
99
"vscode": "^1.85.0"

package-lock.json

Lines changed: 28 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "posecode-monorepo",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"private": true,
55
"description": "Posecode: a kinematic motion protocol for LLMs. Describe 3D human movement as text; render it in the browser.",
66
"license": "MIT",
@@ -15,6 +15,7 @@
1515
"coverage": "vitest run --coverage",
1616
"dev": "npm run dev -w playground",
1717
"build": "npm run build -w playground",
18+
"build:packages": "npm run build -w posecode-parser && npm run build -w posecode-share && npm run build -w posecode-render && npm run build -w posecode-embed && npm run build -w posecode-mcp",
1819
"prepare:xbot": "node scripts/prepare-xbot.mjs",
1920
"eval": "tsx packages/posecode-eval/src/cli.ts",
2021
"video:cut2": "remotion render video/cut2/index.tsx PosecodeCut2 docs/launch-media/posecode-cut2-builder-16x9.mp4 --public-dir video/cut2/public --codec h264 --crf 16",

packages/posecode-embed/README.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ the movement as an animated 3D figure, right where a share link would have gone.
99
## Quick start (CDN, no build step)
1010

1111
```html
12-
<script src="https://unpkg.com/posecode-embed/dist/posecode-embed.js"></script>
12+
<script src="https://unpkg.com/posecode-embed@0.2.0/dist/posecode-embed.js"></script>
1313

1414
<!-- 1. From a share token (what a posecode.org permalink carries) -->
1515
<posecode-player doc="cG9zZWNvZGUgZXhlcmNpc2Ug…"></posecode-player>
@@ -22,10 +22,10 @@ the movement as an animated 3D figure, right where a share link would have gone.
2222
posecode exercise "Lateral raise"
2323
rig humanoid
2424
pose start = standing
25-
step "Raise" 1.4s ease-out:
25+
step "Raise" 1.4s settle:
2626
shoulders: abduct 90
2727
elbows: flex 10
28-
step "Lower" 1.6s ease-in:
28+
step "Lower" 1.6s drive:
2929
shoulders: abduct 0
3030
elbows: flex 0
3131
repeat 8
@@ -35,6 +35,10 @@ posecode exercise "Lateral raise"
3535
The script auto-registers the element and boots each player when it scrolls into
3636
view. That's it.
3737

38+
Pin a package version in production, as above, so a deployment always uses a
39+
known parser/render pair. A `src` URL can be relative or absolute; cross-origin
40+
movement files must be served with CORS permission.
41+
3842
## With a bundler
3943

4044
```bash
@@ -86,11 +90,33 @@ Boolean attributes accept `false` / `0` / `no` / `off` to turn them off, so
8690

8791
```js
8892
const player = document.querySelector("posecode-player");
89-
player.addEventListener("posecode:ready", () => player.viewer.pause());
90-
player.addEventListener("posecode:error", (e) => console.warn(e.detail.error));
93+
player.addEventListener("posecode:ready", (e) => {
94+
console.log(e.detail.version, e.detail.languageVersion, e.detail.warnings);
95+
player.viewer.pause();
96+
});
97+
player.addEventListener("posecode:error", (e) => {
98+
console.warn(e.detail.code, e.detail.error, e.detail.errors);
99+
});
91100

92101
player.toggle(); // play / pause
93102
player.viewer; // the underlying render Viewer (null until booted)
94103
```
95104

105+
The host element reflects `data-posecode-state="loading|ready|error"`, plus the
106+
package and language versions, for integration tests and monitoring. Existing
107+
listeners that only read `event.detail.error` remain compatible.
108+
109+
CDN users can validate source without creating WebGL:
110+
111+
```js
112+
const result = Posecode.validatePosecode(source);
113+
console.log(Posecode.version, Posecode.languageVersion, result.errors);
114+
```
115+
116+
For a movement library in CI, run:
117+
118+
```bash
119+
npx posecode-parser@0.2.0 validate --strict ./movements
120+
```
121+
96122
MIT-licensed, part of [Posecode](https://github.com/posecode-dev/posecode).

packages/posecode-embed/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "posecode-embed",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Embed a live 3D Posecode movement anywhere with one <script> tag \u2014 the <posecode-player> web component.",
55
"license": "MIT",
66
"type": "module",
@@ -32,8 +32,8 @@
3232
"three.js"
3333
],
3434
"dependencies": {
35-
"posecode-parser": "0.1.0",
36-
"posecode-render": "0.1.0",
35+
"posecode-parser": "0.2.0",
36+
"posecode-render": "0.2.0",
3737
"posecode-share": "0.1.0"
3838
},
3939
"devDependencies": {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/** Public compatibility metadata and no-render validation for CDN consumers. */
2+
3+
import { parse, POSECODE_VERSION } from "posecode-parser";
4+
import type { ParseResult } from "posecode-parser";
5+
6+
/** Version of the posecode-embed package/bundle. */
7+
export const version = "0.2.0";
8+
9+
/** Version of the Posecode language understood by this bundle. */
10+
export const languageVersion = POSECODE_VERSION;
11+
12+
/** Validate source without creating a renderer or WebGL context. */
13+
export function validatePosecode(source: string): ParseResult {
14+
return parse(source);
15+
}

0 commit comments

Comments
 (0)