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 --glance parameter to shift both eyes horizontally

Positive = glance right, negative = left. Animatable with
--anim glance:-2:2 for a shifty-eyed look. Eyes stay clamped
within the body bounds.

goose.art 302b093a 51ab52dc

verified
+22 -4
+8 -1
src/animation.rs
··· 17 17 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 18 18 pub enum AnimParam { 19 19 Mood, 20 + Glance, 20 21 LeftEye, 21 22 RightEye, 22 23 LeftEyeClosed, ··· 37 38 fn from_str(s: &str) -> Result<Self, Self::Err> { 38 39 match s { 39 40 "mood" => Ok(Self::Mood), 41 + "glance" => Ok(Self::Glance), 40 42 "lefteye" => Ok(Self::LeftEye), 41 43 "righteye" => Ok(Self::RightEye), 42 44 "lefteyeclosed" => Ok(Self::LeftEyeClosed), ··· 50 52 "legsize" => Ok(Self::LegSize), 51 53 "numlegs" => Ok(Self::NumLegs), 52 54 _ => Err(format!( 53 - "unknown parameter '{}'. Valid: mood, lefteye, righteye, \ 55 + "unknown parameter '{}'. Valid: mood, glance, lefteye, righteye, \ 54 56 lefteyeclosed, righteyeclosed, round, leftarm, rightarm, \ 55 57 height, width, armsize, legsize, numlegs", 56 58 s ··· 63 65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 64 66 let name = match self { 65 67 Self::Mood => "mood", 68 + Self::Glance => "glance", 66 69 Self::LeftEye => "lefteye", 67 70 Self::RightEye => "righteye", 68 71 Self::LeftEyeClosed => "lefteyeclosed", ··· 154 157 /// Starts from base values, then animations override individual fields. 155 158 struct FrameState { 156 159 mood: i32, 160 + glance: i32, 157 161 left_eye_offset: i32, 158 162 right_eye_offset: i32, 159 163 left_eye_closed: i32, ··· 173 177 fn from_clood(c: &Clood) -> Self { 174 178 FrameState { 175 179 mood: c.mood, 180 + glance: c.glance, 176 181 left_eye_offset: c.left_eye.offset, 177 182 right_eye_offset: c.right_eye.offset, 178 183 left_eye_closed: if c.left_eye.closed { 1 } else { 0 }, ··· 192 197 fn apply(&mut self, param: AnimParam, value: i32) { 193 198 match param { 194 199 AnimParam::Mood => self.mood = value, 200 + AnimParam::Glance => self.glance = value, 195 201 AnimParam::LeftEye => self.left_eye_offset = value, 196 202 AnimParam::RightEye => self.right_eye_offset = value, 197 203 AnimParam::LeftEyeClosed => self.left_eye_closed = value, ··· 214 220 height: self.height, 215 221 round: self.round.max(0) as usize, 216 222 mood: self.mood, 223 + glance: self.glance, 217 224 left_eye: EyeState { 218 225 offset: self.left_eye_offset, 219 226 closed: self.left_eye_closed != 0,
+9 -3
src/clood.rs
··· 27 27 28 28 /// Eye mood baseline offset from center. Positive = eyes higher. 29 29 pub mood: i32, 30 + /// Horizontal offset for both eyes. Positive = glance right, negative = left. 31 + #[serde(default)] 32 + pub glance: i32, 30 33 pub left_eye: EyeState, 31 34 pub right_eye: EyeState, 32 35 ··· 80 83 let left_eye_row = clamp_to_body(eye_baseline - c.left_eye.offset, c.height); 81 84 let right_eye_row = clamp_to_body(eye_baseline - c.right_eye.offset, c.height); 82 85 83 - // Eyes sit at ~1/3 and ~2/3 across the body width. 84 - let left_eye_col = c.width / 3; 85 - let right_eye_col = c.width - 1 - (c.width / 3); 86 + // Eyes sit at ~1/3 and ~2/3 across the body width, shifted by glance. 87 + // Clamped to stay within the body after corner rounding. 88 + let base_left_col = c.width as i32 / 3 + c.glance; 89 + let base_right_col = (c.width as i32 - 1 - c.width as i32 / 3) + c.glance; 90 + let left_eye_col = base_left_col.clamp(1, c.width as i32 - 2) as usize; 91 + let right_eye_col = base_right_col.clamp(1, c.width as i32 - 2) as usize; 86 92 87 93 // ── Arm positions ─────────────────────────────────────────────────── 88 94 // Arms default to one row below the lower eye.
+5
src/main.rs
··· 85 85 #[arg(long, allow_hyphen_values = true)] 86 86 mood: Option<i32>, 87 87 88 + /// Horizontal eye offset. Positive = glance right, negative = left. [default: 0] 89 + #[arg(long, default_value_t = 0, allow_hyphen_values = true)] 90 + glance: i32, 91 + 88 92 /// Left eye vertical offset from mood baseline [default: 0] 89 93 #[arg(long, default_value_t = 0, allow_hyphen_values = true)] 90 94 lefteye: i32, ··· 291 295 height: args.height.unwrap_or_else(|| rng.gen_range(4..=8)), 292 296 round: args.round, 293 297 mood: args.mood.unwrap_or_else(|| rng.gen_range(-2..=2)), 298 + glance: args.glance, 294 299 left_eye: EyeState { 295 300 offset: args.lefteye, 296 301 closed: args.lefteyeclosed != 0,