Small library for generating claude-code like unicde block mascots, and providing animations when they do stuff
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add --leftslope and --rightslope for diagonal arms

Each column of the arm going outward from the body steps this many
rows up (positive) or down (negative). Creates diagonal arm lines
for expressive poses like reaching up, drooping, or shrugging.
Animatable with --anim leftslope:-1:1 etc.

goose.art b4c98f6a 302b093a

verified
+51 -6
+15 -1
src/animation.rs
··· 25 25 Round, 26 26 LeftArm, 27 27 RightArm, 28 + LeftSlope, 29 + RightSlope, 28 30 Height, 29 31 Width, 30 32 ArmSize, ··· 46 48 "round" => Ok(Self::Round), 47 49 "leftarm" => Ok(Self::LeftArm), 48 50 "rightarm" => Ok(Self::RightArm), 51 + "leftslope" => Ok(Self::LeftSlope), 52 + "rightslope" => Ok(Self::RightSlope), 49 53 "height" => Ok(Self::Height), 50 54 "width" => Ok(Self::Width), 51 55 "armsize" => Ok(Self::ArmSize), ··· 54 58 _ => Err(format!( 55 59 "unknown parameter '{}'. Valid: mood, glance, lefteye, righteye, \ 56 60 lefteyeclosed, righteyeclosed, round, leftarm, rightarm, \ 57 - height, width, armsize, legsize, numlegs", 61 + leftslope, rightslope, height, width, armsize, legsize, numlegs", 58 62 s 59 63 )), 60 64 } ··· 73 77 Self::Round => "round", 74 78 Self::LeftArm => "leftarm", 75 79 Self::RightArm => "rightarm", 80 + Self::LeftSlope => "leftslope", 81 + Self::RightSlope => "rightslope", 76 82 Self::Height => "height", 77 83 Self::Width => "width", 78 84 Self::ArmSize => "armsize", ··· 165 171 round: i32, 166 172 left_arm_offset: i32, 167 173 right_arm_offset: i32, 174 + left_arm_slope: i32, 175 + right_arm_slope: i32, 168 176 height: usize, 169 177 width: usize, 170 178 arm_size: usize, ··· 185 193 round: c.round as i32, 186 194 left_arm_offset: c.left_arm_offset, 187 195 right_arm_offset: c.right_arm_offset, 196 + left_arm_slope: c.left_arm_slope, 197 + right_arm_slope: c.right_arm_slope, 188 198 height: c.height, 189 199 width: c.width, 190 200 arm_size: c.arm_size, ··· 205 215 AnimParam::Round => self.round = value, 206 216 AnimParam::LeftArm => self.left_arm_offset = value, 207 217 AnimParam::RightArm => self.right_arm_offset = value, 218 + AnimParam::LeftSlope => self.left_arm_slope = value, 219 + AnimParam::RightSlope => self.right_arm_slope = value, 208 220 AnimParam::Height => self.height = value.max(3) as usize, 209 221 AnimParam::Width => self.width = value.max(4) as usize, 210 222 AnimParam::ArmSize => self.arm_size = value.max(0) as usize, ··· 232 244 arm_size: self.arm_size, 233 245 left_arm_offset: self.left_arm_offset, 234 246 right_arm_offset: self.right_arm_offset, 247 + left_arm_slope: self.left_arm_slope, 248 + right_arm_slope: self.right_arm_slope, 235 249 num_legs: self.num_legs, 236 250 leg_size: self.leg_size, 237 251 body_color: base.body_color,
+26 -5
src/clood.rs
··· 39 39 pub left_arm_offset: i32, 40 40 /// Vertical offset of right arm from default position. Positive = higher. 41 41 pub right_arm_offset: i32, 42 + /// Slope of left arm: each column going outward steps this many rows up (positive) or down (negative). 43 + #[serde(default)] 44 + pub left_arm_slope: i32, 45 + /// Slope of right arm: each column going outward steps this many rows up (positive) or down (negative). 46 + #[serde(default)] 47 + pub right_arm_slope: i32, 42 48 43 49 /// Number of legs (0 for legless, typically even). 44 50 pub num_legs: usize, ··· 109 115 let region = classify_column(col, arm_pad, c.width); 110 116 111 117 match region { 112 - ColumnRegion::LeftArm if row == left_arm_row => { 113 - output.push_str(&body_block); 118 + ColumnRegion::LeftArm => { 119 + // Distance from body: col closest to body = arm_pad-1, farthest = 0 120 + let dist = (arm_pad - 1 - col) as i32; 121 + let arm_row_here = (left_arm_row as i32 - dist * c.left_arm_slope) 122 + .clamp(0, c.height as i32 - 1) as usize; 123 + if row == arm_row_here { 124 + output.push_str(&body_block); 125 + } else { 126 + output.push_str(space); 127 + } 114 128 } 115 - ColumnRegion::RightArm if row == right_arm_row => { 116 - output.push_str(&body_block); 129 + ColumnRegion::RightArm => { 130 + // Distance from body: col closest to body = arm_pad+width, farthest = last 131 + let dist = (col - arm_pad - c.width) as i32; 132 + let arm_row_here = (right_arm_row as i32 - dist * c.right_arm_slope) 133 + .clamp(0, c.height as i32 - 1) as usize; 134 + if row == arm_row_here { 135 + output.push_str(&body_block); 136 + } else { 137 + output.push_str(space); 138 + } 117 139 } 118 140 ColumnRegion::Body(body_col) => { 119 141 if body_col < corner_cut || body_col >= c.width - corner_cut { ··· 126 148 output.push_str(&body_block); 127 149 } 128 150 } 129 - _ => output.push_str(space), 130 151 } 131 152 } 132 153 output.push('\n');
+10
src/main.rs
··· 117 117 #[arg(long, default_value_t = 0, allow_hyphen_values = true)] 118 118 rightarm: i32, 119 119 120 + /// Left arm slope: each column outward steps this many rows up (+) or down (-). [default: 0] 121 + #[arg(long, default_value_t = 0, allow_hyphen_values = true)] 122 + leftslope: i32, 123 + 124 + /// Right arm slope: each column outward steps this many rows up (+) or down (-). [default: 0] 125 + #[arg(long, default_value_t = 0, allow_hyphen_values = true)] 126 + rightslope: i32, 127 + 120 128 /// Animate a parameter: <param>:<min>:<max> (ping-pong loop). Repeatable. 121 129 #[arg(long = "anim", value_name = "PARAM:MIN:MAX", action = clap::ArgAction::Append)] 122 130 anims: Vec<String>, ··· 307 315 arm_size: args.armsize.unwrap_or_else(|| rng.gen_range(2..=4)), 308 316 left_arm_offset: args.leftarm, 309 317 right_arm_offset: args.rightarm, 318 + left_arm_slope: args.leftslope, 319 + right_arm_slope: args.rightslope, 310 320 num_legs: args.numlegs.unwrap_or_else(|| rng.gen_range(0..=4) * 2), 311 321 leg_size: args.legsize.unwrap_or_else(|| rng.gen_range(2..=4)), 312 322 body_color: args