magical markdown slides

feat: basic cli definition build: logging & terminal event setup (manager struct)

+1421 -22
+983
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 + name = "allocator-api2" 7 + version = "0.2.21" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 + 11 + [[package]] 12 + name = "anstream" 13 + version = "0.6.21" 14 + source = "registry+https://github.com/rust-lang/crates.io-index" 15 + checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 16 + dependencies = [ 17 + "anstyle", 18 + "anstyle-parse", 19 + "anstyle-query", 20 + "anstyle-wincon", 21 + "colorchoice", 22 + "is_terminal_polyfill", 23 + "utf8parse", 24 + ] 25 + 26 + [[package]] 27 + name = "anstyle" 28 + version = "1.0.13" 29 + source = "registry+https://github.com/rust-lang/crates.io-index" 30 + checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 31 + 32 + [[package]] 33 + name = "anstyle-parse" 34 + version = "0.2.7" 35 + source = "registry+https://github.com/rust-lang/crates.io-index" 36 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 37 + dependencies = [ 38 + "utf8parse", 39 + ] 40 + 41 + [[package]] 42 + name = "anstyle-query" 43 + version = "1.1.4" 44 + source = "registry+https://github.com/rust-lang/crates.io-index" 45 + checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 46 + dependencies = [ 47 + "windows-sys 0.60.2", 48 + ] 49 + 50 + [[package]] 51 + name = "anstyle-wincon" 52 + version = "3.0.10" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 55 + dependencies = [ 56 + "anstyle", 57 + "once_cell_polyfill", 58 + "windows-sys 0.60.2", 59 + ] 60 + 61 + [[package]] 62 + name = "bitflags" 63 + version = "2.9.4" 64 + source = "registry+https://github.com/rust-lang/crates.io-index" 65 + checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" 66 + 67 + [[package]] 68 + name = "cassowary" 69 + version = "0.3.0" 70 + source = "registry+https://github.com/rust-lang/crates.io-index" 71 + checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 72 + 73 + [[package]] 74 + name = "castaway" 75 + version = "0.2.4" 76 + source = "registry+https://github.com/rust-lang/crates.io-index" 77 + checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" 78 + dependencies = [ 79 + "rustversion", 80 + ] 81 + 82 + [[package]] 83 + name = "cfg-if" 84 + version = "1.0.3" 85 + source = "registry+https://github.com/rust-lang/crates.io-index" 86 + checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 87 + 88 + [[package]] 89 + name = "clap" 90 + version = "4.5.48" 91 + source = "registry+https://github.com/rust-lang/crates.io-index" 92 + checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" 93 + dependencies = [ 94 + "clap_builder", 95 + "clap_derive", 96 + ] 97 + 98 + [[package]] 99 + name = "clap_builder" 100 + version = "4.5.48" 101 + source = "registry+https://github.com/rust-lang/crates.io-index" 102 + checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" 103 + dependencies = [ 104 + "anstream", 105 + "anstyle", 106 + "clap_lex", 107 + "strsim", 108 + ] 109 + 110 + [[package]] 111 + name = "clap_derive" 112 + version = "4.5.47" 113 + source = "registry+https://github.com/rust-lang/crates.io-index" 114 + checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" 115 + dependencies = [ 116 + "heck", 117 + "proc-macro2", 118 + "quote", 119 + "syn", 120 + ] 121 + 122 + [[package]] 123 + name = "clap_lex" 124 + version = "0.7.5" 125 + source = "registry+https://github.com/rust-lang/crates.io-index" 126 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 127 + 128 + [[package]] 129 + name = "colorchoice" 130 + version = "1.0.4" 131 + source = "registry+https://github.com/rust-lang/crates.io-index" 132 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 133 + 134 + [[package]] 135 + name = "compact_str" 136 + version = "0.8.1" 137 + source = "registry+https://github.com/rust-lang/crates.io-index" 138 + checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" 139 + dependencies = [ 140 + "castaway", 141 + "cfg-if", 142 + "itoa", 143 + "rustversion", 144 + "ryu", 145 + "static_assertions", 146 + ] 147 + 148 + [[package]] 149 + name = "convert_case" 150 + version = "0.7.1" 151 + source = "registry+https://github.com/rust-lang/crates.io-index" 152 + checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" 153 + dependencies = [ 154 + "unicode-segmentation", 155 + ] 156 + 157 + [[package]] 158 + name = "crossterm" 159 + version = "0.28.1" 160 + source = "registry+https://github.com/rust-lang/crates.io-index" 161 + checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" 162 + dependencies = [ 163 + "bitflags", 164 + "crossterm_winapi", 165 + "mio", 166 + "parking_lot", 167 + "rustix 0.38.44", 168 + "signal-hook", 169 + "signal-hook-mio", 170 + "winapi", 171 + ] 172 + 173 + [[package]] 174 + name = "crossterm" 175 + version = "0.29.0" 176 + source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" 178 + dependencies = [ 179 + "bitflags", 180 + "crossterm_winapi", 181 + "derive_more", 182 + "document-features", 183 + "mio", 184 + "parking_lot", 185 + "rustix 1.1.2", 186 + "signal-hook", 187 + "signal-hook-mio", 188 + "winapi", 189 + ] 190 + 191 + [[package]] 192 + name = "crossterm_winapi" 193 + version = "0.9.1" 194 + source = "registry+https://github.com/rust-lang/crates.io-index" 195 + checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 196 + dependencies = [ 197 + "winapi", 198 + ] 199 + 200 + [[package]] 201 + name = "darling" 202 + version = "0.20.11" 203 + source = "registry+https://github.com/rust-lang/crates.io-index" 204 + checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 205 + dependencies = [ 206 + "darling_core", 207 + "darling_macro", 208 + ] 209 + 210 + [[package]] 211 + name = "darling_core" 212 + version = "0.20.11" 213 + source = "registry+https://github.com/rust-lang/crates.io-index" 214 + checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 215 + dependencies = [ 216 + "fnv", 217 + "ident_case", 218 + "proc-macro2", 219 + "quote", 220 + "strsim", 221 + "syn", 222 + ] 223 + 224 + [[package]] 225 + name = "darling_macro" 226 + version = "0.20.11" 227 + source = "registry+https://github.com/rust-lang/crates.io-index" 228 + checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 229 + dependencies = [ 230 + "darling_core", 231 + "quote", 232 + "syn", 233 + ] 234 + 235 + [[package]] 236 + name = "derive_more" 237 + version = "2.0.1" 238 + source = "registry+https://github.com/rust-lang/crates.io-index" 239 + checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" 240 + dependencies = [ 241 + "derive_more-impl", 242 + ] 243 + 244 + [[package]] 245 + name = "derive_more-impl" 246 + version = "2.0.1" 247 + source = "registry+https://github.com/rust-lang/crates.io-index" 248 + checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" 249 + dependencies = [ 250 + "convert_case", 251 + "proc-macro2", 252 + "quote", 253 + "syn", 254 + ] 255 + 256 + [[package]] 257 + name = "document-features" 258 + version = "0.2.11" 259 + source = "registry+https://github.com/rust-lang/crates.io-index" 260 + checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" 261 + dependencies = [ 262 + "litrs", 263 + ] 264 + 265 + [[package]] 266 + name = "either" 267 + version = "1.15.0" 268 + source = "registry+https://github.com/rust-lang/crates.io-index" 269 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 270 + 271 + [[package]] 272 + name = "equivalent" 273 + version = "1.0.2" 274 + source = "registry+https://github.com/rust-lang/crates.io-index" 275 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 276 + 277 + [[package]] 278 + name = "errno" 279 + version = "0.3.14" 280 + source = "registry+https://github.com/rust-lang/crates.io-index" 281 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 282 + dependencies = [ 283 + "libc", 284 + "windows-sys 0.60.2", 285 + ] 286 + 287 + [[package]] 288 + name = "fnv" 289 + version = "1.0.7" 290 + source = "registry+https://github.com/rust-lang/crates.io-index" 291 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 292 + 293 + [[package]] 294 + name = "foldhash" 295 + version = "0.1.5" 296 + source = "registry+https://github.com/rust-lang/crates.io-index" 297 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 298 + 299 + [[package]] 300 + name = "hashbrown" 301 + version = "0.15.5" 302 + source = "registry+https://github.com/rust-lang/crates.io-index" 303 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 304 + dependencies = [ 305 + "allocator-api2", 306 + "equivalent", 307 + "foldhash", 308 + ] 309 + 310 + [[package]] 311 + name = "heck" 312 + version = "0.5.0" 313 + source = "registry+https://github.com/rust-lang/crates.io-index" 314 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 315 + 316 + [[package]] 317 + name = "ident_case" 318 + version = "1.0.1" 319 + source = "registry+https://github.com/rust-lang/crates.io-index" 320 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 321 + 322 + [[package]] 323 + name = "indoc" 324 + version = "2.0.6" 325 + source = "registry+https://github.com/rust-lang/crates.io-index" 326 + checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" 327 + 328 + [[package]] 329 + name = "instability" 330 + version = "0.3.9" 331 + source = "registry+https://github.com/rust-lang/crates.io-index" 332 + checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" 333 + dependencies = [ 334 + "darling", 335 + "indoc", 336 + "proc-macro2", 337 + "quote", 338 + "syn", 339 + ] 340 + 341 + [[package]] 342 + name = "is_terminal_polyfill" 343 + version = "1.70.1" 344 + source = "registry+https://github.com/rust-lang/crates.io-index" 345 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 346 + 347 + [[package]] 348 + name = "itertools" 349 + version = "0.13.0" 350 + source = "registry+https://github.com/rust-lang/crates.io-index" 351 + checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 352 + dependencies = [ 353 + "either", 354 + ] 355 + 356 + [[package]] 357 + name = "itoa" 358 + version = "1.0.15" 359 + source = "registry+https://github.com/rust-lang/crates.io-index" 360 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 361 + 362 + [[package]] 363 + name = "lazy_static" 364 + version = "1.5.0" 365 + source = "registry+https://github.com/rust-lang/crates.io-index" 366 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 367 + 368 + [[package]] 369 + name = "libc" 370 + version = "0.2.176" 371 + source = "registry+https://github.com/rust-lang/crates.io-index" 372 + checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" 373 + 374 + [[package]] 375 + name = "linux-raw-sys" 376 + version = "0.4.15" 377 + source = "registry+https://github.com/rust-lang/crates.io-index" 378 + checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 379 + 380 + [[package]] 381 + name = "linux-raw-sys" 382 + version = "0.11.0" 383 + source = "registry+https://github.com/rust-lang/crates.io-index" 384 + checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 385 + 386 + [[package]] 387 + name = "litrs" 388 + version = "0.4.2" 389 + source = "registry+https://github.com/rust-lang/crates.io-index" 390 + checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" 391 + 392 + [[package]] 393 + name = "lock_api" 394 + version = "0.4.14" 395 + source = "registry+https://github.com/rust-lang/crates.io-index" 396 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 397 + dependencies = [ 398 + "scopeguard", 399 + ] 400 + 401 + [[package]] 402 + name = "log" 403 + version = "0.4.28" 404 + source = "registry+https://github.com/rust-lang/crates.io-index" 405 + checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 406 + 407 + [[package]] 408 + name = "lru" 409 + version = "0.12.5" 410 + source = "registry+https://github.com/rust-lang/crates.io-index" 411 + checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 412 + dependencies = [ 413 + "hashbrown", 414 + ] 415 + 416 + [[package]] 417 + name = "mio" 418 + version = "1.0.4" 419 + source = "registry+https://github.com/rust-lang/crates.io-index" 420 + checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 421 + dependencies = [ 422 + "libc", 423 + "log", 424 + "wasi", 425 + "windows-sys 0.59.0", 426 + ] 427 + 428 + [[package]] 429 + name = "nu-ansi-term" 430 + version = "0.50.1" 431 + source = "registry+https://github.com/rust-lang/crates.io-index" 432 + checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" 433 + dependencies = [ 434 + "windows-sys 0.52.0", 435 + ] 436 + 437 + [[package]] 438 + name = "once_cell" 439 + version = "1.21.3" 440 + source = "registry+https://github.com/rust-lang/crates.io-index" 441 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 442 + 443 + [[package]] 444 + name = "once_cell_polyfill" 445 + version = "1.70.1" 446 + source = "registry+https://github.com/rust-lang/crates.io-index" 447 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 448 + 449 + [[package]] 450 + name = "owo-colors" 451 + version = "4.2.3" 452 + source = "registry+https://github.com/rust-lang/crates.io-index" 453 + checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" 454 + 455 + [[package]] 456 + name = "parking_lot" 457 + version = "0.12.5" 458 + source = "registry+https://github.com/rust-lang/crates.io-index" 459 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 460 + dependencies = [ 461 + "lock_api", 462 + "parking_lot_core", 463 + ] 464 + 465 + [[package]] 466 + name = "parking_lot_core" 467 + version = "0.9.12" 468 + source = "registry+https://github.com/rust-lang/crates.io-index" 469 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 470 + dependencies = [ 471 + "cfg-if", 472 + "libc", 473 + "redox_syscall", 474 + "smallvec", 475 + "windows-link", 476 + ] 477 + 478 + [[package]] 479 + name = "paste" 480 + version = "1.0.15" 481 + source = "registry+https://github.com/rust-lang/crates.io-index" 482 + checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 483 + 484 + [[package]] 485 + name = "pin-project-lite" 486 + version = "0.2.16" 487 + source = "registry+https://github.com/rust-lang/crates.io-index" 488 + checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 489 + 490 + [[package]] 491 + name = "proc-macro2" 492 + version = "1.0.101" 493 + source = "registry+https://github.com/rust-lang/crates.io-index" 494 + checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 495 + dependencies = [ 496 + "unicode-ident", 497 + ] 498 + 499 + [[package]] 500 + name = "quote" 501 + version = "1.0.41" 502 + source = "registry+https://github.com/rust-lang/crates.io-index" 503 + checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" 504 + dependencies = [ 505 + "proc-macro2", 506 + ] 507 + 508 + [[package]] 509 + name = "ratatui" 510 + version = "0.29.0" 511 + source = "registry+https://github.com/rust-lang/crates.io-index" 512 + checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" 513 + dependencies = [ 514 + "bitflags", 515 + "cassowary", 516 + "compact_str", 517 + "crossterm 0.28.1", 518 + "indoc", 519 + "instability", 520 + "itertools", 521 + "lru", 522 + "paste", 523 + "strum", 524 + "unicode-segmentation", 525 + "unicode-truncate", 526 + "unicode-width 0.2.0", 527 + ] 528 + 529 + [[package]] 530 + name = "redox_syscall" 531 + version = "0.5.18" 532 + source = "registry+https://github.com/rust-lang/crates.io-index" 533 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 534 + dependencies = [ 535 + "bitflags", 536 + ] 537 + 538 + [[package]] 539 + name = "rustix" 540 + version = "0.38.44" 541 + source = "registry+https://github.com/rust-lang/crates.io-index" 542 + checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 543 + dependencies = [ 544 + "bitflags", 545 + "errno", 546 + "libc", 547 + "linux-raw-sys 0.4.15", 548 + "windows-sys 0.59.0", 549 + ] 550 + 551 + [[package]] 552 + name = "rustix" 553 + version = "1.1.2" 554 + source = "registry+https://github.com/rust-lang/crates.io-index" 555 + checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 556 + dependencies = [ 557 + "bitflags", 558 + "errno", 559 + "libc", 560 + "linux-raw-sys 0.11.0", 561 + "windows-sys 0.60.2", 562 + ] 563 + 564 + [[package]] 565 + name = "rustversion" 566 + version = "1.0.22" 567 + source = "registry+https://github.com/rust-lang/crates.io-index" 568 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 569 + 570 + [[package]] 571 + name = "ryu" 572 + version = "1.0.20" 573 + source = "registry+https://github.com/rust-lang/crates.io-index" 574 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 575 + 576 + [[package]] 577 + name = "scopeguard" 578 + version = "1.2.0" 579 + source = "registry+https://github.com/rust-lang/crates.io-index" 580 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 581 + 582 + [[package]] 583 + name = "sharded-slab" 584 + version = "0.1.7" 585 + source = "registry+https://github.com/rust-lang/crates.io-index" 586 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 587 + dependencies = [ 588 + "lazy_static", 589 + ] 590 + 591 + [[package]] 592 + name = "signal-hook" 593 + version = "0.3.18" 594 + source = "registry+https://github.com/rust-lang/crates.io-index" 595 + checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" 596 + dependencies = [ 597 + "libc", 598 + "signal-hook-registry", 599 + ] 600 + 601 + [[package]] 602 + name = "signal-hook-mio" 603 + version = "0.2.4" 604 + source = "registry+https://github.com/rust-lang/crates.io-index" 605 + checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" 606 + dependencies = [ 607 + "libc", 608 + "mio", 609 + "signal-hook", 610 + ] 611 + 612 + [[package]] 613 + name = "signal-hook-registry" 614 + version = "1.4.6" 615 + source = "registry+https://github.com/rust-lang/crates.io-index" 616 + checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 617 + dependencies = [ 618 + "libc", 619 + ] 620 + 621 + [[package]] 6 622 name = "slides-cli" 7 623 version = "0.1.0" 624 + dependencies = [ 625 + "clap", 626 + "crossterm 0.29.0", 627 + "owo-colors", 628 + "ratatui", 629 + "tracing", 630 + "tracing-subscriber", 631 + ] 8 632 9 633 [[package]] 10 634 name = "slides-core" 11 635 version = "0.1.0" 636 + dependencies = [ 637 + "crossterm 0.29.0", 638 + "owo-colors", 639 + "tracing", 640 + ] 12 641 13 642 [[package]] 14 643 name = "slides-tui" 15 644 version = "0.1.0" 645 + 646 + [[package]] 647 + name = "smallvec" 648 + version = "1.15.1" 649 + source = "registry+https://github.com/rust-lang/crates.io-index" 650 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 651 + 652 + [[package]] 653 + name = "static_assertions" 654 + version = "1.1.0" 655 + source = "registry+https://github.com/rust-lang/crates.io-index" 656 + checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 657 + 658 + [[package]] 659 + name = "strsim" 660 + version = "0.11.1" 661 + source = "registry+https://github.com/rust-lang/crates.io-index" 662 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 663 + 664 + [[package]] 665 + name = "strum" 666 + version = "0.26.3" 667 + source = "registry+https://github.com/rust-lang/crates.io-index" 668 + checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 669 + dependencies = [ 670 + "strum_macros", 671 + ] 672 + 673 + [[package]] 674 + name = "strum_macros" 675 + version = "0.26.4" 676 + source = "registry+https://github.com/rust-lang/crates.io-index" 677 + checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 678 + dependencies = [ 679 + "heck", 680 + "proc-macro2", 681 + "quote", 682 + "rustversion", 683 + "syn", 684 + ] 685 + 686 + [[package]] 687 + name = "syn" 688 + version = "2.0.106" 689 + source = "registry+https://github.com/rust-lang/crates.io-index" 690 + checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 691 + dependencies = [ 692 + "proc-macro2", 693 + "quote", 694 + "unicode-ident", 695 + ] 696 + 697 + [[package]] 698 + name = "thread_local" 699 + version = "1.1.9" 700 + source = "registry+https://github.com/rust-lang/crates.io-index" 701 + checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 702 + dependencies = [ 703 + "cfg-if", 704 + ] 705 + 706 + [[package]] 707 + name = "tracing" 708 + version = "0.1.41" 709 + source = "registry+https://github.com/rust-lang/crates.io-index" 710 + checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 711 + dependencies = [ 712 + "pin-project-lite", 713 + "tracing-attributes", 714 + "tracing-core", 715 + ] 716 + 717 + [[package]] 718 + name = "tracing-attributes" 719 + version = "0.1.30" 720 + source = "registry+https://github.com/rust-lang/crates.io-index" 721 + checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" 722 + dependencies = [ 723 + "proc-macro2", 724 + "quote", 725 + "syn", 726 + ] 727 + 728 + [[package]] 729 + name = "tracing-core" 730 + version = "0.1.34" 731 + source = "registry+https://github.com/rust-lang/crates.io-index" 732 + checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 733 + dependencies = [ 734 + "once_cell", 735 + "valuable", 736 + ] 737 + 738 + [[package]] 739 + name = "tracing-log" 740 + version = "0.2.0" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 743 + dependencies = [ 744 + "log", 745 + "once_cell", 746 + "tracing-core", 747 + ] 748 + 749 + [[package]] 750 + name = "tracing-subscriber" 751 + version = "0.3.20" 752 + source = "registry+https://github.com/rust-lang/crates.io-index" 753 + checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" 754 + dependencies = [ 755 + "nu-ansi-term", 756 + "sharded-slab", 757 + "smallvec", 758 + "thread_local", 759 + "tracing-core", 760 + "tracing-log", 761 + ] 762 + 763 + [[package]] 764 + name = "unicode-ident" 765 + version = "1.0.19" 766 + source = "registry+https://github.com/rust-lang/crates.io-index" 767 + checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" 768 + 769 + [[package]] 770 + name = "unicode-segmentation" 771 + version = "1.12.0" 772 + source = "registry+https://github.com/rust-lang/crates.io-index" 773 + checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 774 + 775 + [[package]] 776 + name = "unicode-truncate" 777 + version = "1.1.0" 778 + source = "registry+https://github.com/rust-lang/crates.io-index" 779 + checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" 780 + dependencies = [ 781 + "itertools", 782 + "unicode-segmentation", 783 + "unicode-width 0.1.14", 784 + ] 785 + 786 + [[package]] 787 + name = "unicode-width" 788 + version = "0.1.14" 789 + source = "registry+https://github.com/rust-lang/crates.io-index" 790 + checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 791 + 792 + [[package]] 793 + name = "unicode-width" 794 + version = "0.2.0" 795 + source = "registry+https://github.com/rust-lang/crates.io-index" 796 + checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 797 + 798 + [[package]] 799 + name = "utf8parse" 800 + version = "0.2.2" 801 + source = "registry+https://github.com/rust-lang/crates.io-index" 802 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 803 + 804 + [[package]] 805 + name = "valuable" 806 + version = "0.1.1" 807 + source = "registry+https://github.com/rust-lang/crates.io-index" 808 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 809 + 810 + [[package]] 811 + name = "wasi" 812 + version = "0.11.1+wasi-snapshot-preview1" 813 + source = "registry+https://github.com/rust-lang/crates.io-index" 814 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 815 + 816 + [[package]] 817 + name = "winapi" 818 + version = "0.3.9" 819 + source = "registry+https://github.com/rust-lang/crates.io-index" 820 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 821 + dependencies = [ 822 + "winapi-i686-pc-windows-gnu", 823 + "winapi-x86_64-pc-windows-gnu", 824 + ] 825 + 826 + [[package]] 827 + name = "winapi-i686-pc-windows-gnu" 828 + version = "0.4.0" 829 + source = "registry+https://github.com/rust-lang/crates.io-index" 830 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 831 + 832 + [[package]] 833 + name = "winapi-x86_64-pc-windows-gnu" 834 + version = "0.4.0" 835 + source = "registry+https://github.com/rust-lang/crates.io-index" 836 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 837 + 838 + [[package]] 839 + name = "windows-link" 840 + version = "0.2.0" 841 + source = "registry+https://github.com/rust-lang/crates.io-index" 842 + checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" 843 + 844 + [[package]] 845 + name = "windows-sys" 846 + version = "0.52.0" 847 + source = "registry+https://github.com/rust-lang/crates.io-index" 848 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 849 + dependencies = [ 850 + "windows-targets 0.52.6", 851 + ] 852 + 853 + [[package]] 854 + name = "windows-sys" 855 + version = "0.59.0" 856 + source = "registry+https://github.com/rust-lang/crates.io-index" 857 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 858 + dependencies = [ 859 + "windows-targets 0.52.6", 860 + ] 861 + 862 + [[package]] 863 + name = "windows-sys" 864 + version = "0.60.2" 865 + source = "registry+https://github.com/rust-lang/crates.io-index" 866 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 867 + dependencies = [ 868 + "windows-targets 0.53.4", 869 + ] 870 + 871 + [[package]] 872 + name = "windows-targets" 873 + version = "0.52.6" 874 + source = "registry+https://github.com/rust-lang/crates.io-index" 875 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 876 + dependencies = [ 877 + "windows_aarch64_gnullvm 0.52.6", 878 + "windows_aarch64_msvc 0.52.6", 879 + "windows_i686_gnu 0.52.6", 880 + "windows_i686_gnullvm 0.52.6", 881 + "windows_i686_msvc 0.52.6", 882 + "windows_x86_64_gnu 0.52.6", 883 + "windows_x86_64_gnullvm 0.52.6", 884 + "windows_x86_64_msvc 0.52.6", 885 + ] 886 + 887 + [[package]] 888 + name = "windows-targets" 889 + version = "0.53.4" 890 + source = "registry+https://github.com/rust-lang/crates.io-index" 891 + checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" 892 + dependencies = [ 893 + "windows-link", 894 + "windows_aarch64_gnullvm 0.53.0", 895 + "windows_aarch64_msvc 0.53.0", 896 + "windows_i686_gnu 0.53.0", 897 + "windows_i686_gnullvm 0.53.0", 898 + "windows_i686_msvc 0.53.0", 899 + "windows_x86_64_gnu 0.53.0", 900 + "windows_x86_64_gnullvm 0.53.0", 901 + "windows_x86_64_msvc 0.53.0", 902 + ] 903 + 904 + [[package]] 905 + name = "windows_aarch64_gnullvm" 906 + version = "0.52.6" 907 + source = "registry+https://github.com/rust-lang/crates.io-index" 908 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 909 + 910 + [[package]] 911 + name = "windows_aarch64_gnullvm" 912 + version = "0.53.0" 913 + source = "registry+https://github.com/rust-lang/crates.io-index" 914 + checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 915 + 916 + [[package]] 917 + name = "windows_aarch64_msvc" 918 + version = "0.52.6" 919 + source = "registry+https://github.com/rust-lang/crates.io-index" 920 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 921 + 922 + [[package]] 923 + name = "windows_aarch64_msvc" 924 + version = "0.53.0" 925 + source = "registry+https://github.com/rust-lang/crates.io-index" 926 + checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 927 + 928 + [[package]] 929 + name = "windows_i686_gnu" 930 + version = "0.52.6" 931 + source = "registry+https://github.com/rust-lang/crates.io-index" 932 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 933 + 934 + [[package]] 935 + name = "windows_i686_gnu" 936 + version = "0.53.0" 937 + source = "registry+https://github.com/rust-lang/crates.io-index" 938 + checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 939 + 940 + [[package]] 941 + name = "windows_i686_gnullvm" 942 + version = "0.52.6" 943 + source = "registry+https://github.com/rust-lang/crates.io-index" 944 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 945 + 946 + [[package]] 947 + name = "windows_i686_gnullvm" 948 + version = "0.53.0" 949 + source = "registry+https://github.com/rust-lang/crates.io-index" 950 + checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 951 + 952 + [[package]] 953 + name = "windows_i686_msvc" 954 + version = "0.52.6" 955 + source = "registry+https://github.com/rust-lang/crates.io-index" 956 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 957 + 958 + [[package]] 959 + name = "windows_i686_msvc" 960 + version = "0.53.0" 961 + source = "registry+https://github.com/rust-lang/crates.io-index" 962 + checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 963 + 964 + [[package]] 965 + name = "windows_x86_64_gnu" 966 + version = "0.52.6" 967 + source = "registry+https://github.com/rust-lang/crates.io-index" 968 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 969 + 970 + [[package]] 971 + name = "windows_x86_64_gnu" 972 + version = "0.53.0" 973 + source = "registry+https://github.com/rust-lang/crates.io-index" 974 + checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 975 + 976 + [[package]] 977 + name = "windows_x86_64_gnullvm" 978 + version = "0.52.6" 979 + source = "registry+https://github.com/rust-lang/crates.io-index" 980 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 981 + 982 + [[package]] 983 + name = "windows_x86_64_gnullvm" 984 + version = "0.53.0" 985 + source = "registry+https://github.com/rust-lang/crates.io-index" 986 + checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 987 + 988 + [[package]] 989 + name = "windows_x86_64_msvc" 990 + version = "0.52.6" 991 + source = "registry+https://github.com/rust-lang/crates.io-index" 992 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 993 + 994 + [[package]] 995 + name = "windows_x86_64_msvc" 996 + version = "0.53.0" 997 + source = "registry+https://github.com/rust-lang/crates.io-index" 998 + checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+7 -7
ROADMAP.md
··· 4 4 5 5 __Objective:__ Establish a clean, testable core with `clap` and a minimal `ratatui` loop. 6 6 7 - | Task | Description | Key Crates | 8 - | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | 9 - | __Project Scaffolding__ | Initialize workspace with `slides-core`, `slides-cli`, and `slides-tui` crates. Use `cargo-generate` and a `justfile` for scripts. | `cargo`, `just`, `clap` | 10 - | __CLI Definition__ | Implement root command `slides` with subcommands:<br>• `present` (TUI)<br>• `print` (stdout)<br>• `init` (scaffold deck)<br>• `check` (lint slides). | [`clap`](https://docs.rs/clap/latest/clap/) | 11 - | __Logging and Colors__ | Integrate structured logs via `tracing`.<br>Use __owo-colors__ for color abstraction (no dynamic dispatch). | [`owo-colors`](https://docs.rs/owo-colors/latest/owo_colors/), `tracing` | 12 - | __Terminal & Event Setup__ | Configure alternate screen, raw mode, input loop, resize handler. | [`crossterm`](https://docs.rs/crossterm/latest/crossterm/), `ratatui` | 13 - | __CI/CD + Tooling__ | Setup `cargo fmt`, `clippy`, `test`, and `cross` matrix CI. | GitHub Actions | 7 + | Task | Description | Key Crates | 8 + | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | 9 + | __✓ Project Scaffolding__ | Initialize workspace with `slides-core`, `slides-cli`, and `slides-ui` crates. Use `cargo-generate` and a `justfile` for scripts. | `cargo`, `just`, `clap` | 10 + | __✓ CLI Definition__ | Implement root command `slides` with subcommands:<br>• `present` (TUI)<br>• `print` (stdout)<br>• `init` (scaffold deck)<br>• `check` (lint slides). | [`clap`](https://docs.rs/clap/latest/clap/) | 11 + | __✓ Logging & Colors__ | Integrate structured logs via `tracing`.<br>Use __owo-colors__ for color abstraction (no dynamic dispatch). | [`owo-colors`](https://docs.rs/owo-colors/latest/owo_colors/), `tracing` | 12 + | __✓ Terminal & Event Setup__ | Configure alternate screen, raw mode, input loop, resize handler. | [`crossterm`](https://docs.rs/crossterm/latest/crossterm/), `ratatui` | 13 + | __CI/CD + Tooling__ | Setup `cargo fmt`, `clippy`, `test`, and `cross` matrix CI. | GitHub Actions | 14 14 15 15 ## Data Model (Parser & Slides) 16 16
+6
cli/Cargo.toml
··· 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + clap = { version = "4.5.48", features = ["derive"] } 8 + crossterm = "0.29.0" 9 + owo-colors = "4.2.3" 10 + ratatui = "0.29.0" 11 + tracing = "0.1.41" 12 + tracing-subscriber = "0.3.20"
+165 -1
cli/src/main.rs
··· 1 + use clap::{Parser, Subcommand}; 2 + use std::path::PathBuf; 3 + use tracing::Level; 4 + 5 + /// A modern terminal-based presentation tool 6 + #[derive(Parser, Debug)] 7 + #[command(name = "slides")] 8 + #[command(version, about, long_about = None)] 9 + struct ArgParser { 10 + /// Set logging level (error, warn, info, debug, trace) 11 + #[arg(short, long, global = true, default_value = "info")] 12 + log_level: Level, 13 + 14 + #[command(subcommand)] 15 + command: Commands, 16 + } 17 + 18 + #[derive(Subcommand, Debug)] 19 + enum Commands { 20 + /// Present slides in interactive TUI mode 21 + Present { 22 + /// Path to the markdown file 23 + file: PathBuf, 24 + 25 + /// Theme to use for presentation 26 + #[arg(short, long)] 27 + theme: Option<String>, 28 + }, 29 + 30 + /// Print slides to stdout with formatting 31 + Print { 32 + /// Path to the markdown file 33 + file: PathBuf, 34 + 35 + /// Maximum width for output (in characters) 36 + #[arg(short, long, default_value = "80")] 37 + width: usize, 38 + 39 + /// Theme to use for coloring 40 + #[arg(short, long)] 41 + theme: Option<String>, 42 + }, 43 + 44 + /// Initialize a new slide deck with example content 45 + Init { 46 + /// Directory to create the deck in 47 + #[arg(default_value = ".")] 48 + path: PathBuf, 49 + 50 + /// Name of the deck file 51 + #[arg(short, long, default_value = "slides.md")] 52 + name: String, 53 + }, 54 + 55 + /// Check slides for errors and lint issues 56 + Check { 57 + /// Path to the markdown file 58 + file: PathBuf, 59 + 60 + /// Enable strict mode with additional checks 61 + #[arg(short, long)] 62 + strict: bool, 63 + }, 64 + } 65 + 1 66 fn main() { 2 - println!("Hello, world!"); 67 + let cli = ArgParser::parse(); 68 + 69 + tracing_subscriber::fmt().with_max_level(cli.log_level).init(); 70 + 71 + match cli.command { 72 + Commands::Present { file, theme } => { 73 + tracing::info!("Presenting slides from: {}", file.display()); 74 + if let Some(theme) = theme { 75 + tracing::debug!("Using theme: {}", theme); 76 + } 77 + eprintln!("TUI presentation mode not yet implemented"); 78 + } 79 + 80 + Commands::Print { file, width, theme } => { 81 + tracing::info!("Printing slides from: {} (width: {})", file.display(), width); 82 + if let Some(theme) = theme { 83 + tracing::debug!("Using theme: {}", theme); 84 + } 85 + eprintln!("Print mode not yet implemented"); 86 + } 87 + 88 + Commands::Init { path, name } => { 89 + tracing::info!("Initializing new deck: {} in {}", name, path.display()); 90 + eprintln!("Init command not yet implemented"); 91 + } 92 + 93 + Commands::Check { file, strict } => { 94 + tracing::info!("Checking slides: {}", file.display()); 95 + if strict { 96 + tracing::debug!("Strict mode enabled"); 97 + } 98 + eprintln!("Check command not yet implemented"); 99 + } 100 + } 101 + } 102 + 103 + #[cfg(test)] 104 + mod tests { 105 + use super::*; 106 + 107 + #[test] 108 + fn cli_present_command() { 109 + let cli = ArgParser::parse_from(["slides", "present", "test.md"]); 110 + match cli.command { 111 + Commands::Present { file, theme } => { 112 + assert_eq!(file, PathBuf::from("test.md")); 113 + assert_eq!(theme, None); 114 + } 115 + _ => panic!("Expected Present command"), 116 + } 117 + } 118 + 119 + #[test] 120 + fn cli_present_with_theme() { 121 + let cli = ArgParser::parse_from(["slides", "present", "test.md", "--theme", "dark"]); 122 + match cli.command { 123 + Commands::Present { file, theme } => { 124 + assert_eq!(file, PathBuf::from("test.md")); 125 + assert_eq!(theme, Some("dark".to_string())); 126 + } 127 + _ => panic!("Expected Present command"), 128 + } 129 + } 130 + 131 + #[test] 132 + fn cli_print_command() { 133 + let cli = ArgParser::parse_from(["slides", "print", "test.md", "-w", "100"]); 134 + match cli.command { 135 + Commands::Print { file, width, theme } => { 136 + assert_eq!(file, PathBuf::from("test.md")); 137 + assert_eq!(width, 100); 138 + assert_eq!(theme, None); 139 + } 140 + _ => panic!("Expected Print command"), 141 + } 142 + } 143 + 144 + #[test] 145 + fn cli_init_command() { 146 + let cli = ArgParser::parse_from(["slides", "init", "--name", "my-deck.md"]); 147 + match cli.command { 148 + Commands::Init { path, name } => { 149 + assert_eq!(path, PathBuf::from(".")); 150 + assert_eq!(name, "my-deck.md"); 151 + } 152 + _ => panic!("Expected Init command"), 153 + } 154 + } 155 + 156 + #[test] 157 + fn cli_check_command() { 158 + let cli = ArgParser::parse_from(["slides", "check", "test.md", "--strict"]); 159 + match cli.command { 160 + Commands::Check { file, strict } => { 161 + assert_eq!(file, PathBuf::from("test.md")); 162 + assert!(strict); 163 + } 164 + _ => panic!("Expected Check command"), 165 + } 166 + } 3 167 }
+3
core/Cargo.toml
··· 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + tracing = "0.1.41" 8 + owo-colors = "4.2.3" 9 + crossterm = "0.29.0"
+2 -14
core/src/lib.rs
··· 1 - pub fn add(left: u64, right: u64) -> u64 { 2 - left + right 3 - } 4 - 5 - #[cfg(test)] 6 - mod tests { 7 - use super::*; 8 - 9 - #[test] 10 - fn it_works() { 11 - let result = add(2, 2); 12 - assert_eq!(result, 4); 13 - } 14 - } 1 + pub mod term; 2 + pub mod theme;
+179
core/src/term.rs
··· 1 + use crossterm::{ 2 + event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, 3 + execute, 4 + terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, 5 + }; 6 + use std::{io, time::Duration}; 7 + 8 + /// Terminal manager that handles setup and cleanup 9 + /// 10 + /// Configures the terminal for TUI mode with alternate screen and raw mode. 11 + /// Automatically restores terminal state on drop to prevent terminal corruption. 12 + pub struct Terminal { 13 + in_alternate_screen: bool, 14 + in_raw_mode: bool, 15 + } 16 + 17 + impl Default for Terminal { 18 + fn default() -> Self { 19 + Self { 20 + in_alternate_screen: true, 21 + in_raw_mode: true, 22 + } 23 + } 24 + } 25 + 26 + impl Terminal { 27 + /// Initialize terminal for TUI mode 28 + /// 29 + /// Enables alternate screen and raw mode for full terminal control. 30 + pub fn setup() -> io::Result<Self> { 31 + let mut stdout = io::stdout(); 32 + execute!(stdout, EnterAlternateScreen)?; 33 + enable_raw_mode()?; 34 + 35 + Ok(Self::default()) 36 + } 37 + 38 + /// Restore terminal to normal mode by disabling raw mode and exits alternate screen. 39 + /// 40 + /// Called automatically on drop, but can be called manually for explicit cleanup. 41 + pub fn restore(&mut self) -> io::Result<()> { 42 + if self.in_raw_mode { 43 + disable_raw_mode()?; 44 + self.in_raw_mode = false; 45 + } 46 + 47 + if self.in_alternate_screen { 48 + let mut stdout = io::stdout(); 49 + execute!(stdout, LeaveAlternateScreen)?; 50 + self.in_alternate_screen = false; 51 + } 52 + 53 + Ok(()) 54 + } 55 + } 56 + 57 + impl Drop for Terminal { 58 + fn drop(&mut self) { 59 + let _ = self.restore(); 60 + } 61 + } 62 + 63 + /// Input event handler for slide navigation and control 64 + #[derive(Debug, Clone, PartialEq, Eq)] 65 + pub enum InputEvent { 66 + /// Move to next slide 67 + Next, 68 + /// Move to previous slide 69 + Previous, 70 + /// Jump to specific slide number 71 + Jump(usize), 72 + /// Toggle speaker notes 73 + ToggleNotes, 74 + /// Search slides 75 + Search, 76 + /// Quit presentation 77 + Quit, 78 + /// Terminal was resized 79 + Resize { width: u16, height: u16 }, 80 + /// Unknown/unhandled event 81 + Other, 82 + } 83 + 84 + impl InputEvent { 85 + /// Convert crossterm event to input event 86 + /// 87 + /// Maps keyboard and terminal events to presentation actions. 88 + pub fn from_crossterm(event: Event) -> Self { 89 + match event { 90 + Event::Key(KeyEvent { code, modifiers, .. }) => Self::from_key(code, modifiers), 91 + Event::Resize(width, height) => Self::Resize { width, height }, 92 + _ => Self::Other, 93 + } 94 + } 95 + 96 + /// Map key press to input event 97 + fn from_key(code: KeyCode, modifiers: KeyModifiers) -> Self { 98 + match (code, modifiers) { 99 + (KeyCode::Right | KeyCode::Char('j') | KeyCode::Char(' '), _) => Self::Next, 100 + (KeyCode::Char('n'), KeyModifiers::NONE) => Self::Next, 101 + (KeyCode::Left | KeyCode::Char('k'), _) => Self::Previous, 102 + (KeyCode::Char('p'), KeyModifiers::NONE) => Self::Previous, 103 + (KeyCode::Char('q'), KeyModifiers::NONE) => Self::Quit, 104 + (KeyCode::Char('c'), KeyModifiers::CONTROL) => Self::Quit, 105 + (KeyCode::Esc, _) => Self::Quit, 106 + (KeyCode::Char('n'), KeyModifiers::SHIFT) => Self::ToggleNotes, 107 + (KeyCode::Char('f'), KeyModifiers::CONTROL) => Self::Search, 108 + (KeyCode::Char('/'), KeyModifiers::NONE) => Self::Search, 109 + (KeyCode::Char(c), KeyModifiers::NONE) if c.is_ascii_digit() => { 110 + if let Some(num) = c.to_digit(10) { 111 + Self::Jump(num as usize) 112 + } else { 113 + Self::Other 114 + } 115 + } 116 + _ => Self::Other, 117 + } 118 + } 119 + 120 + /// Poll for next input event with timeout 121 + pub fn poll(timeout: Duration) -> io::Result<Option<Self>> { 122 + if event::poll(timeout)? { 123 + let event = event::read()?; 124 + Ok(Some(Self::from_crossterm(event))) 125 + } else { 126 + Ok(None) 127 + } 128 + } 129 + 130 + /// Read next input event (blocking until an event is available) 131 + pub fn read() -> io::Result<Self> { 132 + let event = event::read()?; 133 + Ok(Self::from_crossterm(event)) 134 + } 135 + } 136 + 137 + #[cfg(test)] 138 + mod tests { 139 + use super::*; 140 + 141 + #[test] 142 + fn input_event_navigation() { 143 + let next = InputEvent::from_key(KeyCode::Right, KeyModifiers::NONE); 144 + assert_eq!(next, InputEvent::Next); 145 + 146 + let prev = InputEvent::from_key(KeyCode::Left, KeyModifiers::NONE); 147 + assert_eq!(prev, InputEvent::Previous); 148 + } 149 + 150 + #[test] 151 + fn input_event_quit() { 152 + let quit_q = InputEvent::from_key(KeyCode::Char('q'), KeyModifiers::NONE); 153 + assert_eq!(quit_q, InputEvent::Quit); 154 + 155 + let quit_ctrl_c = InputEvent::from_key(KeyCode::Char('c'), KeyModifiers::CONTROL); 156 + assert_eq!(quit_ctrl_c, InputEvent::Quit); 157 + } 158 + 159 + #[test] 160 + fn input_event_jump() { 161 + let jump = InputEvent::from_key(KeyCode::Char('5'), KeyModifiers::NONE); 162 + assert_eq!(jump, InputEvent::Jump(5)); 163 + } 164 + 165 + #[test] 166 + fn input_event_search() { 167 + let search_slash = InputEvent::from_key(KeyCode::Char('/'), KeyModifiers::NONE); 168 + assert_eq!(search_slash, InputEvent::Search); 169 + 170 + let search_ctrl_f = InputEvent::from_key(KeyCode::Char('f'), KeyModifiers::CONTROL); 171 + assert_eq!(search_ctrl_f, InputEvent::Search); 172 + } 173 + 174 + #[test] 175 + fn input_event_resize() { 176 + let resize = InputEvent::from_crossterm(Event::Resize(80, 24)); 177 + assert_eq!(resize, InputEvent::Resize { width: 80, height: 24 }); 178 + } 179 + }
+76
core/src/theme.rs
··· 1 + use owo_colors::{OwoColorize, Style}; 2 + 3 + /// Color theme abstraction for slides with owo-colors with semantic roles for consistent theming across the application. 4 + /// 5 + /// Avoids dynamic dispatch by using compile-time color assignments. 6 + #[derive(Debug, Clone)] 7 + pub struct ThemeColors { 8 + pub heading: Style, 9 + pub body: Style, 10 + pub accent: Style, 11 + pub code: Style, 12 + pub dimmed: Style, 13 + } 14 + 15 + impl Default for ThemeColors { 16 + fn default() -> Self { 17 + Self { 18 + heading: Style::new().bright_cyan().bold(), 19 + body: Style::new().white(), 20 + accent: Style::new().bright_yellow(), 21 + code: Style::new().bright_green(), 22 + dimmed: Style::new().dimmed(), 23 + } 24 + } 25 + } 26 + 27 + impl ThemeColors { 28 + /// Apply heading style to text 29 + pub fn heading<'a, T: OwoColorize>(&self, text: &'a T) -> owo_colors::Styled<&'a T> { 30 + text.style(self.heading) 31 + } 32 + 33 + /// Apply body style to text 34 + pub fn body<'a, T: OwoColorize>(&self, text: &'a T) -> owo_colors::Styled<&'a T> { 35 + text.style(self.body) 36 + } 37 + 38 + /// Apply accent style to text 39 + pub fn accent<'a, T: OwoColorize>(&self, text: &'a T) -> owo_colors::Styled<&'a T> { 40 + text.style(self.accent) 41 + } 42 + 43 + /// Apply code style to text 44 + pub fn code<'a, T: OwoColorize>(&self, text: &'a T) -> owo_colors::Styled<&'a T> { 45 + text.style(self.code) 46 + } 47 + 48 + /// Apply dimmed style to text 49 + pub fn dimmed<'a, T: OwoColorize>(&self, text: &'a T) -> owo_colors::Styled<&'a T> { 50 + text.style(self.dimmed) 51 + } 52 + } 53 + 54 + #[cfg(test)] 55 + mod tests { 56 + use super::*; 57 + 58 + #[test] 59 + fn theme_colors_default() { 60 + let theme = ThemeColors::default(); 61 + let text = "Test"; 62 + let heading = theme.heading(&text); 63 + assert!(heading.to_string().contains("Test")); 64 + } 65 + 66 + #[test] 67 + fn theme_colors_apply_styles() { 68 + let theme = ThemeColors::default(); 69 + 70 + assert!(theme.heading(&"Heading").to_string().contains("Heading")); 71 + assert!(theme.body(&"Body").to_string().contains("Body")); 72 + assert!(theme.accent(&"Accent").to_string().contains("Accent")); 73 + assert!(theme.code(&"Code").to_string().contains("Code")); 74 + assert!(theme.dimmed(&"Dimmed").to_string().contains("Dimmed")); 75 + } 76 + }