Merge 81af54b6f4fe5042d429730fd12fa9193c2bb442 into b011a4d5bfb4c57fbd65fe0f92c9293e0def8692

This commit is contained in:
Jeremy Wall 2025-04-12 16:18:09 -04:00 committed by GitHub
commit 4ef5422d78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 728 additions and 27 deletions

504
Cargo.lock generated
View File

@ -129,6 +129,27 @@ dependencies = [
"backtrace", "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]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.6" version = "0.7.6"
@ -153,7 +174,7 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
"object", "object",
"rustc-demangle", "rustc-demangle",
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -243,6 +264,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.4.4" version = "0.4.4"
@ -307,7 +334,7 @@ dependencies = [
"js-sys", "js-sys",
"num-traits", "num-traits",
"wasm-bindgen", "wasm-bindgen",
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -381,6 +408,15 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 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]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.3" version = "1.0.3"
@ -527,6 +563,21 @@ dependencies = [
"subtle", "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]] [[package]]
name = "either" name = "either"
version = "1.13.0" version = "1.13.0"
@ -549,6 +600,33 @@ dependencies = [
"windows-sys 0.52.0", "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]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.34" version = "1.0.34"
@ -676,6 +754,16 @@ dependencies = [
"version_check", "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]] [[package]]
name = "getopts" name = "getopts"
version = "0.2.21" version = "0.2.21"
@ -746,6 +834,21 @@ dependencies = [
"digest", "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 = "htmf"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abcb5a4078c86d49875d3079c1b31c3bd5c277ad6adb88800a4d5c6af0fec9b2"
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.61" version = "0.1.61"
@ -769,6 +872,19 @@ dependencies = [
"cc", "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]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.6.0" version = "2.6.0"
@ -876,6 +992,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "jpeg-decoder"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.72" version = "0.3.72"
@ -891,6 +1013,16 @@ version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" 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.52.6",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -947,6 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [ dependencies = [
"adler2", "adler2",
"simd-adler32",
] ]
[[package]] [[package]]
@ -987,6 +1120,77 @@ dependencies = [
"autocfg", "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]] [[package]]
name = "object" name = "object"
version = "0.36.5" version = "0.36.5"
@ -1024,6 +1228,16 @@ dependencies = [
"pkg-config", "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]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.3" version = "0.12.3"
@ -1044,7 +1258,7 @@ dependencies = [
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -1085,6 +1299,22 @@ dependencies = [
"sha2", "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]] [[package]]
name = "phf" name = "phf"
version = "0.11.2" version = "0.11.2"
@ -1149,11 +1379,24 @@ checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
dependencies = [ dependencies = [
"base64", "base64",
"indexmap", "indexmap",
"quick-xml", "quick-xml 0.32.0",
"serde", "serde",
"time", "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]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@ -1238,6 +1481,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "quick-xml"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.37" version = "1.0.37"
@ -1436,6 +1688,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@ -1507,11 +1765,13 @@ name = "sheetui"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arboard",
"clap", "clap",
"colorsys", "colorsys",
"crossterm", "crossterm",
"csv", "csv",
"futures", "futures",
"htmf",
"ironcalc", "ironcalc",
"pulldown-cmark 0.13.0", "pulldown-cmark 0.13.0",
"ratatui", "ratatui",
@ -1559,6 +1819,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]] [[package]]
name = "simdutf8" name = "simdutf8"
version = "0.1.5" version = "0.1.5"
@ -1664,6 +1930,19 @@ dependencies = [
"yaml-rust", "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]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.65" version = "1.0.65"
@ -1684,6 +1963,17 @@ dependencies = [
"syn", "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]] [[package]]
name = "time" name = "time"
version = "0.3.36" version = "0.3.36"
@ -1763,6 +2053,20 @@ dependencies = [
"once_cell", "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]] [[package]]
name = "tui-markdown" name = "tui-markdown"
version = "0.3.1" version = "0.3.1"
@ -1932,6 +2236,85 @@ version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 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]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -1969,7 +2352,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -1978,7 +2361,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -1987,7 +2370,22 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [ 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]] [[package]]
@ -1996,28 +2394,46 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu", "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm", "windows_i686_gnullvm",
"windows_i686_msvc", "windows_i686_msvc 0.52.6",
"windows_x86_64_gnu", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc", "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]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.52.6"
@ -2030,24 +2446,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 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]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 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]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 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]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
@ -2063,6 +2503,42 @@ dependencies = [
"memchr", "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]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.5" version = "0.4.5"

View File

@ -22,3 +22,5 @@ colorsys = "0.6.7"
tui-markdown = { version = "0.3.1", features = [] } tui-markdown = { version = "0.3.1", features = [] }
csv = "1.3.1" csv = "1.3.1"
pulldown-cmark = "0.13.0" pulldown-cmark = "0.13.0"
arboard = { version = "3.5.0", features = ["wayland-data-control"] }
htmf = "0.2.0"

View File

@ -18,6 +18,7 @@ The currently supported commands are:
* `help [topic]` Display help for a given topic. * `help [topic]` Display help for a given topic.
* `export-csv <path>` Export the current sheet to a csv file at `<path>`. * `export-csv <path>` Export the current sheet to a csv file at `<path>`.
* `quit` Quits the application. `q` is a shorthand alias for this command. * `quit` Quits the application. `q` is a shorthand alias for this command.
* `system-paste` Paste from the system clipboard
<aside>Note that in the case of `quit` and `edit` that we do not currently <aside>Note that in the case of `quit` and `edit` that we do not currently
prompt you if the current spreadsheet has not been saved yet. So your changes prompt you if the current spreadsheet has not been saved yet. So your changes

View File

@ -49,3 +49,5 @@ Range selections made from navigation mode will be available to paste into a Cel
<aside>Note that for `q` this will not currently prompt you if the sheet is not <aside>Note that for `q` this will not currently prompt you if the sheet is not
saved.</aside> saved.</aside>
Note also that copy paste works with the system clipboard.

View File

@ -5,8 +5,8 @@ select mode from CellEdit mode with `CTRL-r`.
* `h`, `j`, `k`, `l` will navigate around the sheet. * `h`, `j`, `k`, `l` will navigate around the sheet.
* `Ctrl-n`, `Ctrl-p` will navigate between sheets. * `Ctrl-n`, `Ctrl-p` will navigate between sheets.
* `Ctrl-c`, `y` Copy the cell or range contents. * `Ctrl-c`, `y` Copy the cell or range formatted contents.
* `Ctrl-Shift-C`, 'Y' Copy the cell or range formatted content. * `Ctrl-Shift-C`, 'Y' Copy the cell or range content.
* `The spacebar will select the start and end of the range respectively. * `The spacebar will select the start and end of the range respectively.
* `d` will delete the contents of the range leaving any style untouched * `d` will delete the contents of the range leaving any style untouched
* `D` will delete the contents of the range including any style * `D` will delete the contents of the range including any style

View File

@ -26,7 +26,7 @@
buildInputs = buildInputs =
( (
if pkgs.stdenv.isDarwin 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] else [pkgs.openssl]
) )
++ [my-rust-bin]; ++ [my-rust-bin];
@ -37,7 +37,12 @@
rust-bin = pkgs.rust-bin; rust-bin = pkgs.rust-bin;
devShells.default = pkgs.mkShell { 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 ];
}; };
}); });
} }

