From 6a044f174ab32635b97a54094f3b206d765a9cdb Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 8 Apr 2025 22:56:52 -0400 Subject: [PATCH 1/6] wip: convert a range into clipboard format --- Cargo.lock | 513 ++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/book/mod.rs | 35 ++++ 3 files changed, 536 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0f76e3..7199eb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,6 +129,27 @@ dependencies = [ "backtrace", ] +[[package]] +name = "arboard" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.59.0", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "arrayvec" version = "0.7.6" @@ -153,7 +174,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -243,6 +264,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bzip2" version = "0.4.4" @@ -307,7 +334,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -381,6 +408,15 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + [[package]] name = "colorchoice" version = "1.0.3" @@ -527,6 +563,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "either" version = "1.13.0" @@ -549,6 +600,33 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.34" @@ -676,6 +754,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getopts" version = "0.2.21" @@ -746,6 +834,30 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "html" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944d7db81871c611549302f3014418fedbcfbc46902f97e6a1c4f53e785903d2" +dependencies = [ + "html-sys", +] + +[[package]] +name = "html-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13eca55667a5657dd1b86db77c5fe2d1810e3f9413e9555a2c4c461733dd2573" + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -769,6 +881,19 @@ dependencies = [ "cc", ] +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", + "tiff", +] + [[package]] name = "indexmap" version = "2.6.0" @@ -876,6 +1001,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.72" @@ -891,6 +1022,16 @@ version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.48.5", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -947,6 +1088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -987,6 +1129,77 @@ dependencies = [ "autocfg", ] +[[package]] +name = "objc2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +dependencies = [ + "bitflags 2.6.0", + "objc2", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +dependencies = [ + "bitflags 2.6.0", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02" +dependencies = [ + "bitflags 2.6.0", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +dependencies = [ + "bitflags 2.6.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19" +dependencies = [ + "bitflags 2.6.0", + "objc2", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.5" @@ -1024,6 +1237,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1044,7 +1267,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1085,6 +1308,22 @@ dependencies = [ "sha2", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.11.2" @@ -1149,11 +1388,24 @@ checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64", "indexmap", - "quick-xml", + "quick-xml 0.32.0", "serde", "time", ] +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1238,6 +1490,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.37" @@ -1436,6 +1697,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -1507,11 +1774,13 @@ name = "sheetui" version = "0.1.0" dependencies = [ "anyhow", + "arboard", "clap", "colorsys", "crossterm", "csv", "futures", + "html", "ironcalc", "pulldown-cmark 0.13.0", "ratatui", @@ -1559,6 +1828,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "simdutf8" version = "0.1.5" @@ -1664,6 +1939,19 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.65" @@ -1684,6 +1972,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.36" @@ -1763,6 +2062,20 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tree_magic_mini" +version = "3.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469a727cac55b41448315cc10427c069c618ac59bb6a4480283fcd811749bdc2" +dependencies = [ + "fnv", + "home", + "memchr", + "nom", + "once_cell", + "petgraph", +] + [[package]] name = "tui-markdown" version = "0.3.1" @@ -1932,6 +2245,85 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" +dependencies = [ + "bitflags 2.6.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml 0.36.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "winapi" version = "0.3.9" @@ -1969,7 +2361,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1978,7 +2370,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1987,7 +2379,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1996,28 +2403,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2030,24 +2455,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2063,6 +2512,42 @@ dependencies = [ "memchr", ] +[[package]] +name = "wl-clipboard-rs" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a083daad7e8a4b8805ad73947ccadabe62afe37ce0e9787a56ff373d34762c7" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix", + "tempfile", + "thiserror", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index ca39db7..f86e77d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,5 @@ colorsys = "0.6.7" tui-markdown = { version = "0.3.1", features = [] } csv = "1.3.1" pulldown-cmark = "0.13.0" +html = "0.6.3" +arboard = { version = "3.5.0", features = ["wayland-data-control"] } diff --git a/src/book/mod.rs b/src/book/mod.rs index 16230c2..eccb9ba 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -87,6 +87,7 @@ impl<'book> AddressRange<'book> { }; (row_range, col_range) } + } /// A spreadsheet book with some internal state tracking. @@ -131,11 +132,45 @@ impl Book { Ok(()) } + /// Construct a payload of (html, csv_text) for the address range. + pub fn range_to_clipboard_content(&self, range: AddressRange) -> Result<(String, String), anyhow::Error> { + use html::tables; + let rows = self.get_rows_for_range(&range).unwrap_or_default(); + let mut html = tables::Table::builder(); + let mut writer = csv::Writer::from_writer(vec![]); + for row in rows { + let mut html_row = tables::TableRow::builder(); + writer.write_record(&row)?; + for cell in row { + let mut html_cell = tables::TableCell::builder(); + html_cell.text(cell); + html_row.push(html_cell.build()); + } + html.push(html_row.build()); + } + + let csv_content = writer.into_inner().expect("Failed to get the csv content"); + Ok((html.build().to_string(), String::from_utf8_lossy(&csv_content).to_string())) + } + pub fn get_export_rows(&self) -> Result>> { let sheet = self.location.sheet; Ok(self.get_export_rows_for_sheet(sheet)?) } + fn get_rows_for_range(&self, range: &AddressRange) -> Result>, anyhow::Error> { + let mut rows = Vec::new(); + for row in range.as_rows() { + let mut row_data = Vec::new(); + for address in row { + let cell_content = self.get_cell_addr_rendered(&address)?; + row_data.push(cell_content); + } + rows.push(row_data); + } + Ok(rows) + } + pub fn get_export_rows_for_sheet(&self, sheet: u32) -> Result>, anyhow::Error> { let worksheet = self .model From fbbdcb983fdc179cf3ee7de63e8aaaa8eba9e2da Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 8 Apr 2025 23:49:57 -0400 Subject: [PATCH 2/6] wip: convert a sheet into clipboard format --- Cargo.lock | 19 ++------ Cargo.toml | 2 +- src/book/mod.rs | 49 ++++++++++++-------- src/book/test.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7199eb7..0235a8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -844,19 +844,10 @@ dependencies = [ ] [[package]] -name = "html" -version = "0.6.3" +name = "htmf" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944d7db81871c611549302f3014418fedbcfbc46902f97e6a1c4f53e785903d2" -dependencies = [ - "html-sys", -] - -[[package]] -name = "html-sys" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13eca55667a5657dd1b86db77c5fe2d1810e3f9413e9555a2c4c461733dd2573" +checksum = "abcb5a4078c86d49875d3079c1b31c3bd5c277ad6adb88800a4d5c6af0fec9b2" [[package]] name = "iana-time-zone" @@ -1029,7 +1020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -1780,7 +1771,7 @@ dependencies = [ "crossterm", "csv", "futures", - "html", + "htmf", "ironcalc", "pulldown-cmark 0.13.0", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index f86e77d..e0fb8dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,5 @@ colorsys = "0.6.7" tui-markdown = { version = "0.3.1", features = [] } csv = "1.3.1" pulldown-cmark = "0.13.0" -html = "0.6.3" arboard = { version = "3.5.0", features = ["wayland-data-control"] } +htmf = "0.2.0" diff --git a/src/book/mod.rs b/src/book/mod.rs index eccb9ba..249247c 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -132,27 +132,19 @@ impl Book { Ok(()) } - /// Construct a payload of (html, csv_text) for the address range. - pub fn range_to_clipboard_content(&self, range: AddressRange) -> Result<(String, String), anyhow::Error> { - use html::tables; - let rows = self.get_rows_for_range(&range).unwrap_or_default(); - let mut html = tables::Table::builder(); - let mut writer = csv::Writer::from_writer(vec![]); - for row in rows { - let mut html_row = tables::TableRow::builder(); - writer.write_record(&row)?; - for cell in row { - let mut html_cell = tables::TableCell::builder(); - html_cell.text(cell); - html_row.push(html_cell.build()); - } - html.push(html_row.build()); - } - - let csv_content = writer.into_inner().expect("Failed to get the csv content"); - Ok((html.build().to_string(), String::from_utf8_lossy(&csv_content).to_string())) + /// Construct a payload of (html, csv_text) for a sheet. + pub fn sheeet_to_clipboard_content(&self, sheet: u32) -> Result<(String, String), anyhow::Error> { + let rows = self.get_export_rows_for_sheet(sheet)?; + rows_to_clipboard_content(rows) } + /// Construct a payload of (html, csv_text) for the address range. + pub fn range_to_clipboard_content(&self, range: AddressRange) -> Result<(String, String), anyhow::Error> { + let rows = self.get_rows_for_range(&range).unwrap_or_default(); + rows_to_clipboard_content(rows) + } + + /// Get rows for current sheet to export. pub fn get_export_rows(&self) -> Result>> { let sheet = self.location.sheet; Ok(self.get_export_rows_for_sheet(sheet)?) @@ -730,6 +722,25 @@ impl Book { } } +fn rows_to_clipboard_content(rows: Vec>) -> std::result::Result<(String, String), anyhow::Error> { + use htmf::prelude::*; + let table = table([]); + let mut writer = csv::Writer::from_writer(vec![]); + let mut table_rows = vec![]; + for row in rows { + let table_row = tr([]); + writer.write_record(&row)?; + let mut row_cells = vec![]; + for cell in row { + row_cells.push(td([]).with(text(cell))); + } + table_rows.push(table_row.with(row_cells)); + } + + let csv_content = writer.into_inner().expect("Failed to get the csv content"); + Ok((table.with(table_rows).to_html(), String::from_utf8_lossy(&csv_content).to_string())) +} + fn calculate_area(sheet: u32, start: &Address, end: &Address) -> Area { let area = Area { sheet, diff --git a/src/book/test.rs b/src/book/test.rs index 240a288..220229f 100644 --- a/src/book/test.rs +++ b/src/book/test.rs @@ -200,3 +200,119 @@ fn test_book_get_exportable_rows() { ] ); } + +#[test] +fn test_sheet_to_clipboard_content() { + let mut book = Book::default(); + book.update_cell( + &Address { + sheet: 0, + row: 1, + col: 1, + }, + "A1", + ) + .expect("failed to edit cell"); + book.update_cell( + &Address { + sheet: 0, + row: 1, + col: 2, + }, + "B1", + ) + .expect("failed to edit cell"); + book.update_cell( + &Address { + sheet: 0, + row: 2, + col: 1, + }, + "A2", + ) + .expect("failed to edit cell"); + book.update_cell( + &Address { + sheet: 0, + row: 2, + col: 2, + }, + "B2", + ) + .expect("failed to edit cell"); + + let (html, csv) = dbg!(book.sheeet_to_clipboard_content(0).expect("Failed to get clipboard content")); + + // Check that HTML contains table elements and our data + assert!(html.contains("")); + assert!(html.contains("")); + assert!(html.contains("
")); + assert!(html.contains("A1")); + assert!(html.contains("B1")); + assert!(html.contains("A2")); + assert!(html.contains("B2")); + + // Check CSV content + let expected_csv = ",,\n,A1,B1\n,A2,B2\n"; + assert_eq!(csv, expected_csv); +} + +#[test] +fn test_range_to_clipboard_content() { + let mut book = Book::default(); + book.update_cell( + &Address { + sheet: 0, + row: 1, + col: 1, + }, + "A1", + ) + .expect("failed to edit cell"); + book.update_cell( + &Address { + sheet: 0, + row: 1, + col: 2, + }, + "B1", + ) + .expect("failed to edit cell"); + book.update_cell( + &Address { + sheet: 0, + row: 2, + col: 1, + }, + "A2", + ) + .expect("failed to edit cell"); + book.update_cell( + &Address { + sheet: 0, + row: 2, + col: 2, + }, + "B2", + ) + .expect("failed to edit cell"); + + let start = Address { sheet: 0, row: 1, col: 1 }; + let end = Address { sheet: 0, row: 2, col: 2 }; + let range = super::AddressRange { start: &start, end: &end }; + + let (html, csv) = book.range_to_clipboard_content(range).expect("Failed to get clipboard content"); + + // Check that HTML contains table elements and our data + assert!(html.contains("")); + assert!(html.contains("")); + assert!(html.contains("
")); + assert!(html.contains("A1")); + assert!(html.contains("B1")); + assert!(html.contains("A2")); + assert!(html.contains("B2")); + + // Check CSV content + let expected_csv = "A1,B1\nA2,B2\n"; + assert_eq!(csv, expected_csv); +} From 25a782bfede62e44b23528653b18d92110314036 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 9 Apr 2025 00:10:01 -0400 Subject: [PATCH 3/6] wip: system clipboard support --- src/book/mod.rs | 8 ++++---- src/ui/mod.rs | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 249247c..99922e5 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -135,13 +135,13 @@ impl Book { /// Construct a payload of (html, csv_text) for a sheet. pub fn sheeet_to_clipboard_content(&self, sheet: u32) -> Result<(String, String), anyhow::Error> { let rows = self.get_export_rows_for_sheet(sheet)?; - rows_to_clipboard_content(rows) + rows_to_clipboard_content(&rows) } /// Construct a payload of (html, csv_text) for the address range. pub fn range_to_clipboard_content(&self, range: AddressRange) -> Result<(String, String), anyhow::Error> { let rows = self.get_rows_for_range(&range).unwrap_or_default(); - rows_to_clipboard_content(rows) + rows_to_clipboard_content(&rows) } /// Get rows for current sheet to export. @@ -722,14 +722,14 @@ impl Book { } } -fn rows_to_clipboard_content(rows: Vec>) -> std::result::Result<(String, String), anyhow::Error> { +pub fn rows_to_clipboard_content(rows: &Vec>) -> std::result::Result<(String, String), anyhow::Error> { use htmf::prelude::*; let table = table([]); let mut writer = csv::Writer::from_writer(vec![]); let mut table_rows = vec![]; for row in rows { let table_row = tr([]); - writer.write_record(&row)?; + writer.write_record(row)?; let mut row_cells = vec![]; for cell in row { row_cells.push(td([]).with(text(cell))); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 5bba2a0..3d12d6f 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -644,6 +644,7 @@ impl<'ws> Workspace<'ws> { } fn copy_range(&mut self, formatted: bool) -> Result<(), anyhow::Error> { + use arboard::Clipboard; self.update_range_selection()?; match &self.state.range_select.get_range() { Some((start, end)) => { @@ -659,6 +660,9 @@ impl<'ws> Workspace<'ws> { } rows.push(cols); } + let mut cb = Clipboard::new()?; + let (html, csv) = crate::book::rows_to_clipboard_content(&rows)?; + cb.set_html(html, Some(csv))?; self.state.clipboard = Some(ClipboardContents::Range(rows)); } None => { From 475822a7dca24420aab71769e9b0742c5bb001a9 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 9 Apr 2025 00:10:01 -0400 Subject: [PATCH 4/6] wip: nix build needs AppKit on mac now --- flake.nix | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index a1fdfeb..4c262aa 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,7 @@ buildInputs = ( if pkgs.stdenv.isDarwin - then with pkgs.darwin.apple_sdk.frameworks; [Security SystemConfiguration] + then with pkgs.darwin.apple_sdk.frameworks; [Security SystemConfiguration AppKit] else [pkgs.openssl] ) ++ [my-rust-bin]; @@ -37,7 +37,12 @@ rust-bin = pkgs.rust-bin; devShells.default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ gnumake my-rust-bin rust-analyzer cargo-tarpaulin ]; + nativeBuildInputs = with pkgs; ( + if pkgs.stdenv.isDarwin + then with pkgs.darwin.apple_sdk.frameworks; [Security SystemConfiguration AppKit] + else [pkgs.openssl] + ) + ++ [ gnumake my-rust-bin rust-analyzer cargo-tarpaulin ]; }; }); } From 563a885b7019627640ab1593c9f301977d190ea6 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 9 Apr 2025 00:16:41 -0400 Subject: [PATCH 5/6] wip: always copy rendered text --- src/ui/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 3d12d6f..d0bd2c0 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -660,8 +660,9 @@ impl<'ws> Workspace<'ws> { } rows.push(cols); } + // TODO(zaphar): Rethink this a bit perhaps? let mut cb = Clipboard::new()?; - let (html, csv) = crate::book::rows_to_clipboard_content(&rows)?; + let (html, csv) = self.book.range_to_clipboard_content(AddressRange { start, end })?; cb.set_html(html, Some(csv))?; self.state.clipboard = Some(ClipboardContents::Range(rows)); } From 81af54b6f4fe5042d429730fd12fa9193c2bb442 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Thu, 10 Apr 2025 21:19:09 -0400 Subject: [PATCH 6/6] wip: pasting from system clipboard --- docs/command.md | 1 + docs/navigation.md | 2 ++ docs/visual.md | 4 ++-- src/ui/cmd.rs | 20 +++++++++++++++++++ src/ui/mod.rs | 48 ++++++++++++++++++++++++++++++++++++---------- 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/docs/command.md b/docs/command.md index 0f49d23..ef2db6f 100644 --- a/docs/command.md +++ b/docs/command.md @@ -18,6 +18,7 @@ The currently supported commands are: * `help [topic]` Display help for a given topic. * `export-csv ` Export the current sheet to a csv file at ``. * `quit` Quits the application. `q` is a shorthand alias for this command. +* `system-paste` Paste from the system clipboard