1717//! This walker runs the new CSS-engine pipeline (taffy + cosmic-text) so the
1818//! geometry it checks matches what the renderer will actually paint.
1919
20+ use rustmotion:: components:: box_builder:: build_scene_from_refs;
21+ use rustmotion:: components:: intrinsic:: TextIntrinsic ;
2022use rustmotion:: components:: { ChildComponent , Component } ;
2123use rustmotion:: core:: css:: taffy_bridge:: ConversionContext ;
2224use rustmotion:: core:: engine:: box_tree:: { AvailableSpace , BoxNode , IntrinsicMeasure } ;
2325use rustmotion:: core:: engine:: layout_pass:: { run_layout, BoxLayout , LayoutResult } ;
2426use rustmotion:: engine:: animator:: {
2527 apply_orbits, apply_wiggles, extract_effects, resolve_animations, AnimatedProperties ,
2628} ;
27- use rustmotion:: components:: box_builder:: build_scene_from_refs;
28- use rustmotion:: components:: intrinsic:: TextIntrinsic ;
2929use rustmotion:: engine:: render;
3030use rustmotion:: schema:: ResolvedScenario ;
3131use serde:: Serialize ;
@@ -59,6 +59,7 @@ pub enum Axis {
5959
6060#[ derive( Debug , Clone , Copy , Serialize , PartialEq , Eq ) ]
6161#[ serde( rename_all = "snake_case" ) ]
62+ #[ allow( clippy:: enum_variant_names) ] // "Overflow" postfix is load-bearing: serde output matches CLI docs
6263pub enum ViolationKind {
6364 /// Component bbox crosses the viewport edge.
6465 ViewportOverflow ,
@@ -271,7 +272,10 @@ fn check_unwrappable_text(
271272 if let Component :: Text ( t) = component {
272273 let nowrap = matches ! (
273274 t. style. white_space,
274- Some ( rustmotion:: core:: css:: style:: WhiteSpace :: Nowrap | rustmotion:: core:: css:: style:: WhiteSpace :: Pre )
275+ Some (
276+ rustmotion:: core:: css:: style:: WhiteSpace :: Nowrap
277+ | rustmotion:: core:: css:: style:: WhiteSpace :: Pre
278+ )
275279 ) ;
276280 if !nowrap {
277281 return ;
@@ -316,7 +320,11 @@ fn check_auto_scroll(
316320 let font_size = cb. style . font_size_px_or ( 14.0 ) ;
317321 let actual_line_height = cb. style . line_height_for ( font_size) ;
318322 let line_count = cb. code . lines ( ) . count ( ) . max ( 1 ) as f32 ;
319- let chrome_h = if cb. chrome . as_ref ( ) . is_some_and ( |c| c. enabled ) { 36.0 } else { 0.0 } ;
323+ let chrome_h = if cb. chrome . as_ref ( ) . is_some_and ( |c| c. enabled ) {
324+ 36.0
325+ } else {
326+ 0.0
327+ } ;
320328 let pad = 32.0 ; // ~16 top + 16 bottom default
321329 let natural_h = chrome_h + pad + line_count * actual_line_height;
322330 if natural_h > bbox. h + 0.5 {
@@ -587,15 +595,25 @@ fn resolve_props_for_validation(
587595 return AnimatedProperties :: default ( ) ;
588596 }
589597 }
590- let base = if let Some ( start) = start_at { time - start } else { time } ;
598+ let base = if let Some ( start) = start_at {
599+ time - start
600+ } else {
601+ time
602+ } ;
591603 base. max ( 0.0 )
592604 } else {
593605 time
594606 } ;
595607
596608 let mut props = AnimatedProperties :: default ( ) ;
597609 for ( preset, preset_config) in & extracted. presets {
598- let p = resolve_animations ( & [ ] , Some ( preset) , Some ( preset_config) , anim_time, scene_duration) ;
610+ let p = resolve_animations (
611+ & [ ] ,
612+ Some ( preset) ,
613+ Some ( preset_config) ,
614+ anim_time,
615+ scene_duration,
616+ ) ;
599617 props. merge ( & p) ;
600618 }
601619 if !extracted. keyframes . is_empty ( ) {
@@ -671,6 +689,37 @@ fn hint_for_animated(
671689 }
672690}
673691
692+ /// Render a violation for human consumption (multi-line, color-free).
693+ pub fn format_violation ( v : & GeometryViolation ) -> String {
694+ let axis_str = match v. axis {
695+ Axis :: X => "x" ,
696+ Axis :: Y => "y" ,
697+ Axis :: Both => "x+y" ,
698+ } ;
699+ let kind_str = match v. kind {
700+ ViolationKind :: ViewportOverflow => "viewport overflow" ,
701+ ViolationKind :: UnwrappableTextOverflow => "wrap=false but text too wide" ,
702+ ViolationKind :: AutoScrollDisabledOverflow => "auto_scroll=false but content too tall" ,
703+ ViolationKind :: AnimatedTextOverflow => "animation pushes content outside viewport" ,
704+ } ;
705+ format ! (
706+ "ERROR: {} ({})\n view: {}, scene: {}\n path: {}\n bbox: [{:.0}, {:.0}] -> [{:.0}, {:.0}] (viewport: {}x{})\n axis: {}\n hint: {}" ,
707+ v. component,
708+ kind_str,
709+ v. view_index,
710+ v. scene_index,
711+ v. path,
712+ v. bbox. x,
713+ v. bbox. y,
714+ v. bbox. x + v. bbox. w,
715+ v. bbox. y + v. bbox. h,
716+ v. viewport. 0 ,
717+ v. viewport. 1 ,
718+ axis_str,
719+ v. hint,
720+ )
721+ }
722+
674723#[ cfg( test) ]
675724mod tests {
676725 use super :: * ;
@@ -698,7 +747,11 @@ mod tests {
698747 }"## ;
699748 let scenario = parse ( json) ;
700749 let violations = validate_geometry ( & scenario) ;
701- assert ! ( violations. is_empty( ) , "expected clean, got: {:?}" , violations) ;
750+ assert ! (
751+ violations. is_empty( ) ,
752+ "expected clean, got: {:?}" ,
753+ violations
754+ ) ;
702755 }
703756
704757 #[ test]
@@ -721,12 +774,27 @@ mod tests {
721774 }"## ;
722775 let scenario = parse ( json) ;
723776 let violations = validate_geometry ( & scenario) ;
724- let viewport = violations. iter ( ) . find ( |v| v. kind == ViolationKind :: ViewportOverflow ) ;
725- assert ! ( viewport. is_some( ) , "missing viewport violation in {:?}" , violations) ;
777+ let viewport = violations
778+ . iter ( )
779+ . find ( |v| v. kind == ViolationKind :: ViewportOverflow ) ;
780+ assert ! (
781+ viewport. is_some( ) ,
782+ "missing viewport violation in {:?}" ,
783+ violations
784+ ) ;
726785 let v = viewport. unwrap ( ) ;
727- assert_eq ! ( v. axis, Axis :: X , "expected X-axis overflow, got {:?}" , v. axis) ;
786+ assert_eq ! (
787+ v. axis,
788+ Axis :: X ,
789+ "expected X-axis overflow, got {:?}" ,
790+ v. axis
791+ ) ;
728792 assert_eq ! ( v. component, "shape" ) ;
729- assert ! ( v. path. contains( "children[0]" ) , "path missing index: {}" , v. path) ;
793+ assert ! (
794+ v. path. contains( "children[0]" ) ,
795+ "path missing index: {}" ,
796+ v. path
797+ ) ;
730798 }
731799
732800 #[ test]
@@ -751,8 +819,14 @@ mod tests {
751819 }"## ;
752820 let scenario = parse ( json) ;
753821 let violations = validate_geometry ( & scenario) ;
754- let v = violations. iter ( ) . find ( |v| v. kind == ViolationKind :: UnwrappableTextOverflow ) ;
755- assert ! ( v. is_some( ) , "missing UnwrappableTextOverflow in {:?}" , violations) ;
822+ let v = violations
823+ . iter ( )
824+ . find ( |v| v. kind == ViolationKind :: UnwrappableTextOverflow ) ;
825+ assert ! (
826+ v. is_some( ) ,
827+ "missing UnwrappableTextOverflow in {:?}" ,
828+ violations
829+ ) ;
756830 let v = v. unwrap ( ) ;
757831 assert_eq ! ( v. component, "text" ) ;
758832 assert_eq ! ( v. axis, Axis :: X ) ;
@@ -801,39 +875,14 @@ mod tests {
801875 }"## ;
802876 let scenario = parse ( json) ;
803877 let violations = validate_geometry ( & scenario) ;
804- let v = violations. iter ( ) . find ( |v| v. kind == ViolationKind :: AutoScrollDisabledOverflow ) ;
805- assert ! ( v. is_some( ) , "missing AutoScrollDisabledOverflow in {:?}" , violations) ;
878+ let v = violations
879+ . iter ( )
880+ . find ( |v| v. kind == ViolationKind :: AutoScrollDisabledOverflow ) ;
881+ assert ! (
882+ v. is_some( ) ,
883+ "missing AutoScrollDisabledOverflow in {:?}" ,
884+ violations
885+ ) ;
806886 assert_eq ! ( v. unwrap( ) . component, "codeblock" ) ;
807887 }
808888}
809-
810- /// Render a violation for human consumption (multi-line, color-free).
811- pub fn format_violation ( v : & GeometryViolation ) -> String {
812- let axis_str = match v. axis {
813- Axis :: X => "x" ,
814- Axis :: Y => "y" ,
815- Axis :: Both => "x+y" ,
816- } ;
817- let kind_str = match v. kind {
818- ViolationKind :: ViewportOverflow => "viewport overflow" ,
819- ViolationKind :: UnwrappableTextOverflow => "wrap=false but text too wide" ,
820- ViolationKind :: AutoScrollDisabledOverflow => "auto_scroll=false but content too tall" ,
821- ViolationKind :: AnimatedTextOverflow => "animation pushes content outside viewport" ,
822- } ;
823- format ! (
824- "ERROR: {} ({})\n view: {}, scene: {}\n path: {}\n bbox: [{:.0}, {:.0}] -> [{:.0}, {:.0}] (viewport: {}x{})\n axis: {}\n hint: {}" ,
825- v. component,
826- kind_str,
827- v. view_index,
828- v. scene_index,
829- v. path,
830- v. bbox. x,
831- v. bbox. y,
832- v. bbox. x + v. bbox. w,
833- v. bbox. y + v. bbox. h,
834- v. viewport. 0 ,
835- v. viewport. 1 ,
836- axis_str,
837- v. hint,
838- )
839- }
0 commit comments