Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ Prints the JSON Schema for scenario files (editor autocompletion, LLM prompts).
rustmotion schema -o schema.json
```

It describes a scenario as written, templating included: `"delay": "$delay"`
validates wherever a number is declared, because the engine substitutes before
it deserializes. A bare string there is still an error — the placeholder has to
look like one. Each union is steered by its tag (`type` on a component, `name`
on an animation), so a validator reports the property that is wrong instead of
failing the whole component against all 54 branches.

### `rustmotion info`

Shows information about a scenario (duration, scene count, dimensions, ...).
Expand Down
20 changes: 18 additions & 2 deletions crates/rustmotion-core/src/schema/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ pub struct TimelineStep {
pub style: Option<Box<crate::css::CssStyle>>,
}

/// Font weight — named ("normal"/"bold") or numeric (100-900)
#[derive(Debug, Clone, JsonSchema, Default)]
#[derive(Debug, Clone, Default)]
pub enum FontWeight {
#[default]
Normal,
Expand Down Expand Up @@ -91,6 +90,23 @@ impl Serialize for FontWeight {
}
}

impl schemars::JsonSchema for FontWeight {
fn schema_name() -> String {
"FontWeight".to_string()
}

fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
serde_json::from_value(serde_json::json!({
"description": "Font weight — \"normal\", \"bold\", or a number.",
"anyOf": [
{ "type": "string", "enum": ["normal", "bold"] },
{ "type": "number" }
]
}))
.expect("FontWeight's schema is a literal written here")
}
}

impl<'de> Deserialize<'de> for FontWeight {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct FontWeightVisitor;
Expand Down
11 changes: 8 additions & 3 deletions crates/rustmotion-core/src/schema/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ pub struct AnimationTiming {
/// this field. The JSON `"loop"` key this deserializes from accepts
/// either shape (see [`AnimationTimingWire`]); `repeat` alone can't
/// tell you which one was written — check `repeat_count` for that.
#[serde(rename = "loop")]
#[serde(default, rename = "loop")]
#[schemars(with = "RepeatSpec")]
pub repeat: bool,
/// How many times the animation plays, when the JSON `"loop"` value
/// was a positive integer rather than a bare bool (issue #330) — e.g.
Expand All @@ -219,7 +220,8 @@ pub struct AnimationTiming {
/// [`RepeatSpec::into_parts`]): there is no visible difference
/// between "play once" and "loop zero times", so there's no reason to
/// carry a count that never changes anything downstream.
#[serde(default)]
#[serde(default, skip)]
#[schemars(skip)]
pub repeat_count: Option<u32>,
/// Reverse direction on every other play (ping-pong) instead of
/// snapping back to the start each cycle — GSAP calls this `yoyo`.
Expand Down Expand Up @@ -256,7 +258,10 @@ fn default_animation_duration() -> f64 {
0.8
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
/// `true` loops forever, `false` plays once, and a positive integer is a
/// total number of plays (`12` for GSAP's `repeat: 11`). `0` and `1` both
/// mean "play once".
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
enum RepeatSpec {
Loop(bool),
Expand Down
2 changes: 2 additions & 0 deletions crates/rustmotion/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ CLI :
- `--report r.json` — rapport JSON
- `--strict-anim` — vérification frame par frame ; ajoute la détection `animated_text_overflow` (transform animé qui sort du viewport à un instant échantillonné). L'échantillonnage s'arrête à `scene.freeze_at`, puisque rien n'est rendu au-delà.
- `--strict-attrs` — promeut en erreurs les attributs inconnus (détection schéma + did-you-mean, activée par défaut en warnings)

`rustmotion schema` exporte un Draft-7 qui accepte un scénario **templaté** (`"$delay"` passe là où un nombre est déclaré) et qui aiguille chaque union sur son tag, pour qu'un validateur nomme la propriété fautive. Ce que `validate` accepte, le schéma l'accepte : c'est verrouillé par `tests/exported_schema_accepts_the_examples.rs`, qui valide les 15 exemples du dépôt contre ce que la commande imprime vraiment.
- `--lenient` — warnings au lieu d'errors

## Google Fonts: the network is denied by default
Expand Down
3 changes: 3 additions & 0 deletions crates/rustmotion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,6 @@ ffmpeg_integration = []
## Re-expose native Lottie decoding (default-on). Activating this feature here
## propagates to rustmotion-components so consumers only need to touch rustmotion.
lottie-native = ["rustmotion-components/lottie-native"]

[dev-dependencies]
boon = "0.6.1"
Loading
Loading