View File

@ -87,6 +87,7 @@ impl<'book> AddressRange<'book> {
}; };
(row_range, col_range) (row_range, col_range)
} }
} }
/// A spreadsheet book with some internal state tracking. /// A spreadsheet book with some internal state tracking.
@ -131,11 +132,37 @@ impl Book {
Ok(()) Ok(())
} }
/// 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<Vec<Vec<String>>> { pub fn get_export_rows(&self) -> Result<Vec<Vec<String>>> {
let sheet = self.location.sheet; let sheet = self.location.sheet;
Ok(self.get_export_rows_for_sheet(sheet)?) Ok(self.get_export_rows_for_sheet(sheet)?)
} }
fn get_rows_for_range(&self, range: &AddressRange) -> Result<Vec<Vec<String>>, 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<Vec<Vec<String>>, anyhow::Error> { pub fn get_export_rows_for_sheet(&self, sheet: u32) -> Result<Vec<Vec<String>>, anyhow::Error> {
let worksheet = self let worksheet = self
.model .model
@ -695,6 +722,25 @@ impl Book {
} }
} }
pub fn rows_to_clipboard_content(rows: &Vec<Vec<String>>) -> 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 { fn calculate_area(sheet: u32, start: &Address, end: &Address) -> Area {
let area = Area { let area = Area {
sheet, sheet,

View File

@ -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("<table>"));
assert!(html.contains("<tr>"));
assert!(html.contains("<td>"));
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("<table>"));
assert!(html.contains("<tr>"));
assert!(html.contains("<td>"));
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);
}

View File

