╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Title.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
64 65 const Glyph = [9][]const u8; 66 67 fn glyphFor(c: u8) ?Glyph { 68 return switch (std.ascii.toUpper(c)) { 69 ' ' => .{ 70 "......", 71 "......", 72 "......", 73 "......", 74 "......", 75 "......", 76 "......", 77 "......", 78 "......", 79 }, 80 'A' => .{ 81 "..##..", 82 ".####.", 83 ".#..#.", 84 "##..##", 85 "######", 86 "#....#", 87 "#....#", 88 "#....#", 89 "#....#", 90 }, 91 'B' => .{ 92 "#####.", 93 "#....#", 94 "#....#", 95 "#....#", 96 "#####.", 97 "#....#", 98 "#....#", 99 "#....#", 100 "#####.", 101 }, 102 'C' => .{ 103 ".####.", 104 "#....#", 105 "#.....", 106 "#.....", 107 "#.....", 108 "#.....", 109 "#.....", 110 "#....#", 111 ".####.", 112 }, 113 'D' => .{ 114 "#####.", 115 "#....#", 116 "#....#", 117 "#....#", 118 "#....#", 119 "#....#", 120 "#....#", 121 "#....#", 122 "#####.", 123 }, 124 'E' => .{ 125 "######", 126 "#.....", 127 "#.....", 128 "#.....", 129 "#####.", 130 "#.....", 131 "#.....", 132 "#.....", 133 "######", 134 }, 135 'F' => .{ 136 "######", 137 "#.....", 138 "#.....", 139 "#.....", 140 "#####.", 141 "#.....", 142 "#.....", 143 "#.....", 144 "#.....", 145 }, 146 'G' => .{ 147 ".####.", 148 "#....#", 149 "#.....", 150 "#.....", 151 "#..###", 152 "#....#", 153 "#....#", 154 "#....#", 155 ".####.", 156 }, 157 'H' => .{ 158 "#....#", 159 "#....#", 160 "#....#", 161 "#....#", 162 "######", 163 "#....#", 164 "#....#", 165 "#....#", 166 "#....#", 167 }, 168 'I' => .{ 169 "######", 170 "..##..", 171 "..##..", 172 "..##..", 173 "..##..", 174 "..##..", 175 "..##..", 176 "..##..", 177 "######", 178 }, 179 'J' => .{ 180 "....##", 181 "....##", 182 "....##", 183 "....##", 184 "....##", 185 "....##", 186 "#...##", 187 "#..##.", 188 ".##...", 189 }, 190 'K' => .{ 191 "#....#", 192 "#...#.", 193 "#..#..", 194 "#.#...", 195 "##....", 196 "#.#...", 197 "#..#..", 198 "#...#.", 199 "#....#", 200 }, 201 'L' => .{ 202 "#.....", 203 "#.....", 204 "#.....", 205 "#.....", 206 "#.....", 207 "#.....", 208 "#.....", 209 "#.....", 210 "######", 211 }, 212 'M' => .{ 213 "#....#", 214 "##..##", 215 "#.##.#", 216 "#.##.#", 217 "#....#", 218 "#....#", 219 "#....#", 220 "#....#", 221 "#....#", 222 }, 223 'N' => .{ 224 "#....#", 225 "##...#", 226 "##...#", 227 "#.#..#", 228 "#..#.#", 229 "#...##", 230 "#...##", 231 "#....#", 232 "#....#", 233 }, 234 'O' => .{ 235 ".####.", 236 "#....#", 237 "#....#", 238 "#....#", 239 "#....#", 240 "#....#", 241 "#....#", 242 "#....#", 243 ".####.", 244 }, 245 'P' => .{ 246 "#####.", 247 "#....#", 248 "#....#", 249 "#....#", 250 "#####.", 251 "#.....", 252 "#.....", 253 "#.....", 254 "#.....", 255 }, 256 'Q' => .{ 257 ".####.", 258 "#....#", 259 "#....#", 260 "#....#", 261 "#....#", 262 "#....#", 263 "#..#.#", 264 ".####.", 265 ".....#", 266 }, 267 'R' => .{ 268 "#####.", 269 "#....#", 270 "#....#", 271 "#....#", 272 "#####.", 273 "#.#...", 274 "#..#..", 275 "#...#.", 276 "#....#", 277 }, 278 'S' => .{ 279 ".####.", 280 "#....#", 281 "#.....", 282 "#.....", 283 ".####.", 284 ".....#", 285 ".....#", 286 "#....#", 287 ".####.", 288 }, 289 'T' => .{ 290 "######", 291 "..##..", 292 "..##..", 293 "..##..", 294 "..##..", 295 "..##..", 296 "..##..", 297 "..##..", 298 "..##..", 299 }, 300 'U' => .{ 301 "#....#", 302 "#....#", 303 "#....#", 304 "#....#", 305 "#....#", 306 "#....#", 307 "#....#", 308 "#....#", 309 ".####.", 310 }, 311 'V' => .{ 312 "#....#", 313 "#....#", 314 "#....#", 315 ".#..#.", 316 ".#..#.", 317 ".#..#.", 318 ".#..#.", 319 ".#..#.", 320 "..##..", 321 }, 322 'W' => .{ 323 "#....#", 324 "#....#", 325 "#....#", 326 "#....#", 327 "#....#", 328 "#.##.#", 329 "#.##.#", 330 "##..##", 331 ".#..#.", 332 }, 333 'X' => .{ 334 "#....#", 335 ".#..#.", 336 ".#..#.", 337 "..##..", 338 "..##..", 339 "..##..", 340 ".#..#.", 341 ".#..#.", 342 "#....#", 343 }, 344 'Y' => .{ 345 "#....#", 346 ".#..#.", 347 ".#..#.", 348 "..##..", 349 "..##..", 350 "..##..", 351 "..##..", 352 "..##..", 353 "..##..", 354 }, 355 'Z' => .{ 356 "######", 357 "....#.", 358 "....#.", 359 "...#..", 360 "..##..", 361 "..#...", 362 ".#....", 363 ".#....", 364 "######", 365 }, 366 '0' => .{ 367 ".####.", 368 "#...##", 369 "#..#.#", 370 "#..#.#", 371 "#.#..#", 372 "#.#..#", 373 "##...#", 374 "#....#", 375 ".####.", 376 }, 377 '1' => .{ 378 "..##..", 379 ".###..", 380 "..##..", 381 "..##..", 382 "..##..", 383 "..##..", 384 "..##..", 385 "..##..", 386 "######", 387 }, 388 '2' => .{ 389 ".####.", 390 "#....#", 391 ".....#", 392 "....#.", 393 "...#..", 394 "..#...", 395 ".#....", 396 "#.....", 397 "######", 398 }, 399 '3' => .{ 400 ".####.", 401 "#....#", 402 ".....#", 403 ".....#", 404 ".####.", 405 ".....#", 406 ".....#", 407 "#....#", 408 ".####.", 409 }, 410 '4' => .{ 411 "....#.", 412 "...##.", 413 "..#.#.", 414 ".#..#.", 415 "#...#.", 416 "######", 417 "....#.", 418 "....#.", 419 "....#.", 420 }, 421 '5' => .{ 422 "######", 423 "#.....", 424 "#.....", 425 "#.....", 426 "#####.", 427 ".....#", 428 ".....#", 429 "#....#", 430 ".####.", 431 }, 432 '6' => .{ 433 ".####.", 434 "#....#", 435 "#.....", 436 "#.....", 437 "#####.", 438 "#....#", 439 "#....#", 440 "#....#", 441 ".####.", 442 }, 443 '7' => .{ 444 "######", 445 ".....#", 446 "....#.", 447 "....#.", 448 "...#..", 449 "...#..", 450 "..#...", 451 "..#...", 452 ".#....", 453 }, 454 '8' => .{ 455 ".####.", 456 "#....#", 457 "#....#", 458 "#....#", 459 ".####.", 460 "#....#", 461 "#....#", 462 "#....#", 463 ".####.", 464 }, 465 '9' => .{ 466 ".####.", 467 "#....#", 468 "#....#", 469 "#....#", 470 ".#####", 471 ".....#", 472 ".....#", 473 "#....#", 474 ".####.", 475 }, 476 '-' => .{ 477 "......", 478 "......", 479 "......", 480 "......", 481 ".####.", 482 "......", 483 "......", 484 "......", 485 "......", 486 }, 487 '.' => .{ 488 "......", 489 "......", 490 "......", 491 "......", 492 "......", 493 "......", 494 "......", 495 "..##..", 496 "..##..", 497 }, 498 else => null, 499 }; 500 } 501 502 // map a 2x3 sub-pixel pattern to its corresponding sextant codepoint 503 fn sextantCodepoint(pattern: u6) u21 { 504 return switch (pattern) { 505 0b000000 => ' ', 506 0b010101 => 0x258C, // ▌ left half block 507 0b101010 => 0x2590, // ▐ right half block 508 0b111111 => 0x2588, // █ full block 509 else => blk: { 510 var offset: u21 = @as(u21, pattern) - 1; 511 if (pattern > 0b010101) offset -= 1; 512 if (pattern > 0b101010) offset -= 1; 513 break :blk 0x1FB00 + offset; 514 }, 515 }; 516 } 517 518 fn renderTitle(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { 519 var rows: [3]std.ArrayList(u8) = .{ .empty, .empty, .empty }; 520 defer for (&rows) |*r| r.deinit(allocator); 521 522 var first = true; 523 for (input) |c| { 524 const glyph = glyphFor(c) orelse continue; 525 526 // row-aligned dither: keep on-pixels only at even bitmap rows. 527 var kept: [9][6]bool = std.mem.zeroes([9][6]bool); 528 for (0..9) |r| { 529 if (r % 2 != 0) continue; 530 for (0..6) |col| { 531 kept[r][col] = glyph[r][col] == '#'; 532 } 533 } 534 535 if (!first) { 536 for (&rows) |*r| try r.append(allocator, ' '); 537 } 538 first = false; 539 540 for (0..3) |text_row| { 541 for (0..3) |text_col| { 542 var pattern: u6 = 0; 543 for (0..3) |sub_row| { 544 for (0..2) |sub_col| { 545 const row_idx = text_row * 3 + sub_row; 546 const col_idx = text_col * 2 + sub_col; 547 if (kept[row_idx][col_idx]) { 548 pattern |= @as(u6, 1) << @intCast(sub_row * 2 + sub_col); 549 } 550 } 551 } 552 const cp = sextantCodepoint(pattern); 553 var utf8_buf: [4]u8 = undefined; 554 const len = try std.unicode.utf8Encode(cp, &utf8_buf); 555 try rows[text_row].appendSlice(allocator, utf8_buf[0..len]); 556 } 557 } 558 } 559 560 var out: std.ArrayList(u8) = .empty; 561 errdefer out.deinit(allocator); 562 try out.appendSlice(allocator, rows[0].items); 563 try out.append(allocator, '\n'); 564 try out.appendSlice(allocator, rows[1].items); 565 try out.append(allocator, '\n'); 566 try out.appendSlice(allocator, rows[2].items); 567 return out.toOwnedSlice(allocator); 568 } 569