mirror of
https://github.com/itsjunetime/tdf.git
synced 2026-06-01 23:51:46 -04:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2a9c90b34 | |||
| 16c0aed9a3 | |||
| 19030f7fd4 | |||
| d5d62c81a3 | |||
| 7b9e1462da | |||
| a4905b2ae5 | |||
| 9b9796e718 | |||
| e5cf92221f | |||
| 3fcf3be65f | |||
| 3dc5135a8b | |||
| 065797ccf6 | |||
| 55e0c2b33f | |||
| 0a0112a7e1 | |||
| 8f57cd02c3 | |||
| 74def1c0a8 | |||
| 670251fdff | |||
| b7d1b78e98 | |||
| fe8287bf7a | |||
| 38b307d628 | |||
| 09a332f07e | |||
| cde86b4f2c | |||
| 5a492599da |
@@ -0,0 +1,2 @@
|
||||
[*.rs]
|
||||
indent_style = tab
|
||||
+12
-1
@@ -1,7 +1,18 @@
|
||||
# Unreleased
|
||||
|
||||
# v0.5.0
|
||||
|
||||
- Switched simd base64 crate for one that works on stable (from `vb64` to `base64_simd`)
|
||||
- Allow boolean arguments to function as flags, without a `true` or `false` argument following the flag itself
|
||||
- Allow boolean arguments to function as flags, without a `true` or `false` argument following the flag itself ([#109](https://github.com/itsjunetime/tdf/pull/109), thanks [@tatounee](https://github.com/tatounee)!)
|
||||
- Fix cropping issues when zooming out too much while using kitty protocol
|
||||
- Added `gg` and `G` keybindings for scrolling to the top and bottom of a page, respectively, when filling the width of the screen with kitty
|
||||
- Updated help page to only show kitty keybindings when you're actually using kitty
|
||||
- Map page-up and page-down keybindings to do the same thing as up-key and down-key ([#115](https://github.com/itsjunetime/tdf/pull/115), thanks [@maxdexh](https://github.com/maxdexh)!)
|
||||
- Vertically center pages within the available space if they are not constrained by the height ([#116](https://github.com/itsjunetime/tdf/pull/116), thanks [@maxdexh](https://github.com/maxdexh)!)
|
||||
- Fixed issue with cooked mode not being restored upon panic/error ([#118](https://github.com/itsjunetime/tdf/pull/118), thanks [@maxdexh](https://github.com/maxdexh)!)
|
||||
- Implemented a debounce for file reload updates to prevent some editors from paralyzing the app due to a flurry of reloads ([#117](https://github.com/itsjunetime/tdf/pull/117), thanks [@maxdexh](https://github.com/maxdexh))
|
||||
- Fixed an overflow when zooming out of horizontal pdfs ([#119](https://github.com/itsjunetime/tdf/pull/119), thanks [@maxdexh](https://github.com/maxdexh)!)
|
||||
- Reworked zooming to allow for full zooming in and out and panning in both directions ([#121](https://github.com/itsjunetime/tdf/pull/121), thanks [@maxdexh](https://github.com/maxdexh)!)
|
||||
|
||||
# v0.4.3
|
||||
|
||||
|
||||
Generated
+240
-203
@@ -26,6 +26,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923"
|
||||
dependencies = [
|
||||
"as-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned-vec"
|
||||
version = "0.6.4"
|
||||
@@ -82,7 +91,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -91,6 +100,15 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "as-slice"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
@@ -99,7 +117,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -123,6 +141,26 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "av-scenechange"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
|
||||
dependencies = [
|
||||
"aligned",
|
||||
"anyhow",
|
||||
"arg_enum_proc_macro",
|
||||
"arrayvec",
|
||||
"log",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"pastey",
|
||||
"rayon",
|
||||
"thiserror 2.0.17",
|
||||
"v_frame",
|
||||
"y4m",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "av1-grain"
|
||||
version = "0.2.5"
|
||||
@@ -148,9 +186,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871"
|
||||
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"bytes",
|
||||
@@ -241,7 +279,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -259,6 +297,12 @@ version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -273,9 +317,12 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "bitstream-io"
|
||||
version = "2.6.0"
|
||||
version = "4.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
|
||||
checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
|
||||
dependencies = [
|
||||
"core2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
@@ -288,9 +335,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.7.7"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"
|
||||
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
@@ -318,9 +365,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
@@ -339,9 +386,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.44"
|
||||
version = "1.2.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
|
||||
checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -358,16 +405,6 @@ dependencies = [
|
||||
"nom 7.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
@@ -431,18 +468,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.51"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.51"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -569,6 +606,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core2"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
@@ -702,9 +748,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
@@ -722,11 +768,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "csscolorparser"
|
||||
version = "0.7.2"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fda6aace1fbef3aa217b27f4c8d7d071ef2a70a5ca51050b1f17d40299d3f16"
|
||||
checksum = "3b9e6b904cb9ee4cb0bb93ba2b1bf3dcd2d3fc58c25557934a4b56cbd2ad9f88"
|
||||
dependencies = [
|
||||
"phf",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -750,7 +796,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -761,9 +807,15 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debounce"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f2e5bc95e82bd8e9b333f4c5ff6dceab54e2e99f4d8cef2a680d417206ead34"
|
||||
|
||||
[[package]]
|
||||
name = "deltae"
|
||||
version = "0.3.2"
|
||||
@@ -797,7 +849,7 @@ dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -884,7 +936,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -922,6 +974,21 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.74.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.11.0"
|
||||
@@ -954,9 +1021,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "finl_unicode"
|
||||
@@ -1071,7 +1138,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1141,9 +1208,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
@@ -1216,9 +1283,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
@@ -1252,12 +1319,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
@@ -1304,9 +1370,9 @@ checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.7.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
|
||||
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -1340,9 +1406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
|
||||
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -1397,12 +1463,13 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.8"
|
||||
version = "0.25.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7"
|
||||
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"exr",
|
||||
"moxcms",
|
||||
"num-traits",
|
||||
"png",
|
||||
@@ -1420,9 +1487,9 @@ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.0"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -1467,7 +1534,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1478,16 +1545,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1547,10 +1605,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittage"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/itsjunetime/kittage.git#0ec72474513721f9960332067fd740afab3ed255"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be6b61789e2841119b210569d4acd950d47dae805f8d3235190d28cb40677632"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"base64-simd",
|
||||
"crossterm",
|
||||
"futures-core",
|
||||
"image",
|
||||
@@ -1596,6 +1655,12 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
@@ -1622,6 +1687,12 @@ dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.44"
|
||||
@@ -1961,7 +2032,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1991,6 +2062,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2073,6 +2145,12 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pastey"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_geometry"
|
||||
version = "0.5.1"
|
||||
@@ -2100,9 +2178,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4"
|
||||
checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"ucd-trie",
|
||||
@@ -2110,9 +2188,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de"
|
||||
checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -2120,22 +2198,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843"
|
||||
checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.8.3"
|
||||
version = "2.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a"
|
||||
checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"sha2",
|
||||
@@ -2168,7 +2246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2181,7 +2259,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2210,7 +2288,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2318,7 +2396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2341,7 +2419,7 @@ dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2355,9 +2433,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "psx-shm"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d6abd69b7d14272c59b72b14e3ff058232197ac0846480d58f047140ce1a2"
|
||||
checksum = "b9d9d53299b024cf527e0cdc4e871cee6e7aa28955c1214b78799804102a2e3d"
|
||||
dependencies = [
|
||||
"memmap2",
|
||||
"rustix",
|
||||
@@ -2380,9 +2458,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -2399,19 +2477,27 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2419,8 +2505,14 @@ name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2518,19 +2610,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rav1e"
|
||||
version = "0.7.1"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
|
||||
checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
|
||||
dependencies = [
|
||||
"aligned-vec",
|
||||
"arbitrary",
|
||||
"arg_enum_proc_macro",
|
||||
"arrayvec",
|
||||
"av-scenechange",
|
||||
"av1-grain",
|
||||
"bitstream-io",
|
||||
"built",
|
||||
"cfg-if",
|
||||
"interpolate_name",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.14.0",
|
||||
"libc",
|
||||
"libfuzzer-sys",
|
||||
"log",
|
||||
@@ -2539,23 +2633,21 @@ dependencies = [
|
||||
"noop_proc_macro",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"profiling",
|
||||
"rand",
|
||||
"rand 0.9.2",
|
||||
"rand_chacha",
|
||||
"simd_helpers",
|
||||
"system-deps",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.17",
|
||||
"v_frame",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ravif"
|
||||
version = "0.11.20"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b"
|
||||
checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
|
||||
dependencies = [
|
||||
"avif-serialize",
|
||||
"imgref",
|
||||
@@ -2735,7 +2827,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2751,15 +2843,6 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
@@ -2809,9 +2892,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -2868,6 +2951,12 @@ dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -2898,7 +2987,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2914,9 +3003,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.109"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2929,34 +3018,16 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
|
||||
dependencies = [
|
||||
"cfg-expr",
|
||||
"heck",
|
||||
"pkg-config",
|
||||
"toml",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tdf-viewer"
|
||||
version = "0.4.3"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"console-subscriber",
|
||||
"cpuprofiler",
|
||||
"criterion",
|
||||
"crossterm",
|
||||
"csscolorparser 0.7.2",
|
||||
"csscolorparser 0.8.0",
|
||||
"debounce",
|
||||
"flexi_logger",
|
||||
"flume",
|
||||
"futures-util",
|
||||
@@ -3064,7 +3135,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3075,7 +3146,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3142,7 +3213,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3169,40 +3240,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.14.2"
|
||||
@@ -3293,7 +3330,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3403,12 +3440,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -3496,7 +3527,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3695,7 +3726,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3706,7 +3737,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3871,15 +3902,6 @@ dependencies = [
|
||||
"windows-ext",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wio"
|
||||
version = "0.2.2"
|
||||
@@ -3910,6 +3932,12 @@ version = "0.4.0-pre.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a6d9b56f406f5754a3808524166b6e6bdfe219c0526e490cfc39ecc0582a4e6"
|
||||
|
||||
[[package]]
|
||||
name = "y4m"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
|
||||
|
||||
[[package]]
|
||||
name = "yeslogic-fontconfig-sys"
|
||||
version = "6.0.0"
|
||||
@@ -3923,35 +3951,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
version = "0.8.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.27"
|
||||
version = "0.8.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.4.12"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||
checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.21"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
|
||||
checksum = "dc6fb7703e32e9a07fb3f757360338b3a567a5054f21b5f52a666752e333d58e"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
+136
-20
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tdf-viewer"
|
||||
version = "0.4.3"
|
||||
version = "0.5.0"
|
||||
authors = ["June Welker <junewelker@gmail.com>"]
|
||||
edition = "2024"
|
||||
description = "A terminal viewer for PDFs"
|
||||
@@ -39,10 +39,11 @@ xflags = "0.4.0-pre.2"
|
||||
mimalloc = "0.1.43"
|
||||
nix = { version = "0.30.0", features = ["signal"] }
|
||||
mupdf = { git = "https://github.com/messense/mupdf-rs.git", rev = "2e0fae910fac8048c7008211fc4d3b9f5d227a07", default-features = false, features = ["svg", "system-fonts", "img"] }
|
||||
rayon = { version = "*", default-features = false }
|
||||
rayon = { version = "1", default-features = false }
|
||||
# kittage = { path = "../kittage/", features = ["crossterm-tokio", "image-crate", "log"] }
|
||||
kittage = { git = "https://github.com/itsjunetime/kittage.git", features = ["crossterm-tokio", "image-crate", "log"] }
|
||||
memmap2 = "*"
|
||||
kittage = { version = "0.1.1", features = ["crossterm-tokio", "image-crate", "log"] }
|
||||
memmap2 = "0"
|
||||
csscolorparser = { version = "0.8.0", default-features = false }
|
||||
|
||||
# logging
|
||||
log = "0.4.27"
|
||||
@@ -50,7 +51,7 @@ flexi_logger = "0.31"
|
||||
|
||||
# for tracing with tokio-console
|
||||
console-subscriber = { version = "0.5.0", optional = true }
|
||||
csscolorparser = { version = "0.7.0" }
|
||||
debounce = "0.2.2"
|
||||
|
||||
[profile.production]
|
||||
inherits = "release"
|
||||
@@ -75,90 +76,205 @@ name = "for_profiling"
|
||||
path = "./benches/for_profiling.rs"
|
||||
|
||||
[lints.clippy]
|
||||
uninlined_format_args = "warn"
|
||||
redundant_closure_for_method_calls = "warn"
|
||||
cast_lossless = "warn"
|
||||
single_char_pattern = "warn"
|
||||
manual_let_else = "warn"
|
||||
ignored_unit_patterns = "warn"
|
||||
range_plus_one = "warn"
|
||||
unreadable_literal = "warn"
|
||||
redundant_else = "warn"
|
||||
alloc_instead_of_core = "warn"
|
||||
allow_attributes = "warn"
|
||||
as_pointer_underscore = "warn"
|
||||
as_ptr_cast_mut = "warn"
|
||||
as_underscore = "warn"
|
||||
assigning_clones = "warn"
|
||||
assertions_on_result_states = "warn"
|
||||
bool_to_int_with_if = "warn"
|
||||
borrow_as_ptr = "warn"
|
||||
branches_sharing_code = "warn"
|
||||
cargo_common_metadata = "warn"
|
||||
case_sensitive_file_extension_comparisons = "warn"
|
||||
cast_lossless = "warn"
|
||||
cast_ptr_alignment = "warn"
|
||||
cfg_not_test = "warn"
|
||||
checked_conversions = "warn"
|
||||
clear_with_drain = "warn"
|
||||
cloned_instead_of_copied = "warn"
|
||||
coerce_container_to_any = "warn"
|
||||
comparison_chain = "warn"
|
||||
copy_iterator = "warn"
|
||||
create_dir = "warn"
|
||||
debug_assert_with_mut_call = "warn"
|
||||
decimal_literal_representation = "warn"
|
||||
default_trait_access = "warn"
|
||||
deref_by_slicing = "warn"
|
||||
doc_broken_link = "warn"
|
||||
doc_link_code = "warn"
|
||||
doc_link_with_quotes = "warn"
|
||||
elidable_lifetime_names = "warn"
|
||||
empty_drop = "warn"
|
||||
empty_enums = "warn"
|
||||
empty_enum_variants_with_brackets = "warn"
|
||||
empty_structs_with_brackets = "warn"
|
||||
equatable_if_let = "warn"
|
||||
error_impl_error = "warn"
|
||||
expl_impl_clone_on_copy = "warn"
|
||||
explicit_deref_methods = "warn"
|
||||
explicit_into_iter_loop = "warn"
|
||||
explicit_iter_loop = "warn"
|
||||
fallible_impl_from = "warn"
|
||||
filetype_is_file = "warn"
|
||||
filter_map_next = "warn"
|
||||
flat_map_option = "warn"
|
||||
fn_to_numeric_cast_any = "warn"
|
||||
fn_params_excessive_bools = "warn"
|
||||
from_iter_instead_of_collect = "warn"
|
||||
format_collect = "warn"
|
||||
format_push_string = "warn"
|
||||
get_unwrap = "warn"
|
||||
if_then_some_else_none = "warn"
|
||||
ignore_without_reason = "warn"
|
||||
ignored_unit_patterns = "warn"
|
||||
implicit_clone = "warn"
|
||||
imprecise_flops = "warn"
|
||||
index_refutable_slice = "warn"
|
||||
inefficient_to_string = "warn"
|
||||
indexing_slicing = "allow" # can't warn on this cause we basically have to do indexing for some ratatui apis
|
||||
infinite_loop = "warn"
|
||||
invalid_upcast_comparisons = "warn"
|
||||
ip_constant = "warn"
|
||||
iter_filter_is_ok = "warn"
|
||||
iter_filter_is_some = "warn"
|
||||
iter_not_returning_iterator = "warn"
|
||||
iter_on_empty_collections = "warn"
|
||||
iter_on_single_items = "warn"
|
||||
large_digit_groups = "warn"
|
||||
large_futures = "warn"
|
||||
large_stack_arrays = "warn"
|
||||
large_include_file = "warn"
|
||||
large_stack_frames = "warn"
|
||||
large_types_passed_by_value = "warn"
|
||||
linkedlist = "warn"
|
||||
literal_string_with_formatting_args = "warn"
|
||||
lossy_float_literal = "warn"
|
||||
macro_use_imports = "warn"
|
||||
manual_assert = "warn"
|
||||
manual_instant_elapsed = "warn"
|
||||
manual_is_power_of_two = "warn"
|
||||
manual_is_variant_and = "warn"
|
||||
manual_let_else = "warn"
|
||||
manual_midpoint = "warn"
|
||||
manual_ok_or = "warn"
|
||||
manual_string_new = "warn"
|
||||
many_single_char_names = "warn"
|
||||
manual_unwrap_or = "warn"
|
||||
map_err_ignore = "warn"
|
||||
map_unwrap_or = "warn"
|
||||
map_with_unused_argument_over_ranges = "warn"
|
||||
match_same_arms = "warn"
|
||||
match_wild_err_arm = "warn"
|
||||
match_wildcard_for_single_variants = "warn"
|
||||
maybe_infinite_iter = "warn"
|
||||
mem_forget = "warn"
|
||||
mismatching_type_param_order = "warn"
|
||||
missing_assert_message = "warn"
|
||||
missing_fields_in_debug = "warn"
|
||||
mixed_read_write_in_expression = "warn"
|
||||
multiple_unsafe_ops_per_block = "warn"
|
||||
must_use_candidate = "warn"
|
||||
mut_mut = "warn"
|
||||
mutex_atomic = "warn"
|
||||
mutex_integer = "warn"
|
||||
naive_bytecount = "warn"
|
||||
needless_bitwise_bool = "warn"
|
||||
needless_collect = "warn"
|
||||
needless_continue = "warn"
|
||||
needless_for_each = "warn"
|
||||
needless_pass_by_ref_mut = "warn"
|
||||
needless_pass_by_value = "warn"
|
||||
needless_raw_string_hashes = "warn"
|
||||
needless_raw_strings = "warn"
|
||||
negative_feature_names = "warn"
|
||||
no_effect_underscore_binding = "warn"
|
||||
no_mangle_with_rust_abi = "warn"
|
||||
non_send_fields_in_send_ty = "warn"
|
||||
non_std_lazy_statics = "warn"
|
||||
non_zero_suggestions = "warn"
|
||||
nonstandard_macro_braces = "warn"
|
||||
option_as_ref_cloned = "warn"
|
||||
option_option = "warn"
|
||||
or_fun_call = "warn"
|
||||
path_buf_push_overwrite = "warn"
|
||||
pathbuf_init_then_push = "warn"
|
||||
precedence_bits = "warn"
|
||||
ptr_as_ptr = "warn"
|
||||
ptr_cast_constness = "warn"
|
||||
pub_underscore_fields = "warn"
|
||||
pub_without_shorthand = "warn"
|
||||
range_minus_one = "warn"
|
||||
range_plus_one = "warn"
|
||||
rc_buffer = "warn"
|
||||
rc_mutex = "warn"
|
||||
read_zero_byte_vec = "warn"
|
||||
redundant_clone = "warn"
|
||||
redundant_closure_for_method_calls = "warn"
|
||||
redundant_else = "warn"
|
||||
redundant_pub_crate = "warn"
|
||||
redundant_test_prefix = "warn"
|
||||
ref_as_ptr = "warn"
|
||||
ref_binding_to_reference = "warn"
|
||||
ref_option = "warn"
|
||||
ref_option_ref = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
return_self_not_must_use = "warn"
|
||||
same_functions_in_if_condition = "warn"
|
||||
self_named_module_files = "warn"
|
||||
semicolon_if_nothing_returned = "warn"
|
||||
semicolon_inside_block = "warn"
|
||||
should_panic_without_expect = "warn"
|
||||
similar_names = "warn"
|
||||
significant_drop_in_scrutinee = "warn" # I thought this was fixed in the 2024 edition. watever
|
||||
significant_drop_tightening = "warn"
|
||||
single_char_pattern = "warn"
|
||||
single_option_map = "warn"
|
||||
stable_sort_primitive = "warn"
|
||||
str_split_at_newline = "warn"
|
||||
string_lit_as_bytes = "warn"
|
||||
string_lit_chars_any = "warn"
|
||||
string_slice = "warn"
|
||||
struct_excessive_bools = "warn"
|
||||
struct_field_names = "warn"
|
||||
suboptimal_flops = "warn"
|
||||
suspicious_operation_groupings = "warn"
|
||||
suspicious_xor_used_as_pow = "warn"
|
||||
tests_outside_test_module = "warn"
|
||||
trait_duplication_in_bounds = "warn"
|
||||
transmute_ptr_to_ptr = "warn"
|
||||
trivial_regex = "warn"
|
||||
trivially_copy_pass_by_ref = "warn"
|
||||
try_err = "warn"
|
||||
tuple_array_conversions = "warn"
|
||||
type_repetition_in_bounds = "warn"
|
||||
unchecked_time_subtraction = "warn"
|
||||
undocumented_unsafe_blocks = "warn"
|
||||
unicode_not_nfc = "warn"
|
||||
uninhabited_references = "warn"
|
||||
uninlined_format_args = "warn"
|
||||
unnecessary_box_returns = "warn"
|
||||
unnecessary_debug_formatting = "warn"
|
||||
unnecessary_join = "warn"
|
||||
unnecessary_literal_bound = "warn"
|
||||
unnecessary_safety_comment = "warn"
|
||||
unnecessary_safety_doc = "warn"
|
||||
unnecessary_self_imports = "warn"
|
||||
unnecessary_semicolon = "warn"
|
||||
unnecessary_struct_initialization = "warn"
|
||||
unnecessary_wraps = "warn"
|
||||
unnested_or_patterns = "warn"
|
||||
unreadable_literal = "warn"
|
||||
unsafe_derive_deserialize = "warn"
|
||||
unused_async = "warn"
|
||||
unused_peekable = "warn"
|
||||
unused_result_ok = "warn"
|
||||
unused_rounding = "warn"
|
||||
unused_self = "warn"
|
||||
unused_trait_names = "warn"
|
||||
use_self = "warn"
|
||||
used_underscore_binding = "warn"
|
||||
used_underscore_items = "warn"
|
||||
useless_let_if_seq = "warn"
|
||||
verbose_bit_mask = "warn"
|
||||
verbose_file_reads = "warn"
|
||||
volatile_composites = "warn"
|
||||
while_float = "warn"
|
||||
wildcard_dependencies = "warn"
|
||||
wildcard_imports = "warn"
|
||||
zero_sized_map_values = "warn"
|
||||
|
||||
@@ -31,6 +31,8 @@ If it turns out that you're missing one of these, it will fail to compile and te
|
||||
|
||||
The binary should then be found at `./target/release/tdf`.
|
||||
|
||||
You can also pull this in via [radicle](https://radicle.xyz) with `rad clone rad:zb11K1XGfQooopqEfwtCMyvbcyK1`
|
||||
|
||||
## Why in the world would you use this?
|
||||
|
||||
I dunno. Just for fun, mostly.
|
||||
@@ -39,4 +41,6 @@ I dunno. Just for fun, mostly.
|
||||
|
||||
Yeah, sure. Please do.
|
||||
|
||||
Please note, though, that all contributions will be treated as licensed under MPL-2.0.
|
||||
Please note, though, that:
|
||||
1. No AI-generated or AI-assisted or AI-viewed or AI-anythinged code will be accepted. "AI" is a plague upon this earth and I won't be caught dead pretending it's normal.
|
||||
2. All contributions will be treated as licensed under MPL-2.0 :)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use ratatui_image::picker::ProtocolType;
|
||||
|
||||
mod utils;
|
||||
|
||||
const BLACK: i32 = 0;
|
||||
@@ -12,5 +14,5 @@ async fn main() {
|
||||
.nth(1)
|
||||
.expect("Please enter a file to profile");
|
||||
|
||||
utils::render_doc(file, None, BLACK, WHITE).await;
|
||||
utils::render_doc(file, None, BLACK, WHITE, ProtocolType::Kitty).await;
|
||||
}
|
||||
|
||||
+82
-66
@@ -1,17 +1,15 @@
|
||||
mod utils;
|
||||
|
||||
use std::{
|
||||
hint::black_box,
|
||||
path::Path,
|
||||
time::{SystemTime, UNIX_EPOCH}
|
||||
};
|
||||
use std::{hint::black_box, path::Path};
|
||||
|
||||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main, profiler::Profiler};
|
||||
use futures_util::StreamExt;
|
||||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
||||
use futures_util::StreamExt as _;
|
||||
use ratatui_image::picker::ProtocolType;
|
||||
use tdf::{
|
||||
converter::{ConvertedPage, ConverterMsg},
|
||||
renderer::{PageInfo, RenderInfo, fill_default}
|
||||
};
|
||||
use tokio::runtime::Runtime;
|
||||
use utils::{
|
||||
RenderState, handle_converter_msg, handle_renderer_msg, render_doc, start_all_rendering,
|
||||
start_converting_loop, start_rendering_loop
|
||||
@@ -23,90 +21,103 @@ const FILES: [&str; 3] = [
|
||||
"benches/geotopo.pdf"
|
||||
];
|
||||
|
||||
const PROTOS: [ProtocolType; 3] = [
|
||||
ProtocolType::Kitty,
|
||||
ProtocolType::Sixel,
|
||||
ProtocolType::Iterm2
|
||||
];
|
||||
|
||||
const BLACK: i32 = 0;
|
||||
const WHITE: i32 = i32::from_be_bytes([0, 0xff, 0xff, 0xff]);
|
||||
|
||||
fn render_full(c: &mut Criterion) {
|
||||
for file in FILES {
|
||||
c.bench_with_input(BenchmarkId::new("render_full", file), &file, |b, &file| {
|
||||
b.to_async(tokio::runtime::Runtime::new().unwrap())
|
||||
.iter(|| render_doc(file, None, BLACK, WHITE))
|
||||
});
|
||||
fn for_all_combos(
|
||||
name: &'static str,
|
||||
mut f: impl FnMut(&Runtime, BenchmarkId, &'static str, ProtocolType)
|
||||
) {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
for proto in PROTOS {
|
||||
for file in FILES {
|
||||
f(
|
||||
&rt,
|
||||
BenchmarkId::new(name, format!("{file},{proto:?}")),
|
||||
file,
|
||||
proto
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_full(c: &mut Criterion) {
|
||||
for_all_combos("render_full", |rt, id, file, proto| {
|
||||
_ = c.bench_with_input(id, &file, |b, &file| {
|
||||
b.to_async(rt)
|
||||
.iter(|| render_doc(file, None, BLACK, WHITE, proto));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn render_to_first_page(c: &mut Criterion) {
|
||||
for file in FILES {
|
||||
c.bench_with_input(
|
||||
BenchmarkId::new("render_first_page", file),
|
||||
&file,
|
||||
|b, &file| {
|
||||
b.to_async(tokio::runtime::Runtime::new().unwrap())
|
||||
.iter(|| render_first_page(file, BLACK, WHITE))
|
||||
}
|
||||
);
|
||||
}
|
||||
for_all_combos("render_first_page", |rt, id, file, proto| {
|
||||
c.bench_with_input(id, &file, |b, &file| {
|
||||
b.to_async(rt)
|
||||
.iter(|| render_first_page(file, BLACK, WHITE, proto));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn only_converting(c: &mut Criterion) {
|
||||
for file in FILES {
|
||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
let all_rendered = runtime.block_on(render_all_files(file, BLACK, WHITE));
|
||||
for_all_combos("only_converting", |rt, id, file, proto| {
|
||||
let all_rendered = rt.block_on(render_all_files(file, BLACK, WHITE));
|
||||
|
||||
c.bench_with_input(
|
||||
BenchmarkId::new("only_converting", file),
|
||||
&(all_rendered, file),
|
||||
|b, (rendered, _)| {
|
||||
b.to_async(tokio::runtime::Runtime::new().unwrap())
|
||||
.iter_with_setup(|| rendered.clone(), convert_all_files)
|
||||
}
|
||||
);
|
||||
}
|
||||
c.bench_with_input(id, &all_rendered, |b, rendered| {
|
||||
b.to_async(rt)
|
||||
.iter_with_setup(|| rendered.clone(), |f| convert_all_files(f, proto));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
fn search_short_common(c: &mut Criterion) {
|
||||
for file in FILES {
|
||||
c.bench_with_input(
|
||||
BenchmarkId::new("search_short_common", file),
|
||||
&file,
|
||||
|b, &file| {
|
||||
b.to_async(tokio::runtime::Runtime::new().unwrap())
|
||||
.iter(|| render_doc(file, Some("an"), BLACK, WHITE))
|
||||
}
|
||||
);
|
||||
}
|
||||
for_all_combos("search_short_common", |rt, id, file, proto| {
|
||||
c.bench_with_input(id, &file, |b, &file| {
|
||||
b.to_async(rt)
|
||||
.iter(|| render_doc(file, Some("an"), BLACK, WHITE, proto))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn search_long_rare(c: &mut Criterion) {
|
||||
for file in FILES {
|
||||
c.bench_with_input(
|
||||
BenchmarkId::new("search_long_rare", file),
|
||||
&file,
|
||||
|b, &file| {
|
||||
b.to_async(tokio::runtime::Runtime::new().unwrap())
|
||||
.iter(|| render_doc(file, Some("this is long and rare"), BLACK, WHITE))
|
||||
}
|
||||
);
|
||||
}
|
||||
for_all_combos("search_long_rare", |rt, id, file, proto| {
|
||||
c.bench_with_input(id, &file, |b, &file| {
|
||||
b.to_async(rt)
|
||||
.iter(|| render_doc(file, Some("this is long and rare"), BLACK, WHITE, proto))
|
||||
});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
pub async fn render_first_page(path: impl AsRef<Path>, black: i32, white: i32) {
|
||||
pub async fn render_first_page(
|
||||
path: impl AsRef<Path>,
|
||||
black: i32,
|
||||
white: i32,
|
||||
proto: ProtocolType
|
||||
) {
|
||||
let RenderState {
|
||||
mut from_render_rx,
|
||||
mut from_converter_rx,
|
||||
mut pages,
|
||||
mut to_converter_tx,
|
||||
to_converter_tx,
|
||||
to_render_tx
|
||||
} = start_all_rendering(path, black, white);
|
||||
} = start_all_rendering(path, black, white, proto);
|
||||
|
||||
// we only want to render until the first page is ready to be printed
|
||||
while pages.iter().all(Option::is_none) {
|
||||
tokio::select! {
|
||||
Some(renderer_msg) = from_render_rx.next() => {
|
||||
handle_renderer_msg(renderer_msg, &mut pages, &mut to_converter_tx);
|
||||
handle_renderer_msg(renderer_msg, &mut pages, &to_converter_tx);
|
||||
},
|
||||
Some(converter_msg) = from_converter_rx.next() => {
|
||||
handle_converter_msg(converter_msg, &mut pages, &mut to_converter_tx);
|
||||
handle_converter_msg(converter_msg, &mut pages, &to_converter_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +140,7 @@ async fn render_all_files(path: &'static str, black: i32, white: i32) -> Vec<Pag
|
||||
let num = page.page_num;
|
||||
pages[num] = Some(page);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if pages.iter().all(Option::is_some) {
|
||||
break;
|
||||
@@ -140,9 +151,9 @@ async fn render_all_files(path: &'static str, black: i32, white: i32) -> Vec<Pag
|
||||
pages.into_iter().flatten().collect()
|
||||
}
|
||||
|
||||
async fn convert_all_files(files: Vec<PageInfo>) {
|
||||
async fn convert_all_files(files: Vec<PageInfo>, proto: ProtocolType) {
|
||||
let num_files = files.len();
|
||||
let (mut from_converter_rx, to_converter_tx) = start_converting_loop(num_files);
|
||||
let (mut from_converter_rx, to_converter_tx) = start_converting_loop(proto, num_files);
|
||||
|
||||
to_converter_tx
|
||||
.send(ConverterMsg::NumPages(num_files))
|
||||
@@ -181,10 +192,12 @@ async fn convert_all_files(files: Vec<PageInfo>) {
|
||||
black_box(converted);
|
||||
}
|
||||
|
||||
/*
|
||||
struct CpuProfiler;
|
||||
|
||||
impl Profiler for CpuProfiler {
|
||||
impl criterion::profiler::Profiler for CpuProfiler {
|
||||
fn start_profiling(&mut self, benchmark_id: &str, _: &std::path::Path) {
|
||||
use std::time::{SystemTime, UNIX_EPOCH}
|
||||
let file = format!(
|
||||
"./{}-{}.profile",
|
||||
benchmark_id.replace('/', "-"),
|
||||
@@ -200,10 +213,13 @@ impl Profiler for CpuProfiler {
|
||||
cpuprofiler::PROFILER.lock().unwrap().stop().unwrap();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
criterion_group!(
|
||||
name = benches;
|
||||
config = Criterion::default().sample_size(40).with_profiler(CpuProfiler);
|
||||
targets = render_full, render_to_first_page, only_converting, search_short_common, search_long_rare
|
||||
// config = Criterion::default().sample_size(40).with_profiler(CpuProfiler);
|
||||
config = Criterion::default().sample_size(40);
|
||||
// targets = render_full, render_to_first_page, only_converting, search_short_common, search_long_rare
|
||||
targets = render_full, render_to_first_page, only_converting
|
||||
);
|
||||
criterion_main!(benches);
|
||||
|
||||
+23
-10
@@ -13,7 +13,7 @@ use tdf::{
|
||||
pub fn handle_renderer_msg(
|
||||
msg: Result<RenderInfo, RenderError>,
|
||||
pages: &mut Vec<Option<ConvertedPage>>,
|
||||
to_converter_tx: &mut Sender<tdf::converter::ConverterMsg>
|
||||
to_converter_tx: &Sender<tdf::converter::ConverterMsg>
|
||||
) {
|
||||
match msg {
|
||||
Ok(RenderInfo::NumPages(num)) => {
|
||||
@@ -30,7 +30,7 @@ pub fn handle_renderer_msg(
|
||||
pub fn handle_converter_msg(
|
||||
msg: Result<ConvertedPage, RenderError>,
|
||||
pages: &mut [Option<ConvertedPage>],
|
||||
to_converter_tx: &mut Sender<ConverterMsg>
|
||||
to_converter_tx: &Sender<ConverterMsg>
|
||||
) {
|
||||
let page = msg.expect("Got error from converter");
|
||||
let num = page.num;
|
||||
@@ -106,7 +106,9 @@ pub fn start_rendering_loop(
|
||||
(from_render_rx, to_render_tx)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn start_converting_loop(
|
||||
proto: ProtocolType,
|
||||
prerender: usize
|
||||
) -> (
|
||||
RecvStream<'static, Result<ConvertedPage, RenderError>>,
|
||||
@@ -116,7 +118,7 @@ pub fn start_converting_loop(
|
||||
let (to_main_tx, from_converter_rx) = unbounded();
|
||||
|
||||
let mut picker = Picker::from_fontsize(FONT_SIZE);
|
||||
picker.set_protocol_type(ProtocolType::Kitty);
|
||||
picker.set_protocol_type(proto);
|
||||
|
||||
tokio::spawn(run_conversion_loop(
|
||||
to_main_tx,
|
||||
@@ -131,9 +133,14 @@ pub fn start_converting_loop(
|
||||
(from_converter_rx, to_converter_tx)
|
||||
}
|
||||
|
||||
pub fn start_all_rendering(path: impl AsRef<Path>, black: i32, white: i32) -> RenderState {
|
||||
pub fn start_all_rendering(
|
||||
path: impl AsRef<Path>,
|
||||
black: i32,
|
||||
white: i32,
|
||||
proto: ProtocolType
|
||||
) -> RenderState {
|
||||
let (from_render_rx, to_render_tx) = start_rendering_loop(path, black, white);
|
||||
let (from_converter_rx, to_converter_tx) = start_converting_loop(20);
|
||||
let (from_converter_rx, to_converter_tx) = start_converting_loop(proto, 20);
|
||||
|
||||
let pages: Vec<Option<ConvertedPage>> = Vec::new();
|
||||
|
||||
@@ -146,14 +153,20 @@ pub fn start_all_rendering(path: impl AsRef<Path>, black: i32, white: i32) -> Re
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn render_doc(path: impl AsRef<Path>, search_term: Option<&str>, black: i32, white: i32) {
|
||||
pub async fn render_doc(
|
||||
path: impl AsRef<Path>,
|
||||
search_term: Option<&str>,
|
||||
black: i32,
|
||||
white: i32,
|
||||
proto: ProtocolType
|
||||
) {
|
||||
let RenderState {
|
||||
mut from_render_rx,
|
||||
mut from_converter_rx,
|
||||
mut pages,
|
||||
mut to_converter_tx,
|
||||
to_converter_tx,
|
||||
to_render_tx
|
||||
} = start_all_rendering(path, black, white);
|
||||
} = start_all_rendering(path, black, white, proto);
|
||||
|
||||
if let Some(term) = search_term {
|
||||
to_render_tx
|
||||
@@ -164,10 +177,10 @@ pub async fn render_doc(path: impl AsRef<Path>, search_term: Option<&str>, black
|
||||
while pages.is_empty() || pages.iter().any(Option::is_none) {
|
||||
tokio::select! {
|
||||
Some(renderer_msg) = from_render_rx.next() => {
|
||||
handle_renderer_msg(renderer_msg, &mut pages, &mut to_converter_tx);
|
||||
handle_renderer_msg(renderer_msg, &mut pages, &to_converter_tx);
|
||||
},
|
||||
Some(converter_msg) = from_converter_rx.next() => {
|
||||
handle_converter_msg(converter_msg, &mut pages, &mut to_converter_tx);
|
||||
handle_converter_msg(converter_msg, &mut pages, &to_converter_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
}:
|
||||
pkgs.mkShell {
|
||||
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||
|
||||
buildInputs = [
|
||||
pkgs.rustPlatform.bindgenHook
|
||||
pkgs.cairo
|
||||
];
|
||||
}
|
||||
+15
-12
@@ -1,19 +1,19 @@
|
||||
use std::{
|
||||
num::{NonZeroU32, NonZeroUsize},
|
||||
num::NonZeroUsize,
|
||||
time::{SystemTime, UNIX_EPOCH}
|
||||
};
|
||||
|
||||
use flume::{Receiver, SendError, Sender, TryRecvError};
|
||||
use futures_util::stream::StreamExt;
|
||||
use futures_util::stream::StreamExt as _;
|
||||
use image::DynamicImage;
|
||||
use kittage::NumberOrId;
|
||||
use kittage::{NumberOrId, action::NONZERO_ONE};
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui_image::{
|
||||
Resize,
|
||||
picker::{Picker, ProtocolType},
|
||||
protocol::Protocol
|
||||
};
|
||||
use rayon::iter::ParallelIterator;
|
||||
use rayon::iter::ParallelIterator as _;
|
||||
|
||||
use crate::{
|
||||
renderer::{PageInfo, RenderError, fill_default},
|
||||
@@ -37,6 +37,7 @@ pub enum ConvertedImage {
|
||||
}
|
||||
|
||||
impl ConvertedImage {
|
||||
#[must_use]
|
||||
pub fn w_h(&self) -> (u16, u16) {
|
||||
match self {
|
||||
Self::Generic(prot) => {
|
||||
@@ -67,7 +68,7 @@ pub enum ConverterMsg {
|
||||
pub async fn run_conversion_loop(
|
||||
sender: Sender<Result<ConvertedPage, RenderError>>,
|
||||
receiver: Receiver<ConverterMsg>,
|
||||
mut picker: Picker,
|
||||
picker: Picker,
|
||||
prerender: usize,
|
||||
shms_work: bool
|
||||
) -> Result<(), SendError<Result<ConvertedPage, RenderError>>> {
|
||||
@@ -77,7 +78,7 @@ pub async fn run_conversion_loop(
|
||||
|
||||
fn next_page(
|
||||
images: &mut [Option<PageInfo>],
|
||||
picker: &mut Picker,
|
||||
picker: &Picker,
|
||||
page: usize,
|
||||
iteration: &mut usize,
|
||||
prerender: usize,
|
||||
@@ -126,7 +127,7 @@ pub async fn run_conversion_loop(
|
||||
.for_each(|(_, _, px)| px.0[2] = px.0[2].saturating_sub(u8::MAX / 2));
|
||||
},
|
||||
_ => unreachable!()
|
||||
};
|
||||
}
|
||||
|
||||
let img_area = Rect {
|
||||
width: page_info.img_data.cell_w,
|
||||
@@ -140,18 +141,20 @@ pub async fn run_conversion_loop(
|
||||
let rn = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_millis() % 1_000_000;
|
||||
.as_nanos() % 1_000_000;
|
||||
|
||||
let mut img = if shms_work {
|
||||
kittage::image::Image::shm_from(dyn_img, &format!("tdf_{pid}_{rn}_{page_num}"))
|
||||
kittage::image::Image::shm_from(dyn_img, &format!("/tdf_{pid}_{rn}_{page_num}"))
|
||||
.map_err(|e| {
|
||||
RenderError::Converting(format!("Couldn't write to shm: {e}"))
|
||||
RenderError::Converting(format!("Couldn't write to shm: {e:?}"))
|
||||
})?
|
||||
} else {
|
||||
kittage::image::Image::from(dyn_img)
|
||||
};
|
||||
|
||||
img.num_or_id = NumberOrId::Id(NonZeroU32::new(page_num as u32 + 1).unwrap());
|
||||
// if ur pdf has 4 billion pages then you deserve to suffer
|
||||
img.num_or_id = NumberOrId::Id(NONZERO_ONE.saturating_add(page_num as u32));
|
||||
|
||||
ConvertedImage::Kitty {
|
||||
img: MaybeTransferred::NotYet(img),
|
||||
cell_w: page_info.img_data.cell_w,
|
||||
@@ -214,7 +217,7 @@ pub async fn run_conversion_loop(
|
||||
|
||||
match next_page(
|
||||
&mut images,
|
||||
&mut picker,
|
||||
&picker,
|
||||
page,
|
||||
&mut iteration,
|
||||
prerender,
|
||||
|
||||
+2
-2
@@ -60,8 +60,8 @@ impl<W: Write> Write for DbgWriter<W> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_action<'image, 'data, 'es>(
|
||||
action: Action<'image, 'data>,
|
||||
pub async fn run_action<'es>(
|
||||
action: Action<'_, '_>,
|
||||
ev_stream: &'es mut EventStream
|
||||
) -> Result<ImageId, TransmitError<<&'es mut EventStream as AsyncInputReader>::Error>> {
|
||||
let writer = DbgWriter {
|
||||
|
||||
@@ -27,6 +27,7 @@ pub struct ScaledResult {
|
||||
scale_factor: f32
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn scale_img_for_area(
|
||||
(img_width, img_height): (f32, f32),
|
||||
(area_width, area_height): (f32, f32),
|
||||
|
||||
+107
-66
@@ -5,8 +5,11 @@ use core::{
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::OsString,
|
||||
io::{BufReader, Read, Stdout, Write, stdout},
|
||||
path::PathBuf
|
||||
io::{BufReader, Read as _, Stdout, Write as _, stdout},
|
||||
mem,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
time::Duration
|
||||
};
|
||||
|
||||
use crossterm::{
|
||||
@@ -17,15 +20,16 @@ use crossterm::{
|
||||
enable_raw_mode, window_size
|
||||
}
|
||||
};
|
||||
use debounce::EventDebouncer;
|
||||
use flexi_logger::FileSpec;
|
||||
use flume::{Sender, r#async::RecvStream};
|
||||
use futures_util::{FutureExt, stream::StreamExt};
|
||||
use futures_util::{FutureExt as _, stream::StreamExt as _};
|
||||
use kittage::{
|
||||
action::Action,
|
||||
delete::{ClearOrDelete, DeleteConfig, WhichToDelete},
|
||||
error::{TerminalError, TransmitError}
|
||||
};
|
||||
use notify::{Event, EventKind, RecursiveMode, Watcher};
|
||||
use notify::{Event, EventKind, RecursiveMode, Watcher as _};
|
||||
use ratatui::{Terminal, backend::CrosstermBackend};
|
||||
use ratatui_image::{
|
||||
FontSize,
|
||||
@@ -35,7 +39,7 @@ use tdf::{
|
||||
PrerenderLimit,
|
||||
converter::{ConvertedPage, ConverterMsg, run_conversion_loop},
|
||||
kitty::{KittyDisplay, display_kitty_images, do_shms_work, run_action},
|
||||
renderer::{self, RenderError, RenderInfo, RenderNotif},
|
||||
renderer::{self, MUPDF_BLACK, MUPDF_WHITE, RenderError, RenderInfo, RenderNotif},
|
||||
tui::{BottomMessage, InputAction, MessageSetting, Tui}
|
||||
};
|
||||
|
||||
@@ -57,17 +61,20 @@ impl std::fmt::Debug for WrappedErr {
|
||||
impl std::error::Error for WrappedErr {}
|
||||
|
||||
fn reset_term() {
|
||||
_ = disable_raw_mode();
|
||||
_ = execute!(
|
||||
std::io::stdout(),
|
||||
LeaveAlternateScreen,
|
||||
crossterm::cursor::Show,
|
||||
crossterm::event::DisableMouseCapture
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), WrappedErr> {
|
||||
inner_main().await.inspect_err(|_| reset_term())
|
||||
let result = inner_main().await;
|
||||
reset_term();
|
||||
result
|
||||
}
|
||||
|
||||
async fn inner_main() -> Result<(), WrappedErr> {
|
||||
@@ -80,6 +87,8 @@ async fn inner_main() -> Result<(), WrappedErr> {
|
||||
#[cfg(feature = "tracing")]
|
||||
console_subscriber::init();
|
||||
|
||||
const DEFAULT_DEBOUNCE_DELAY: Duration = Duration::from_millis(50);
|
||||
|
||||
let flags = xflags::parse_or_exit! {
|
||||
/// Display the pdf with the pages starting at the right hand size and moving left and
|
||||
/// adjust input keys to match
|
||||
@@ -88,6 +97,9 @@ async fn inner_main() -> Result<(), WrappedErr> {
|
||||
optional -m,--max-wide max_wide: NonZeroUsize
|
||||
/// Fullscreen the pdf (hide document name, page count, etc)
|
||||
optional -f,--fullscreen
|
||||
/// The time to wait for the file to stop changing before reloading, in milliseconds.
|
||||
/// Defaults to 50ms.
|
||||
optional --reload-delay reload_delay: u64
|
||||
/// The number of pages to prerender surrounding the currently-shown page; 0 means no
|
||||
/// limit. By default, there is no limit.
|
||||
optional -p,--prerender prerender: usize
|
||||
@@ -116,28 +128,42 @@ async fn inner_main() -> Result<(), WrappedErr> {
|
||||
.canonicalize()
|
||||
.map_err(|e| WrappedErr(format!("Cannot canonicalize provided file: {e}").into()))?;
|
||||
|
||||
let black =
|
||||
parse_color_to_i32(flags.black_color.as_deref().unwrap_or("000000")).map_err(|e| {
|
||||
WrappedErr(
|
||||
format!("Couldn't parse black color: {e} - is it formatted like a CSS color?")
|
||||
let black = flags
|
||||
.black_color
|
||||
.as_deref()
|
||||
.map(|color| {
|
||||
parse_color_to_i32(color).map_err(|e| {
|
||||
WrappedErr(
|
||||
format!(
|
||||
"Couldn't parse black color {color:?}: {e} - is it formatted like a CSS color?"
|
||||
)
|
||||
.into()
|
||||
)
|
||||
})?;
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(MUPDF_BLACK);
|
||||
|
||||
let white =
|
||||
parse_color_to_i32(flags.white_color.as_deref().unwrap_or("FFFFFF")).map_err(|e| {
|
||||
WrappedErr(
|
||||
format!("Couldn't parse white color: {e} - is it formatted like a CSS color?")
|
||||
let white = flags
|
||||
.white_color
|
||||
.as_deref()
|
||||
.map(|color| {
|
||||
parse_color_to_i32(color).map_err(|e| {
|
||||
WrappedErr(
|
||||
format!(
|
||||
"Couldn't parse white color {color:?}: {e} - is it formatted like a CSS color?"
|
||||
)
|
||||
.into()
|
||||
)
|
||||
})?;
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(MUPDF_WHITE);
|
||||
|
||||
// need to keep it around throughout the lifetime of the program, but don't rly need to use it.
|
||||
// Just need to make sure it doesn't get dropped yet.
|
||||
let mut maybe_logger = None;
|
||||
|
||||
if std::env::var("RUST_LOG").is_ok() {
|
||||
maybe_logger = Some(
|
||||
let maybe_logger = if std::env::var("RUST_LOG").is_ok() {
|
||||
Some(
|
||||
flexi_logger::Logger::try_with_env()
|
||||
.map_err(|e| WrappedErr(format!("Couldn't create initial logger: {e}").into()))?
|
||||
.log_to_file(FileSpec::try_from("./debug.log").map_err(|e| {
|
||||
@@ -145,8 +171,10 @@ async fn inner_main() -> Result<(), WrappedErr> {
|
||||
})?)
|
||||
.start()
|
||||
.map_err(|e| WrappedErr(format!("Can't start logger: {e}").into()))?
|
||||
);
|
||||
}
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (watch_to_render_tx, render_rx) = flume::unbounded();
|
||||
let to_renderer = watch_to_render_tx.clone();
|
||||
@@ -158,8 +186,11 @@ async fn inner_main() -> Result<(), WrappedErr> {
|
||||
watch_to_tui_tx,
|
||||
watch_to_render_tx,
|
||||
path.file_name()
|
||||
.ok_or(WrappedErr("Path does not have a last component??".into()))?
|
||||
.to_owned()
|
||||
.ok_or_else(|| WrappedErr("Path does not have a last component??".into()))?
|
||||
.to_owned(),
|
||||
flags
|
||||
.reload_delay
|
||||
.map_or(DEFAULT_DEBOUNCE_DELAY, Duration::from_millis)
|
||||
))
|
||||
.map_err(|e| WrappedErr(format!("Couldn't start watching the provided file: {e}").into()))?;
|
||||
|
||||
@@ -330,17 +361,7 @@ async fn inner_main() -> Result<(), WrappedErr> {
|
||||
)
|
||||
})?;
|
||||
|
||||
execute!(
|
||||
term.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
crossterm::cursor::Show,
|
||||
crossterm::event::DisableMouseCapture
|
||||
)
|
||||
.unwrap();
|
||||
disable_raw_mode().unwrap();
|
||||
|
||||
drop(maybe_logger);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -461,38 +482,58 @@ async fn enter_redraw_loop(
|
||||
fn on_notify_ev(
|
||||
to_tui_tx: flume::Sender<Result<RenderInfo, RenderError>>,
|
||||
to_render_tx: flume::Sender<RenderNotif>,
|
||||
file_name: OsString
|
||||
file_name: OsString,
|
||||
debounce_delay: Duration
|
||||
) -> impl Fn(notify::Result<Event>) {
|
||||
move |res| match res {
|
||||
// If we get an error here, and then an error sending, everything's going wrong. Just give
|
||||
// up lol.
|
||||
Err(e) => to_tui_tx.send(Err(RenderError::Notify(e))).unwrap(),
|
||||
// TODO: Should we match EventKind::Rename and propogate that so that the other parts of the
|
||||
// process know that too? Or should that be
|
||||
Ok(ev) => {
|
||||
// We only watch the parent directory (see the comment above `watcher.watch` in `fn
|
||||
// main`) so we need to filter out events to only ones that pertain to the single file
|
||||
// we care about
|
||||
if !ev
|
||||
.paths
|
||||
.iter()
|
||||
.any(|path| path.file_name().is_some_and(|f| f == file_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let last_event: Mutex<Result<(), RenderError>> = Mutex::new(Ok(()));
|
||||
let last_event = Arc::new(last_event);
|
||||
|
||||
match ev.kind {
|
||||
EventKind::Access(_) => (),
|
||||
EventKind::Remove(_) => to_tui_tx
|
||||
.send(Err(RenderError::Converting("File was deleted".into())))
|
||||
.unwrap(),
|
||||
let debouncer = EventDebouncer::new(debounce_delay, {
|
||||
let last_event = last_event.clone();
|
||||
move |()| {
|
||||
let event = mem::replace(&mut *last_event.lock().unwrap(), Ok(()));
|
||||
match event {
|
||||
// This shouldn't fail to send unless the receiver gets disconnected. If that's
|
||||
// happened, then like the main thread has panicked or something, so it doesn't matter
|
||||
// we don't handle the error here.
|
||||
EventKind::Other | EventKind::Any | EventKind::Create(_) | EventKind::Modify(_) =>
|
||||
to_render_tx.send(RenderNotif::Reload).unwrap(),
|
||||
Ok(()) => to_render_tx.send(RenderNotif::Reload).unwrap(),
|
||||
// If we get an error here, and then an error sending, everything's going wrong. Just give
|
||||
// up lol.
|
||||
Err(e) => to_tui_tx.send(Err(e)).unwrap()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
move |res| {
|
||||
let event = match res {
|
||||
Err(e) => Err(RenderError::Notify(e)),
|
||||
|
||||
// TODO: Should we match EventKind::Rename and propogate that so that the other parts of the
|
||||
// process know that too? Or should that be
|
||||
Ok(ev) => {
|
||||
// We only watch the parent directory (see the comment above `watcher.watch` in `fn
|
||||
// main`) so we need to filter out events to only ones that pertain to the single file
|
||||
// we care about
|
||||
if !ev
|
||||
.paths
|
||||
.iter()
|
||||
.any(|path| path.file_name().is_some_and(|f| f == file_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
match ev.kind {
|
||||
EventKind::Access(_) => return,
|
||||
EventKind::Remove(_) => Err(RenderError::Converting("File was deleted".into())),
|
||||
EventKind::Other
|
||||
| EventKind::Any
|
||||
| EventKind::Create(_)
|
||||
| EventKind::Modify(_) => Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
*last_event.lock().unwrap() = event;
|
||||
debouncer.put(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,18 +590,18 @@ fn get_font_size_through_stdio() -> Result<(u16, u16), WrappedErr> {
|
||||
));
|
||||
};
|
||||
|
||||
let h = h.parse::<u16>().map_err(|_| {
|
||||
let h = h.parse::<u16>().map_err(|e| {
|
||||
WrappedErr(
|
||||
format!(
|
||||
"Your terminal said its height is {h}, but that is not a 16-bit unsigned integer"
|
||||
"Your terminal said its height is {h}, but that is not a 16-bit unsigned integer: {e}"
|
||||
)
|
||||
.into()
|
||||
)
|
||||
})?;
|
||||
let w = w.parse::<u16>().map_err(|_| {
|
||||
let w = w.parse::<u16>().map_err(|e| {
|
||||
WrappedErr(
|
||||
format!(
|
||||
"Your terminal said its width is {w}, but that is not a 16-bit unsigned integer"
|
||||
"Your terminal said its width is {w}, but that is not a 16-bit unsigned integer: {e}"
|
||||
)
|
||||
.into()
|
||||
)
|
||||
|
||||
+22
-11
@@ -57,8 +57,8 @@ struct PrevRender {
|
||||
num_search_found: Option<usize>
|
||||
}
|
||||
|
||||
const MUPDF_BLACK: i32 = 0;
|
||||
const MUPDF_WHITE: i32 = i32::from_be_bytes([0, 0xff, 0xff, 0xff]);
|
||||
pub const MUPDF_BLACK: i32 = 0;
|
||||
pub const MUPDF_WHITE: i32 = i32::from_be_bytes([0, 0xff, 0xff, 0xff]);
|
||||
|
||||
#[inline]
|
||||
pub fn fill_default<T: Default>(vec: &mut Vec<T>, size: usize) {
|
||||
@@ -78,7 +78,7 @@ pub fn fill_default<T: Default>(vec: &mut Vec<T>, size: usize) {
|
||||
// We're allowing passing by value here because this is only called once, at the beginning of the
|
||||
// program, and the arguments that 'should' be passed by value (`receiver` and `size`) would
|
||||
// probably be more performant if accessed by-value instead of through a reference. Probably.
|
||||
#[allow(clippy::needless_pass_by_value, clippy::too_many_arguments)]
|
||||
#[expect(clippy::needless_pass_by_value, clippy::too_many_arguments)]
|
||||
pub fn start_rendering(
|
||||
path: &str,
|
||||
sender: Sender<Result<RenderInfo, RenderError>>,
|
||||
@@ -116,7 +116,7 @@ pub fn start_rendering(
|
||||
// temporarily removed to facilitate a save or something like that)
|
||||
while let Ok(msg) = receiver.recv() {
|
||||
// and once that comes, just try to reload again
|
||||
if let RenderNotif::Reload = msg {
|
||||
if matches!(msg, RenderNotif::Reload) {
|
||||
continue 'reload;
|
||||
}
|
||||
}
|
||||
@@ -152,8 +152,8 @@ pub fn start_rendering(
|
||||
|
||||
sender.send(Ok(RenderInfo::NumPages(n_pages.get())))?;
|
||||
|
||||
// We're using this vec of bools to indicate which page numbers have already been rendered,
|
||||
// to support people jumping to specific pages and having quick rendering results. We
|
||||
// We're using this vec to indicate which page numbers have already been rendered, to
|
||||
// support people jumping to specific pages and having quick rendering results. We
|
||||
// `split_at_mut` at 0 initially (which bascially makes `right == rendered && left == []`),
|
||||
// doing basically nothing, but if we get a notification that something has been jumped to,
|
||||
// then we can split at that page and render at both sides of it
|
||||
@@ -277,7 +277,7 @@ pub fn start_rendering(
|
||||
// we only want to continue if one of the following is met:
|
||||
// 1. It failed to render last time (we want to retry)
|
||||
// 2. The `contained_term` is set to Unknown, meaning that we need to at least
|
||||
// check if it contains the current term to see if it needs a re-render
|
||||
// check if it contains the current term to see if it needs a re-render
|
||||
if rendered.successful && rendered.num_search_found.is_some() {
|
||||
continue;
|
||||
}
|
||||
@@ -313,7 +313,7 @@ pub fn start_rendering(
|
||||
if let Err(e) = ctx.pixmap.write_to(&mut pixels, mupdf::ImageFormat::PNM) {
|
||||
sender.send(Err(RenderError::Doc(e)))?;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
|
||||
log::debug!("got pixmap for page {page_num} with WxH {w}x{h}");
|
||||
|
||||
@@ -341,7 +341,7 @@ pub fn start_rendering(
|
||||
Err(TryRecvError::Disconnected) => return Ok(()),
|
||||
Ok(notif) => handle_notif!(notif),
|
||||
Err(TryRecvError::Empty) => ()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Now, if we have a search term, we want to look through the rest of the document past
|
||||
@@ -434,7 +434,7 @@ pub fn start_rendering(
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
handle_notif!(msg)
|
||||
handle_notif!(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,9 +565,20 @@ struct PopOnNext<'a> {
|
||||
inner: &'a mut VecDeque<usize>
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PopOnNext<'a> {
|
||||
impl Iterator for PopOnNext<'_> {
|
||||
type Item = usize;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.pop_front()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let l = self.len();
|
||||
(l, Some(l))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for PopOnNext<'_> {
|
||||
fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub struct Skip {
|
||||
}
|
||||
|
||||
impl Skip {
|
||||
#[must_use]
|
||||
pub fn new(skip: bool) -> Self {
|
||||
Self { skip }
|
||||
}
|
||||
@@ -45,6 +46,7 @@ impl InterleavedAroundWithMax {
|
||||
/// the following must hold or else this is liable to panic or produce nonsense values:
|
||||
/// - inclusive_min < exclusive_max
|
||||
/// - inclusive_min <= around <= exclusive_max
|
||||
#[must_use]
|
||||
pub fn new(around: usize, inclusive_min: usize, exclusive_max: NonZeroUsize) -> Self {
|
||||
Self {
|
||||
around,
|
||||
|
||||
+435
-242
@@ -16,6 +16,7 @@ use nix::{
|
||||
use ratatui::{
|
||||
Frame,
|
||||
layout::{Constraint, Flex, Layout, Position, Rect},
|
||||
prelude::{Line, Text},
|
||||
style::{Color, Style},
|
||||
symbols::border,
|
||||
text::Span,
|
||||
@@ -77,8 +78,8 @@ struct PageConstraints {
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct Zoom {
|
||||
// just how much 'zoom' you have. Doesn't relate to anything specific yet, except that 0 means
|
||||
// it fills the screen (instead of fits)
|
||||
// just how much 'zoom' you have. 0 means it fills the screen (instead of fits), such
|
||||
// that one axis is fully on-screen
|
||||
level: i16,
|
||||
// how many terminal-cells worth of content overflow the left side of the screen (and are thus
|
||||
// not displayed)
|
||||
@@ -87,6 +88,79 @@ struct Zoom {
|
||||
// not displayed)
|
||||
cell_pan_from_top: u16
|
||||
}
|
||||
impl Zoom {
|
||||
/// Returns the zoom factor, where 1 is the default and means fill-screen
|
||||
fn factor(&self) -> f32 {
|
||||
// TODO: Make these configurable once we have a good way to set options after startup
|
||||
const ZOOM_RATE: f32 = 1.1;
|
||||
const ZOOM_RATE_GRANULAR: f32 = 1.05;
|
||||
|
||||
if self.level > 0 {
|
||||
ZOOM_RATE.powi(self.level.into())
|
||||
} else {
|
||||
// use a more granular zoom rate for the steps between fit-screen and fill-screen
|
||||
ZOOM_RATE_GRANULAR.powi(self.level.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn step_in(&mut self) {
|
||||
self.level = self.level.saturating_add(1);
|
||||
}
|
||||
fn step_out(&mut self) {
|
||||
self.level = self.level.saturating_sub(1);
|
||||
}
|
||||
|
||||
// TODO: Make this configurable, maybe allow fractional steps?
|
||||
// With fractional steps, it might also be a good idea to have these
|
||||
// have the same ratio as the font aspect ratio.
|
||||
const PAN_STEP_X: i16 = 2;
|
||||
const PAN_STEP_Y: i16 = 1;
|
||||
|
||||
fn pan(&mut self, direction: Direction) {
|
||||
let (target, sign) = match direction {
|
||||
Direction::Up => (&mut self.cell_pan_from_top, -1),
|
||||
Direction::Down => (&mut self.cell_pan_from_top, 1),
|
||||
Direction::Left => (&mut self.cell_pan_from_left, -1),
|
||||
Direction::Right => (&mut self.cell_pan_from_left, 1)
|
||||
};
|
||||
let step = if direction.is_vertical() {
|
||||
Self::PAN_STEP_Y
|
||||
} else {
|
||||
Self::PAN_STEP_X
|
||||
};
|
||||
*target = target.saturating_add_signed(sign * step);
|
||||
}
|
||||
fn pan_bottom(&mut self) {
|
||||
self.cell_pan_from_top = 0;
|
||||
}
|
||||
fn pan_top(&mut self) {
|
||||
self.cell_pan_from_top = u16::MAX;
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
impl Direction {
|
||||
/// Flips the directions for vertical and horizonal panning.
|
||||
fn flip_mouse_xy(self) -> Self {
|
||||
match self {
|
||||
Self::Up => Self::Left,
|
||||
Self::Left => Self::Up,
|
||||
Self::Down => Self::Right,
|
||||
Self::Right => Self::Down
|
||||
}
|
||||
}
|
||||
fn is_vertical(self) -> bool {
|
||||
match self {
|
||||
Self::Up | Self::Down => true,
|
||||
Self::Left | Self::Right => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This seems like a kinda weird struct because it holds two optionals but any representation
|
||||
// within it is valid; I think it's the best way to represent it
|
||||
@@ -108,7 +182,8 @@ pub struct RenderLayout {
|
||||
}
|
||||
|
||||
impl Tui {
|
||||
pub fn new(name: String, max_wide: Option<NonZeroUsize>, r_to_l: bool, is_kitty: bool) -> Tui {
|
||||
#[must_use]
|
||||
pub fn new(name: String, max_wide: Option<NonZeroUsize>, r_to_l: bool, is_kitty: bool) -> Self {
|
||||
Self {
|
||||
name,
|
||||
page: 0,
|
||||
@@ -123,6 +198,7 @@ impl Tui {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn main_layout(frame: &Frame<'_>, fullscreened: bool) -> RenderLayout {
|
||||
if fullscreened {
|
||||
RenderLayout {
|
||||
@@ -147,6 +223,129 @@ impl Tui {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_zoomed<'s>(
|
||||
// area of the 'fit-screen' page
|
||||
mut img_area: Rect,
|
||||
font_size: FontSize,
|
||||
zoom: &mut Zoom,
|
||||
img: &'s mut MaybeTransferred,
|
||||
page_num: usize,
|
||||
img_cell_w: u16,
|
||||
img_cell_h: u16
|
||||
) -> KittyDisplay<'s> {
|
||||
log::debug!("zoom is {zoom:#?}");
|
||||
log::debug!("page area is {img_area:#?}");
|
||||
log::debug!("img dimensions are {img_cell_w}x{img_cell_h}");
|
||||
|
||||
// Dimensions of the section of the image to be displayed.
|
||||
// Kittage calls this the "image area to display".
|
||||
// We need to shrink this or the page area in order to zoom in or out,
|
||||
// respectively.
|
||||
let mut img_section_w = f32::from(img_cell_w);
|
||||
let mut img_section_h = f32::from(img_cell_h);
|
||||
|
||||
let zoom_factor = zoom.factor();
|
||||
|
||||
if zoom_factor >= 1.0 {
|
||||
// Use a smaller section of the image. This efficively zooms into that section.
|
||||
img_section_w /= zoom_factor;
|
||||
img_section_h /= zoom_factor;
|
||||
} else {
|
||||
// Shrink the page area, such that the fill-screen conversion
|
||||
// will zoom out of the image.
|
||||
let initial_page_w = f32::from(img_area.width);
|
||||
let initial_page_h = f32::from(img_area.height);
|
||||
|
||||
// how many pages the image is wide/high
|
||||
let img_page_w_ratio = img_section_w / initial_page_w;
|
||||
let img_page_h_ratio = img_section_h / initial_page_h;
|
||||
|
||||
let shrink_move_page = |dim: &mut u16, pos: &mut u16, axis_zoom_factor: f32| {
|
||||
let old_dim = *dim;
|
||||
// The axis zoom factor tells us what portion of the axis
|
||||
// we need to show.
|
||||
*dim = (f32::from(*dim) * axis_zoom_factor) as u16;
|
||||
|
||||
*pos += old_dim
|
||||
.checked_sub(*dim)
|
||||
.expect("zooming out should shrink the image")
|
||||
/ 2;
|
||||
};
|
||||
|
||||
// TODO: Detect max zoom-out in zoom levels
|
||||
if img_page_w_ratio < img_page_h_ratio {
|
||||
// vertical scroll / tall image. zooming out means decreasing the width of the page area
|
||||
shrink_move_page(
|
||||
&mut img_area.width,
|
||||
&mut img_area.x,
|
||||
// disallow zooming out past fit-screen
|
||||
zoom_factor.max(1.0 / img_page_h_ratio)
|
||||
);
|
||||
} else {
|
||||
// horizontal scroll / wide image. zooming out means decreasing the width of the page area
|
||||
shrink_move_page(
|
||||
&mut img_area.height,
|
||||
&mut img_area.y,
|
||||
// disallow zooming out past fit-screen
|
||||
zoom_factor.max(1.0 / img_page_w_ratio)
|
||||
);
|
||||
}
|
||||
}
|
||||
log::debug!("after adjustment, page area is {img_area:#?}");
|
||||
|
||||
// Crop the image such that in the end, the aspect ratio of the section
|
||||
// is the same as that of the page area. This effectively performs the
|
||||
// conversion to fill-screen.
|
||||
// Note that this only works because cell_w, cell_h is in fit-screen
|
||||
// format, i.e. the cell size and the page area already share at
|
||||
// least one dimension.
|
||||
{
|
||||
let page_area_w = f32::from(img_area.width);
|
||||
let page_area_h = f32::from(img_area.height);
|
||||
|
||||
// how many pages the image is wide/high
|
||||
// Note that this is not the same as during the
|
||||
// zoom-out calculation, since it changed the page
|
||||
// dimensions.
|
||||
let img_page_w_ratio = img_section_w / page_area_w;
|
||||
let img_page_h_ratio = img_section_h / page_area_h;
|
||||
|
||||
if img_page_w_ratio < img_page_h_ratio {
|
||||
img_section_h = page_area_h * img_page_w_ratio;
|
||||
} else {
|
||||
img_section_w = page_area_w * img_page_h_ratio;
|
||||
}
|
||||
}
|
||||
|
||||
let width = (img_section_w * f32::from(font_size.0)) as u32;
|
||||
let height = (img_section_h * f32::from(font_size.1)) as u32;
|
||||
|
||||
zoom.cell_pan_from_left = zoom
|
||||
.cell_pan_from_left
|
||||
.min(img_cell_w.saturating_sub(img_section_w.ceil() as u16));
|
||||
zoom.cell_pan_from_top = zoom
|
||||
.cell_pan_from_top
|
||||
.min(img_cell_h.saturating_sub(img_section_h.ceil() as u16));
|
||||
|
||||
KittyDisplay::DisplayImages(vec![KittyReadyToDisplay {
|
||||
img,
|
||||
page_num,
|
||||
pos: Position {
|
||||
x: img_area.x,
|
||||
y: img_area.y
|
||||
},
|
||||
display_loc: DisplayLocation {
|
||||
x: u32::from(zoom.cell_pan_from_left) * u32::from(font_size.0),
|
||||
y: u32::from(zoom.cell_pan_from_top) * u32::from(font_size.1),
|
||||
width,
|
||||
height,
|
||||
columns: img_area.width,
|
||||
rows: img_area.height,
|
||||
..DisplayLocation::default()
|
||||
}
|
||||
}])
|
||||
}
|
||||
|
||||
// TODO: Make a way to fill the width of the screen with one page and scroll down to view it
|
||||
#[must_use]
|
||||
pub fn render<'s>(
|
||||
@@ -180,166 +379,120 @@ impl Tui {
|
||||
// be written and set to skip it so that ratatui doesn't spend a lot of time diffing it
|
||||
// each re-render
|
||||
frame.render_widget(Skip::new(true), img_area);
|
||||
KittyDisplay::NoChange
|
||||
} else {
|
||||
if let Some(ref mut zoom) = self.zoom {
|
||||
// yes this is ugly and I hate it. it's due to the limitations that currently exist
|
||||
// in the borrow checker. Once `-Zpolonius=next` is stabilized, we can rework this
|
||||
// to look like what we expect.
|
||||
// See https://github.com/rust-lang/rfcs/blob/master/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions
|
||||
// You can also rewrite this to just if an `if let` and run it under
|
||||
// `RUSTFLAGS="-Zpolonius=next"` and see that it works
|
||||
if self.rendered[self.page]
|
||||
.img
|
||||
.as_ref()
|
||||
.is_some_and(|c| matches!(c, ConvertedImage::Kitty { .. }))
|
||||
{
|
||||
let Some(ConvertedImage::Kitty {
|
||||
ref mut img,
|
||||
cell_w,
|
||||
cell_h
|
||||
}) = self.rendered[self.page].img
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
return KittyDisplay::NoChange;
|
||||
}
|
||||
|
||||
log::debug!("zoom is now {zoom:#?}");
|
||||
log::debug!("img_area is {img_area:#?}");
|
||||
if let Some(ref mut zoom) = self.zoom {
|
||||
// yes this is ugly and I hate it. it's due to the limitations that currently exist
|
||||
// in the borrow checker. Once `-Zpolonius=next` is stabilized, we can rework this
|
||||
// to look like what we expect.
|
||||
// See https://github.com/rust-lang/rfcs/blob/master/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions
|
||||
// You can also rewrite this to just if an `if let` and run it under
|
||||
// `RUSTFLAGS="-Zpolonius=next"` and see that it works
|
||||
if self.rendered[self.page]
|
||||
.img
|
||||
.as_ref()
|
||||
.is_some_and(|c| matches!(c, ConvertedImage::Kitty { .. }))
|
||||
{
|
||||
let Some(ConvertedImage::Kitty {
|
||||
ref mut img,
|
||||
cell_w,
|
||||
cell_h
|
||||
}) = self.rendered[self.page].img
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if zoom.level < 0 {
|
||||
img_area = Rect {
|
||||
width: img_area
|
||||
.width
|
||||
.saturating_sub((zoom.level * 2).unsigned_abs())
|
||||
.max(1),
|
||||
x: img_area.x + (zoom.level.unsigned_abs().min(img_area.width / 2)),
|
||||
..img_area
|
||||
}
|
||||
}
|
||||
self.last_render = LastRender {
|
||||
rect: size,
|
||||
pages_shown: 1,
|
||||
unused_width: 0
|
||||
};
|
||||
return Self::render_zoomed(
|
||||
img_area, font_size, zoom, img, self.page, cell_w, cell_h
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("after adjustment, img_area is {img_area:#?}");
|
||||
|
||||
// Ugh I don't like this logic. I wish we could simplify it.
|
||||
let img_width = f32::from(cell_w);
|
||||
let img_height = f32::from(cell_h);
|
||||
let img_area_width = f32::from(img_area.width);
|
||||
let img_area_height = f32::from(img_area.height);
|
||||
let available_to_real_width_ratio = img_area_width / img_width;
|
||||
let available_to_real_height_ratio = img_area_height / img_height;
|
||||
|
||||
let (new_cell_width, new_cell_height) =
|
||||
if available_to_real_width_ratio > available_to_real_height_ratio {
|
||||
(img_width, img_area_height / available_to_real_width_ratio)
|
||||
} else {
|
||||
(img_area_width / available_to_real_height_ratio, img_height)
|
||||
};
|
||||
|
||||
log::debug!("new_cell stuff is {new_cell_width}x{new_cell_height}");
|
||||
|
||||
let width = (new_cell_width * f32::from(font_size.0)) as u32;
|
||||
let height = (new_cell_height * f32::from(font_size.1)) as u32;
|
||||
|
||||
self.last_render = LastRender {
|
||||
rect: size,
|
||||
pages_shown: 1,
|
||||
unused_width: 0
|
||||
};
|
||||
|
||||
zoom.cell_pan_from_left = zoom
|
||||
.cell_pan_from_left
|
||||
.min(cell_w.saturating_sub(new_cell_width as u16));
|
||||
zoom.cell_pan_from_top = zoom
|
||||
.cell_pan_from_top
|
||||
.min(cell_h.saturating_sub(new_cell_height as u16));
|
||||
|
||||
return KittyDisplay::DisplayImages(vec![KittyReadyToDisplay {
|
||||
img,
|
||||
page_num: self.page,
|
||||
pos: Position {
|
||||
x: img_area.x,
|
||||
y: img_area.y
|
||||
},
|
||||
display_loc: DisplayLocation {
|
||||
x: u32::from(zoom.cell_pan_from_left) * u32::from(font_size.0),
|
||||
y: u32::from(zoom.cell_pan_from_top) * u32::from(font_size.1),
|
||||
width,
|
||||
height,
|
||||
columns: img_area.width,
|
||||
rows: img_area.height,
|
||||
..DisplayLocation::default()
|
||||
}
|
||||
}]);
|
||||
// here we calculate how many pages can fit in the available area.
|
||||
let mut test_area_w = img_area.width;
|
||||
// go through our pages, starting at the first one we want to view
|
||||
let mut page_sizes = self.rendered[self.page..]
|
||||
.iter_mut()
|
||||
// and get this to represent a count of how many we're looking at so far to render
|
||||
.enumerate()
|
||||
// and only take as many as are ready to be rendered
|
||||
.take_while(|(idx, page)| {
|
||||
let mut take = page.img.is_some();
|
||||
if let Some(max) = self.page_constraints.max_wide {
|
||||
take &= *idx < max.get();
|
||||
}
|
||||
};
|
||||
|
||||
// here we calculate how many pages can fit in the available area.
|
||||
let mut test_area_w = img_area.width;
|
||||
// go through our pages, starting at the first one we want to view
|
||||
let mut page_widths = self.rendered[self.page..]
|
||||
.iter_mut()
|
||||
// and get this to represent a count of how many we're looking at so far to render
|
||||
.enumerate()
|
||||
// and only take as many as are ready to be rendered
|
||||
.take_while(|(idx, page)| {
|
||||
let mut take = page.img.is_some();
|
||||
if let Some(max) = self.page_constraints.max_wide {
|
||||
take &= *idx < max.get();
|
||||
}
|
||||
take
|
||||
take
|
||||
})
|
||||
// and map it to their width (in cells on the terminal, not pixels)
|
||||
.filter_map(|(_, page)| {
|
||||
page.img.as_mut().map(|img| {
|
||||
let (w, h) = img.w_h();
|
||||
(w, h, img)
|
||||
})
|
||||
// and map it to their width (in cells on the terminal, not pixels)
|
||||
.filter_map(|(_, page)| page.img.as_mut().map(|img| (img.w_h().0, img)))
|
||||
// and then take them as long as they won't overflow the available area.
|
||||
.take_while(|(width, _)| match test_area_w.checked_sub(*width) {
|
||||
Some(new_val) => {
|
||||
test_area_w = new_val;
|
||||
true
|
||||
}
|
||||
None => false
|
||||
})
|
||||
// and then take them as long as they won't overflow the available area.
|
||||
.take_while(|(width, _, _)| match test_area_w.checked_sub(*width) {
|
||||
Some(new_val) => {
|
||||
test_area_w = new_val;
|
||||
true
|
||||
}
|
||||
None => false
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if self.page_constraints.r_to_l {
|
||||
page_sizes.reverse();
|
||||
}
|
||||
|
||||
if page_sizes.is_empty() {
|
||||
// If none are ready to render, just show the loading thing
|
||||
Self::render_loading_in(frame, img_area);
|
||||
KittyDisplay::ClearImages
|
||||
} else {
|
||||
execute!(stdout(), BeginSynchronizedUpdate).unwrap();
|
||||
|
||||
let total_width = page_sizes.iter().map(|(w, _, _)| w).sum::<u16>();
|
||||
|
||||
self.last_render.pages_shown = page_sizes.len();
|
||||
|
||||
let unused_width = img_area.width - total_width;
|
||||
self.last_render.unused_width = unused_width;
|
||||
img_area.x += unused_width / 2;
|
||||
|
||||
if let Some(total_height) = page_sizes.iter().map(|(_, h, _)| h).max() {
|
||||
// This subtraction might sporadicly fail while shrinking the window.
|
||||
if let Some(unused_height) = img_area.height.checked_sub(*total_height) {
|
||||
img_area.y += unused_height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
let to_display = page_sizes
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, (width, _, img))| {
|
||||
let maybe_img =
|
||||
Self::render_single_page(frame, img, Rect { width, ..img_area });
|
||||
img_area.x += width;
|
||||
maybe_img.map(|(img, pos)| KittyReadyToDisplay {
|
||||
img,
|
||||
page_num: idx + self.page,
|
||||
pos,
|
||||
display_loc: DisplayLocation::default()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if self.page_constraints.r_to_l {
|
||||
page_widths.reverse();
|
||||
}
|
||||
// we want to set this at the very end so it doesn't get set somewhere halfway through and
|
||||
// then the whole diffing thing messes it up
|
||||
self.last_render.rect = size;
|
||||
|
||||
if page_widths.is_empty() {
|
||||
// If none are ready to render, just show the loading thing
|
||||
Self::render_loading_in(frame, img_area);
|
||||
KittyDisplay::ClearImages
|
||||
} else {
|
||||
execute!(stdout(), BeginSynchronizedUpdate).unwrap();
|
||||
|
||||
let total_width = page_widths.iter().map(|(w, _)| w).sum::<u16>();
|
||||
|
||||
self.last_render.pages_shown = page_widths.len();
|
||||
|
||||
let unused_width = img_area.width - total_width;
|
||||
self.last_render.unused_width = unused_width;
|
||||
img_area.x += unused_width / 2;
|
||||
|
||||
let to_display = page_widths
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, (width, img))| {
|
||||
let maybe_img =
|
||||
Self::render_single_page(frame, img, Rect { width, ..img_area });
|
||||
img_area.x += width;
|
||||
maybe_img.map(|(img, pos)| KittyReadyToDisplay {
|
||||
img,
|
||||
page_num: idx + self.page,
|
||||
pos,
|
||||
display_loc: DisplayLocation::default()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// we want to set this at the very end so it doesn't get set somewhere halfway through and
|
||||
// then the whole diffing thing messes it up
|
||||
self.last_render.rect = size;
|
||||
|
||||
KittyDisplay::DisplayImages(to_display)
|
||||
}
|
||||
KittyDisplay::DisplayImages(to_display)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,12 +518,13 @@ impl Tui {
|
||||
}
|
||||
|
||||
fn render_loading_in(frame: &mut Frame<'_>, area: Rect) {
|
||||
let loading_str = "Loading...";
|
||||
let inner_space = Layout::horizontal([Constraint::Length(loading_str.len() as u16)])
|
||||
.flex(Flex::Center)
|
||||
.split(area);
|
||||
const LOADING_STR: &str = "Loading...";
|
||||
let inner_space =
|
||||
Layout::horizontal([Constraint::Length(const { LOADING_STR.len() as u16 })])
|
||||
.flex(Flex::Center)
|
||||
.split(area);
|
||||
|
||||
let loading_span = Span::styled(loading_str, Style::new().fg(Color::Cyan));
|
||||
let loading_span = Span::styled(LOADING_STR, Style::new().fg(Color::Cyan));
|
||||
|
||||
frame.render_widget(loading_span, inner_space[0]);
|
||||
}
|
||||
@@ -399,6 +553,8 @@ impl Tui {
|
||||
PageChange::Prev => self.set_page(self.page.saturating_sub(diff))
|
||||
}
|
||||
|
||||
// Yes these conversions could wrap around if you have > isize::MAX pages, but we already
|
||||
// decided that you deserve to suffer if you have more than u32::MAX pages, so that's fine.
|
||||
match self.page as isize - old as isize {
|
||||
0 => None,
|
||||
_ => Some(InputAction::JumpingToPage(self.page))
|
||||
@@ -532,19 +688,15 @@ impl Tui {
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, ev: &Event) -> Option<InputAction> {
|
||||
fn jump_to_page(
|
||||
page: &mut usize,
|
||||
rect: &mut Rect,
|
||||
new_page: Option<usize>
|
||||
) -> Option<InputAction> {
|
||||
new_page.map(|new_page| {
|
||||
*page = new_page;
|
||||
// Make sure we re-render
|
||||
*rect = Rect::default();
|
||||
InputAction::JumpingToPage(new_page)
|
||||
})
|
||||
fn jump_to_page(page: &mut usize, rect: &mut Rect, new_page: usize) -> InputAction {
|
||||
*page = new_page;
|
||||
// Make sure we re-render
|
||||
*rect = Rect::default();
|
||||
InputAction::JumpingToPage(new_page)
|
||||
}
|
||||
|
||||
let can_zoom = self.is_kitty && self.zoom.is_some();
|
||||
|
||||
match ev {
|
||||
Event::Key(key) => {
|
||||
match key.code {
|
||||
@@ -560,6 +712,12 @@ impl Tui {
|
||||
if let BottomMessage::Input(InputCommand::GoToPage(ref mut page)) =
|
||||
self.bottom_msg
|
||||
{
|
||||
if c == 'g' && self.is_kitty {
|
||||
self.update_zoom(Zoom::pan_bottom);
|
||||
self.set_msg(MessageSetting::Pop);
|
||||
return Some(InputAction::Redraw);
|
||||
}
|
||||
|
||||
return c.to_digit(10).map(|input_num| {
|
||||
*page = (*page * 10) + input_num as usize;
|
||||
InputAction::Redraw
|
||||
@@ -593,30 +751,38 @@ impl Tui {
|
||||
'n' if self.page < self.rendered.len() - 1 => {
|
||||
// TODO: If we can't find one, then maybe like block until we've verified
|
||||
// all the pages have been checked?
|
||||
let next_page = self.rendered[(self.page + 1)..]
|
||||
self.rendered[(self.page + 1)..]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(idx, p)| {
|
||||
p.num_results
|
||||
.is_some_and(|num| num > 0)
|
||||
.then_some(self.page + 1 + idx)
|
||||
});
|
||||
|
||||
jump_to_page(&mut self.page, &mut self.last_render.rect, next_page)
|
||||
}
|
||||
'N' if self.page > 0 => {
|
||||
let prev_page = self.rendered[..(self.page)]
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find_map(|(idx, p)| {
|
||||
p.num_results
|
||||
.is_some_and(|num| num > 0)
|
||||
.then_some(self.page - (idx + 1))
|
||||
});
|
||||
|
||||
jump_to_page(&mut self.page, &mut self.last_render.rect, prev_page)
|
||||
})
|
||||
.map(|next_page| {
|
||||
jump_to_page(
|
||||
&mut self.page,
|
||||
&mut self.last_render.rect,
|
||||
next_page
|
||||
)
|
||||
})
|
||||
}
|
||||
'N' if self.page > 0 => self.rendered[..(self.page)]
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find_map(|(idx, p)| {
|
||||
p.num_results
|
||||
.is_some_and(|num| num > 0)
|
||||
.then_some(self.page - (idx + 1))
|
||||
})
|
||||
.map(|prev_page| {
|
||||
jump_to_page(
|
||||
&mut self.page,
|
||||
&mut self.last_render.rect,
|
||||
prev_page
|
||||
)
|
||||
}),
|
||||
'z' if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
// [todo] better error handling here?
|
||||
|
||||
@@ -656,23 +822,13 @@ impl Tui {
|
||||
self.last_render.rect = Rect::default();
|
||||
Some(InputAction::SwitchRenderZoom(f_or_f))
|
||||
}
|
||||
'o' if self.is_kitty => self.update_zoom(|z|
|
||||
// TODO: for now, we don't let people zoom in past fill-screen
|
||||
z.level = z.level.saturating_add(1).min(0)),
|
||||
'O' if self.is_kitty =>
|
||||
self.update_zoom(|z| z.level = z.level.saturating_sub(1)),
|
||||
'L' if self.is_kitty => self.update_zoom(|z| {
|
||||
z.cell_pan_from_left = z.cell_pan_from_left.saturating_add(1)
|
||||
}),
|
||||
'H' if self.is_kitty => self.update_zoom(|z| {
|
||||
z.cell_pan_from_left = z.cell_pan_from_left.saturating_sub(1)
|
||||
}),
|
||||
'J' if self.is_kitty => self.update_zoom(|z| {
|
||||
z.cell_pan_from_top = z.cell_pan_from_top.saturating_add(1)
|
||||
}),
|
||||
'K' if self.is_kitty => self.update_zoom(|z| {
|
||||
z.cell_pan_from_top = z.cell_pan_from_top.saturating_sub(1)
|
||||
}),
|
||||
'o' if can_zoom => self.update_zoom(Zoom::step_in),
|
||||
'O' if can_zoom => self.update_zoom(Zoom::step_out),
|
||||
'L' if can_zoom => self.update_zoom(|z| z.pan(Direction::Right)),
|
||||
'H' if can_zoom => self.update_zoom(|z| z.pan(Direction::Left)),
|
||||
'J' if can_zoom => self.update_zoom(|z| z.pan(Direction::Down)),
|
||||
'K' if can_zoom => self.update_zoom(|z| z.pan(Direction::Up)),
|
||||
'G' if can_zoom => self.update_zoom(Zoom::pan_top),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@@ -686,9 +842,11 @@ impl Tui {
|
||||
None
|
||||
}
|
||||
KeyCode::Right => self.change_page(PageChange::Next, ChangeAmount::Single),
|
||||
KeyCode::Down => self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
|
||||
KeyCode::Down | KeyCode::PageDown =>
|
||||
self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
|
||||
KeyCode::Left => self.change_page(PageChange::Prev, ChangeAmount::Single),
|
||||
KeyCode::Up => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
|
||||
KeyCode::Up | KeyCode::PageUp =>
|
||||
self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
|
||||
KeyCode::Esc => match (self.showing_help_msg, &self.bottom_msg) {
|
||||
(false, BottomMessage::Help) => Some(InputAction::QuitApp),
|
||||
_ => {
|
||||
@@ -757,29 +915,36 @@ impl Tui {
|
||||
}
|
||||
}
|
||||
Event::Mouse(mouse) => {
|
||||
if mouse.modifiers.contains(KeyModifiers::CONTROL)
|
||||
&& self.is_kitty
|
||||
&& self.zoom.is_some()
|
||||
{
|
||||
match mouse.kind {
|
||||
MouseEventKind::ScrollUp =>
|
||||
self.update_zoom(|z| z.level = z.level.saturating_add(1).min(0)),
|
||||
MouseEventKind::ScrollDown =>
|
||||
self.update_zoom(|z| z.level = z.level.saturating_sub(1)),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
match mouse.kind {
|
||||
MouseEventKind::ScrollRight =>
|
||||
self.change_page(PageChange::Next, ChangeAmount::Single),
|
||||
MouseEventKind::ScrollDown =>
|
||||
self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
|
||||
MouseEventKind::ScrollLeft =>
|
||||
self.change_page(PageChange::Prev, ChangeAmount::Single),
|
||||
MouseEventKind::ScrollUp =>
|
||||
self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
|
||||
_ => None
|
||||
let mut handle_scroll = |mut direction: Direction| {
|
||||
if can_zoom {
|
||||
if mouse.modifiers.contains(KeyModifiers::CONTROL) {
|
||||
match direction {
|
||||
Direction::Up => self.update_zoom(Zoom::step_in),
|
||||
Direction::Down => self.update_zoom(Zoom::step_out),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
if mouse.modifiers.contains(KeyModifiers::SHIFT) {
|
||||
direction = direction.flip_mouse_xy();
|
||||
}
|
||||
self.update_zoom(|z| z.pan(direction))
|
||||
}
|
||||
} else {
|
||||
let (change, amount) = match direction {
|
||||
Direction::Right => (PageChange::Next, ChangeAmount::Single),
|
||||
Direction::Down => (PageChange::Next, ChangeAmount::WholeScreen),
|
||||
Direction::Left => (PageChange::Prev, ChangeAmount::Single),
|
||||
Direction::Up => (PageChange::Prev, ChangeAmount::WholeScreen)
|
||||
};
|
||||
self.change_page(change, amount)
|
||||
}
|
||||
};
|
||||
match mouse.kind {
|
||||
MouseEventKind::ScrollRight => handle_scroll(Direction::Right),
|
||||
MouseEventKind::ScrollDown => handle_scroll(Direction::Down),
|
||||
MouseEventKind::ScrollLeft => handle_scroll(Direction::Left),
|
||||
MouseEventKind::ScrollUp => handle_scroll(Direction::Up),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
Event::Resize(_, _) => Some(InputAction::Redraw),
|
||||
@@ -791,7 +956,7 @@ impl Tui {
|
||||
#[expect(clippy::unnecessary_wraps)]
|
||||
fn update_zoom(&mut self, f: impl FnOnce(&mut Zoom)) -> Option<InputAction> {
|
||||
if let Some(z) = &mut self.zoom {
|
||||
f(z)
|
||||
f(z);
|
||||
}
|
||||
self.last_render.rect = Rect::default();
|
||||
Some(InputAction::Redraw)
|
||||
@@ -847,11 +1012,24 @@ impl Tui {
|
||||
.border_set(border::ROUNDED)
|
||||
.border_style(Color::Blue);
|
||||
|
||||
let help_span = Paragraph::new(HELP_PAGE).wrap(Wrap { trim: false });
|
||||
let help_sections = [
|
||||
Text::from(HELP_PAGE),
|
||||
// just some spacing
|
||||
Text::from(""),
|
||||
if self.is_kitty {
|
||||
Text::from(KITTY_HELP)
|
||||
} else {
|
||||
Text::from("Not using kitty, kitty-specific keybindings hidden")
|
||||
.style(Color::DarkGray)
|
||||
}
|
||||
];
|
||||
|
||||
let max_w: u16 = HELP_PAGE
|
||||
.lines()
|
||||
.map(str::len)
|
||||
let max_w: u16 = help_sections
|
||||
.iter()
|
||||
.flat_map(|section| section.lines.as_slice())
|
||||
// We don't really need full unicode-width since we're using all ascii for the help
|
||||
// pages, but this is the function they give us.
|
||||
.map(Line::width)
|
||||
.max()
|
||||
.unwrap_or_default()
|
||||
.try_into()
|
||||
@@ -866,15 +1044,24 @@ impl Tui {
|
||||
|
||||
let block_area = Layout::vertical([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(u16::try_from(HELP_PAGE.lines().count()).unwrap() + 4),
|
||||
Constraint::Length(
|
||||
u16::try_from(help_sections.iter().map(|s| s.lines.len()).sum::<usize>()).unwrap()
|
||||
+ 4
|
||||
),
|
||||
Constraint::Fill(1)
|
||||
])
|
||||
.split(layout[1]);
|
||||
|
||||
let block_inner = block.inner(block_area[1]);
|
||||
let mut block_inner = block.inner(block_area[1]);
|
||||
|
||||
frame.render_widget(block, block_area[1]);
|
||||
frame.render_widget(help_span, block_inner);
|
||||
|
||||
for section in help_sections {
|
||||
let section_lines = section.lines.len();
|
||||
let span = Paragraph::new(section).wrap(Wrap { trim: false });
|
||||
frame.render_widget(span, block_inner);
|
||||
block_inner.y += u16::try_from(section_lines).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -895,18 +1082,24 @@ i:
|
||||
Invert colors
|
||||
f:
|
||||
Remove borders/fullscreen
|
||||
z (when using kitty protocol):
|
||||
Toggle between fill-screen and fit-screen
|
||||
o/O (when on fill-screen):
|
||||
zoom in and out, respectively
|
||||
H, J, K, L (when zoomed in):
|
||||
pan direction around page
|
||||
?:
|
||||
Show this page
|
||||
ctrl+z:
|
||||
Suspend & background tdf \
|
||||
";
|
||||
|
||||
static KITTY_HELP: &str = "\
|
||||
When using Kitty Protocol:
|
||||
z:
|
||||
Toggle between fill-screen and fit-screen
|
||||
o/O (when on fill-screen):
|
||||
Zoom in and out, respectively
|
||||
gg/G (when on fill-screen):
|
||||
Scroll to top/bottom of page
|
||||
H, J, K, L (when zoomed in):
|
||||
Pan direction around page
|
||||
";
|
||||
|
||||
pub enum InputAction {
|
||||
Redraw,
|
||||
JumpingToPage(usize),
|
||||
|
||||
Reference in New Issue
Block a user