@ -16,6 +16,7 @@ pub enum Cmd<'a> {
Edit(&'a str), Edit(&'a str),
Help(Option<&'a str>), Help(Option<&'a str>),
ExportCsv(&'a str), ExportCsv(&'a str),
SystemPaste,
Quit, Quit,
} }
@ -68,6 +69,9 @@ pub fn parse<'cmd, 'i: 'cmd>(input: &'i str) -> Result<Option<Cmd<'cmd>>, &'stat
if let Some(cmd) = try_consume_color_cell(cursor.clone())? { if let Some(cmd) = try_consume_color_cell(cursor.clone())? {
return Ok(Some(cmd)); return Ok(Some(cmd));
} }
if let Some(cmd) = try_consume_system_paste(cursor.clone())? {
return Ok(Some(cmd));
}
Ok(None) Ok(None)
} }
@ -316,6 +320,22 @@ fn try_consume_quit<'cmd, 'i: 'cmd>(
return Ok(Some(Cmd::Quit)); return Ok(Some(Cmd::Quit));
} }
fn try_consume_system_paste<'cmd, 'i: 'cmd>(
mut input: StrCursor<'i>,
) -> Result<Option<Cmd<'cmd>>, &'static str> {
const LONG: &'static str = "system-paste";
if compare(input.clone(), LONG) {
input.seek(LONG.len());
} else {
return Ok(None);
}
if input.remaining() > 0 {
return Err("Invalid command: system-paste does not take an argument");
}
return Ok(Some(Cmd::SystemPaste));
}
fn try_consume_rename_sheet<'cmd, 'i: 'cmd>( fn try_consume_rename_sheet<'cmd, 'i: 'cmd>(
mut input: StrCursor<'i>, mut input: StrCursor<'i>,
) -> Result<Option<Cmd<'cmd>>, &'static str> { ) -> Result<Option<Cmd<'cmd>>, &'static str> {

View File

@ -513,6 +513,12 @@ impl<'ws> Workspace<'ws> {
.set_cell_style(&[("fill.bg_color", &color)], &area)?; .set_cell_style(&[("fill.bg_color", &color)], &area)?;
Ok(None) Ok(None)
} }
Ok(Some(Cmd::SystemPaste)) => {
let rows = self.get_rows_from_system_cipboard()?;
self.state.clipboard = Some(ClipboardContents::Range(rows));
self.paste_range()?;
Ok(None)
}
Ok(None) => { Ok(None) => {
self.enter_dialog_mode(Markdown::from_str(&format!( self.enter_dialog_mode(Markdown::from_str(&format!(
"Unrecognized commmand {}", "Unrecognized commmand {}",
@ -607,19 +613,19 @@ impl<'ws> Workspace<'ws> {
Ok(()) Ok(())
})?; })?;
} }
KeyCode::Char('C') if key.modifiers.contains(KeyModifiers::CONTROL) => { KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
self.copy_range(true)?; self.copy_range(true)?;
self.exit_range_select_mode()?; self.exit_range_select_mode()?;
} }
KeyCode::Char('Y') => {
self.copy_range(true)?;
self.exit_range_select_mode()?;
}
KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => {
self.copy_range(false)?;
self.exit_range_select_mode()?;
}
KeyCode::Char('y') => { KeyCode::Char('y') => {
self.copy_range(true)?;
self.exit_range_select_mode()?;
}
KeyCode::Char('C') if key.modifiers == KeyModifiers::CONTROL => {
self.copy_range(false)?;
self.exit_range_select_mode()?;
}
KeyCode::Char('Y') => {
self.copy_range(false)?; self.copy_range(false)?;
self.exit_range_select_mode()?; self.exit_range_select_mode()?;
} }
@ -644,6 +650,7 @@ impl<'ws> Workspace<'ws> {
} }
fn copy_range(&mut self, formatted: bool) -> Result<(), anyhow::Error> { fn copy_range(&mut self, formatted: bool) -> Result<(), anyhow::Error> {
use arboard::Clipboard;
self.update_range_selection()?; self.update_range_selection()?;
match &self.state.range_select.get_range() { match &self.state.range_select.get_range() {
Some((start, end)) => { Some((start, end)) => {
@ -659,6 +666,12 @@ impl<'ws> Workspace<'ws> {
} }
rows.push(cols); rows.push(cols);
} }
// TODO(zaphar): Rethink this a bit perhaps?
let mut cb = Clipboard::new()?;
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)); self.state.clipboard = Some(ClipboardContents::Range(rows));
} }
None => { None => {
@ -672,6 +685,26 @@ impl<'ws> Workspace<'ws> {
Ok(()) Ok(())
} }
fn get_rows_from_system_cipboard(&mut self) -> Result<Vec<Vec<String>>, anyhow::Error> {
use arboard::Clipboard;
let mut cb = Clipboard::new()?;
let txt = match cb.get_text() {
Ok(txt) => txt,
Err(e) => return Err(anyhow!(e)),
};
let reader = csv::Reader::from_reader(txt.as_bytes());
let mut rows = Vec::new();
for rec in reader.into_byte_records() {
let record = rec?;
let mut row = Vec::with_capacity(record.len());
for i in 0..record.len() {
row.push(String::from_utf8_lossy(record.get(i).expect("Unexpected failure to get cell row")).to_string());
};
rows.push(row);
}
Ok(rows)
}
fn update_range_selection(&mut self) -> Result<bool, anyhow::Error> { fn update_range_selection(&mut self) -> Result<bool, anyhow::Error> {
Ok(if self.state.range_select.start.is_none() { Ok(if self.state.range_select.start.is_none() {
self.state.range_select.start = Some(self.book.location.clone()); self.state.range_select.start = Some(self.book.location.clone());