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

Configure Feed

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

feat: add round parameter and per-eye closed state

- --round <n>: cuts n blocks from top corners (0 to width/2),
tapering by 1 per row for a dome/rounded head look
- --lefteyeclosed / --righteyeclosed: draws a half block (▄)
instead of full block for a closed/squinting eye
- Both are animatable: --anim round:0:3 or --anim lefteyeclosed:0:1
(blink animation)

+68 -4
+68 -4
src/main.rs
··· 53 53 #[arg(long, default_value_t = 0, allow_hyphen_values = true)] 54 54 righteye: i32, 55 55 56 + /// Left eye closed (0=open, 1=closed, draws a half block). Animatable. [default: 0] 57 + #[arg(long, default_value_t = 0)] 58 + lefteyeclosed: i32, 59 + 60 + /// Right eye closed (0=open, 1=closed, draws a half block). Animatable. [default: 0] 61 + #[arg(long, default_value_t = 0)] 62 + righteyeclosed: i32, 63 + 64 + /// Corner rounding amount (0 to width/2). Cuts blocks from top corners. [default: 0] 65 + #[arg(long, default_value_t = 0)] 66 + round: usize, 67 + 56 68 /// Vertical offset of left arm from default (one row below eyes). Positive = higher, negative = lower. [default: 0] 57 69 #[arg(long, default_value_t = 0, allow_hyphen_values = true)] 58 70 leftarm: i32, ··· 124 136 mood: i32, 125 137 lefteye: i32, 126 138 righteye: i32, 139 + lefteyeclosed: bool, 140 + righteyeclosed: bool, 141 + round: usize, 127 142 leftarm: i32, 128 143 rightarm: i32, 129 144 body_color: (u8, u8, u8), ··· 174 189 fn render(p: &CloodParams) -> String { 175 190 let width = p.width.max(4); 176 191 let height = p.height.max(3); 192 + let round = p.round.min(width / 2); 177 193 178 194 let body_block = colored_block(p.body_color.0, p.body_color.1, p.body_color.2); 179 195 let eye_block = colored_block(p.eye_color.0, p.eye_color.1, p.eye_color.2); 196 + // Closed eye: lower half block (▄) — looks like a squinting/shut eye 197 + let left_eye_closed_block = format!( 198 + "\x1b[38;2;{};{};{}m\u{2584}\x1b[0m", 199 + p.eye_color.0, p.eye_color.1, p.eye_color.2 200 + ); 201 + let right_eye_closed_block = format!( 202 + "\x1b[38;2;{};{};{}m\u{2584}\x1b[0m", 203 + p.eye_color.0, p.eye_color.1, p.eye_color.2 204 + ); 180 205 let space = " "; 181 206 182 207 let baseline_row = (height as i32) / 2; ··· 195 220 let arm_pad = p.armsize; 196 221 let total_width_chars = arm_pad + width + arm_pad; 197 222 223 + // Precompute rounding: for each row, how many cols are cut from each side 224 + // Row 0 gets `round` cut, row 1 gets `round-1`, etc. 225 + let round_cut = |row: usize| -> usize { 226 + if row < round { 227 + round - row 228 + } else { 229 + 0 230 + } 231 + }; 232 + 198 233 let mut output = String::new(); 199 234 200 235 for row in 0..height { 236 + let cut = round_cut(row); 237 + 201 238 for col in 0..total_width_chars { 202 239 let in_left_arm = col < arm_pad; 203 240 let in_right_arm = col >= arm_pad + width; ··· 205 242 206 243 if in_body { 207 244 let body_col = col - arm_pad; 208 - if (row == left_eye_row && body_col == eye_left_col) 209 - || (row == right_eye_row && body_col == eye_right_col) 210 - { 211 - output.push_str(&eye_block); 245 + 246 + // Check if this column is cut by rounding 247 + if body_col < cut || body_col >= width - cut { 248 + output.push_str(space); 249 + } else if row == left_eye_row && body_col == eye_left_col { 250 + if p.lefteyeclosed { 251 + output.push_str(&left_eye_closed_block); 252 + } else { 253 + output.push_str(&eye_block); 254 + } 255 + } else if row == right_eye_row && body_col == eye_right_col { 256 + if p.righteyeclosed { 257 + output.push_str(&right_eye_closed_block); 258 + } else { 259 + output.push_str(&eye_block); 260 + } 212 261 } else { 213 262 output.push_str(&body_block); 214 263 } ··· 262 311 let base_mood = args.mood.unwrap_or_else(|| rng.gen_range(-2..=2)); 263 312 let base_lefteye = args.lefteye; 264 313 let base_righteye = args.righteye; 314 + let base_lefteyeclosed = args.lefteyeclosed; 315 + let base_righteyeclosed = args.righteyeclosed; 316 + let base_round = args.round; 265 317 let base_leftarm = args.leftarm; 266 318 let base_rightarm = args.rightarm; 267 319 ··· 296 348 mood: base_mood, 297 349 lefteye: base_lefteye, 298 350 righteye: base_righteye, 351 + lefteyeclosed: base_lefteyeclosed != 0, 352 + righteyeclosed: base_righteyeclosed != 0, 353 + round: base_round, 299 354 leftarm: base_leftarm, 300 355 rightarm: base_rightarm, 301 356 body_color, ··· 326 381 let mut mood = base_mood; 327 382 let mut lefteye = base_lefteye; 328 383 let mut righteye = base_righteye; 384 + let mut lefteyeclosed = base_lefteyeclosed; 385 + let mut righteyeclosed = base_righteyeclosed; 386 + let mut round = base_round as i32; 329 387 let mut leftarm = base_leftarm; 330 388 let mut rightarm = base_rightarm; 331 389 let mut height = base_height; ··· 340 398 "mood" => mood = v, 341 399 "lefteye" => lefteye = v, 342 400 "righteye" => righteye = v, 401 + "lefteyeclosed" => lefteyeclosed = v, 402 + "righteyeclosed" => righteyeclosed = v, 403 + "round" => round = v, 343 404 "leftarm" => leftarm = v, 344 405 "rightarm" => rightarm = v, 345 406 "height" => height = v.max(3) as usize, ··· 360 421 mood, 361 422 lefteye, 362 423 righteye, 424 + lefteyeclosed: lefteyeclosed != 0, 425 + righteyeclosed: righteyeclosed != 0, 426 + round: round.max(0) as usize, 363 427 leftarm, 364 428 rightarm, 365 429 body_color,