mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'css' of github.com:Textualize/textual into text-input
This commit is contained in:
269
poetry.lock
generated
269
poetry.lock
generated
@@ -166,7 +166,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "coverage"
|
name = "coverage"
|
||||||
version = "6.3.2"
|
version = "6.3.3"
|
||||||
description = "Code coverage measurement for Python"
|
description = "Code coverage measurement for Python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -185,7 +185,7 @@ python-versions = "*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
version = "3.6.0"
|
version = "3.7.0"
|
||||||
description = "A platform independent file lock."
|
description = "A platform independent file lock."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -381,6 +381,14 @@ mkdocs-autorefs = ">=0.1"
|
|||||||
pymdown-extensions = ">=6.3"
|
pymdown-extensions = ">=6.3"
|
||||||
pytkdocs = ">=0.14.0"
|
pytkdocs = ">=0.14.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "msgpack"
|
||||||
|
version = "1.0.3"
|
||||||
|
description = "MessagePack (de)serializer."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multidict"
|
name = "multidict"
|
||||||
version = "6.0.2"
|
version = "6.0.2"
|
||||||
@@ -666,7 +674,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-machine"
|
name = "time-machine"
|
||||||
version = "2.6.0"
|
version = "2.7.0"
|
||||||
description = "Travel through time in your tests."
|
description = "Travel through time in your tests."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -728,7 +736,7 @@ testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)",
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "watchdog"
|
name = "watchdog"
|
||||||
version = "2.1.7"
|
version = "2.1.8"
|
||||||
description = "Filesystem events monitoring"
|
description = "Filesystem events monitoring"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -765,7 +773,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
content-hash = "37541ff4aa6aa74d76b10b8183b7e62e34b6c66c8b8f8eec7aad23e9b5793f94"
|
content-hash = "3579be8d55deb729ef79984823765900552e19284c205b7bedc92897190fb6fd"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiohttp = [
|
aiohttp = [
|
||||||
@@ -916,55 +924,55 @@ commonmark = [
|
|||||||
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
|
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
|
||||||
]
|
]
|
||||||
coverage = [
|
coverage = [
|
||||||
{file = "coverage-6.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf"},
|
{file = "coverage-6.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df32ee0f4935a101e4b9a5f07b617d884a531ed5666671ff6ac66d2e8e8246d8"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac"},
|
{file = "coverage-6.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75b5dbffc334e0beb4f6c503fb95e6d422770fd2d1b40a64898ea26d6c02742d"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1"},
|
{file = "coverage-6.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114944e6061b68a801c5da5427b9173a0dd9d32cd5fcc18a13de90352843737d"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4"},
|
{file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab88a01cd180b5640ccc9c47232e31924d5f9967ab7edd7e5c91c68eee47a69"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903"},
|
{file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad8f9068f5972a46d50fe5f32c09d6ee11da69c560fcb1b4c3baea246ca4109b"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c"},
|
{file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4cd696aa712e6cd16898d63cf66139dc70d998f8121ab558f0e1936396dbc579"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f"},
|
{file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1a9942e282cc9d3ed522cd3e3cab081149b27ea3bda72d6f61f84eaf88c1a63"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05"},
|
{file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c06455121a089252b5943ea682187a4e0a5cf0a3fb980eb8e7ce394b144430a9"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-win32.whl", hash = "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39"},
|
{file = "coverage-6.3.3-cp310-cp310-win32.whl", hash = "sha256:cb5311d6ccbd22578c80028c5e292a7ab9adb91bd62c1982087fad75abe2e63d"},
|
||||||
{file = "coverage-6.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1"},
|
{file = "coverage-6.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:6d4a6f30f611e657495cc81a07ff7aa8cd949144e7667c5d3e680d73ba7a70e4"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa"},
|
{file = "coverage-6.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79bf405432428e989cad7b8bc60581963238f7645ae8a404f5dce90236cc0293"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518"},
|
{file = "coverage-6.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:338c417613f15596af9eb7a39353b60abec9d8ce1080aedba5ecee6a5d85f8d3"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7"},
|
{file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db094a6a4ae6329ed322a8973f83630b12715654c197dd392410400a5bfa1a73"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6"},
|
{file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414e8b124611bf4df8d77215bd32cba6e3425da8ce9c1f1046149615e3a9a31"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad"},
|
{file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:93b16b08f94c92cab88073ffd185070cdcb29f1b98df8b28e6649145b7f2c90d"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359"},
|
{file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbc86ae8cc129c801e7baaafe3addf3c8d49c9c1597c44bdf2d78139707c3c62"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4"},
|
{file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b5ba058610e8289a07db2a57bce45a1793ec0d3d11db28c047aae2aa1a832572"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-win32.whl", hash = "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca"},
|
{file = "coverage-6.3.3-cp37-cp37m-win32.whl", hash = "sha256:8329635c0781927a2c6ae068461e19674c564e05b86736ab8eb29c420ee7dc20"},
|
||||||
{file = "coverage-6.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3"},
|
{file = "coverage-6.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:e5af1feee71099ae2e3b086ec04f57f9950e1be9ecf6c420696fea7977b84738"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d"},
|
{file = "coverage-6.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e814a4a5a1d95223b08cdb0f4f57029e8eab22ffdbae2f97107aeef28554517e"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059"},
|
{file = "coverage-6.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:61f4fbf3633cb0713437291b8848634ea97f89c7e849c2be17a665611e433f53"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512"},
|
{file = "coverage-6.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3401b0d2ed9f726fadbfa35102e00d1b3547b73772a1de5508ef3bdbcb36afe7"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca"},
|
{file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8586b177b4407f988731eb7f41967415b2197f35e2a6ee1a9b9b561f6323c8e9"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d"},
|
{file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892e7fe32191960da559a14536768a62e83e87bbb867e1b9c643e7e0fbce2579"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0"},
|
{file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afb03f981fadb5aed1ac6e3dd34f0488e1a0875623d557b6fad09b97a942b38a"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6"},
|
{file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cbe91bc84be4e5ef0b1480d15c7b18e29c73bdfa33e07d3725da7d18e1b0aff2"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2"},
|
{file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:91502bf27cbd5c83c95cfea291ef387469f2387508645602e1ca0fd8a4ba7548"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-win32.whl", hash = "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e"},
|
{file = "coverage-6.3.3-cp38-cp38-win32.whl", hash = "sha256:c488db059848702aff30aa1d90ef87928d4e72e4f00717343800546fdbff0a94"},
|
||||||
{file = "coverage-6.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1"},
|
{file = "coverage-6.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6534fcdfb5c503affb6b1130db7b5bfc8a0f77fa34880146f7a5c117987d0"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620"},
|
{file = "coverage-6.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc692c9ee18f0dd3214843779ba6b275ee4bb9b9a5745ba64265bce911aefd1a"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d"},
|
{file = "coverage-6.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:462105283de203df8de58a68c1bb4ba2a8a164097c2379f664fa81d6baf94b81"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536"},
|
{file = "coverage-6.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc972d829ad5ef4d4c5fcabd2bbe2add84ce8236f64ba1c0c72185da3a273130"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7"},
|
{file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06f54765cdbce99901871d50fe9f41d58213f18e98b170a30ca34f47de7dd5e8"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2"},
|
{file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835f76a081787f0ca62a53504361b3869840a1620049b56d803a8cb3a9eeea3"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4"},
|
{file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f5fee77ec3384b934797f1873758f796dfb4f167e1296dc00f8b2e023ce6ee9"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69"},
|
{file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:baa8be8aba3dd1e976e68677be68a960a633a6d44c325757aefaa4d66175050f"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684"},
|
{file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d06380e777dd6b35ee936f333d55b53dc4a8271036ff884c909cf6e94be8b6c"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-win32.whl", hash = "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4"},
|
{file = "coverage-6.3.3-cp39-cp39-win32.whl", hash = "sha256:f8cabc5fd0091976ab7b020f5708335033e422de25e20ddf9416bdce2b7e07d8"},
|
||||||
{file = "coverage-6.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92"},
|
{file = "coverage-6.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c9441d57b0963cf8340268ad62fc83de61f1613034b79c2b1053046af0c5284"},
|
||||||
{file = "coverage-6.3.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf"},
|
{file = "coverage-6.3.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:d522f1dc49127eab0bfbba4e90fa068ecff0899bbf61bf4065c790ddd6c177fe"},
|
||||||
{file = "coverage-6.3.2.tar.gz", hash = "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9"},
|
{file = "coverage-6.3.3.tar.gz", hash = "sha256:2781c43bffbbec2b8867376d4d61916f5e9c4cc168232528562a61d1b4b01879"},
|
||||||
]
|
]
|
||||||
distlib = [
|
distlib = [
|
||||||
{file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
|
{file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
|
||||||
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
|
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
|
||||||
]
|
]
|
||||||
filelock = [
|
filelock = [
|
||||||
{file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"},
|
{file = "filelock-3.7.0-py3-none-any.whl", hash = "sha256:c7b5fdb219b398a5b28c8e4c1893ef5f98ece6a38c6ab2c22e26ec161556fed6"},
|
||||||
{file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"},
|
{file = "filelock-3.7.0.tar.gz", hash = "sha256:b795f1b42a61bbf8ec7113c341dad679d772567b936fbd1bf43c9a238e673e20"},
|
||||||
]
|
]
|
||||||
frozenlist = [
|
frozenlist = [
|
||||||
{file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2257aaba9660f78c7b1d8fea963b68f3feffb1a9d5d05a18401ca9eb3e8d0a3"},
|
{file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2257aaba9660f78c7b1d8fea963b68f3feffb1a9d5d05a18401ca9eb3e8d0a3"},
|
||||||
@@ -1121,6 +1129,42 @@ mkdocstrings = [
|
|||||||
{file = "mkdocstrings-0.17.0-py3-none-any.whl", hash = "sha256:103fc1dd58cb23b7e0a6da5292435f01b29dc6fa0ba829132537f3f556f985de"},
|
{file = "mkdocstrings-0.17.0-py3-none-any.whl", hash = "sha256:103fc1dd58cb23b7e0a6da5292435f01b29dc6fa0ba829132537f3f556f985de"},
|
||||||
{file = "mkdocstrings-0.17.0.tar.gz", hash = "sha256:75b5cfa2039aeaf3a5f5cf0aa438507b0330ce76c8478da149d692daa7213a98"},
|
{file = "mkdocstrings-0.17.0.tar.gz", hash = "sha256:75b5cfa2039aeaf3a5f5cf0aa438507b0330ce76c8478da149d692daa7213a98"},
|
||||||
]
|
]
|
||||||
|
msgpack = [
|
||||||
|
{file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96acc674bb9c9be63fa8b6dabc3248fdc575c4adc005c440ad02f87ca7edd079"},
|
||||||
|
{file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c3ca57c96c8e69c1a0d2926a6acf2d9a522b41dc4253a8945c4c6cd4981a4e3"},
|
||||||
|
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0a792c091bac433dfe0a70ac17fc2087d4595ab835b47b89defc8bbabcf5c73"},
|
||||||
|
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c58cdec1cb5fcea8c2f1771d7b5fec79307d056874f746690bd2bdd609ab147"},
|
||||||
|
{file = "msgpack-1.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f97c0f35b3b096a330bb4a1a9247d0bd7e1f3a2eba7ab69795501504b1c2c39"},
|
||||||
|
{file = "msgpack-1.0.3-cp310-cp310-win32.whl", hash = "sha256:36a64a10b16c2ab31dcd5f32d9787ed41fe68ab23dd66957ca2826c7f10d0b85"},
|
||||||
|
{file = "msgpack-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c1ba333b4024c17c7591f0f372e2daa3c31db495a9b2af3cf664aef3c14354f7"},
|
||||||
|
{file = "msgpack-1.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c2140cf7a3ec475ef0938edb6eb363fa704159e0bf71dde15d953bacc1cf9d7d"},
|
||||||
|
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4c22717c74d44bcd7af353024ce71c6b55346dad5e2cc1ddc17ce8c4507c6b"},
|
||||||
|
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d733a15ade190540c703de209ffbc42a3367600421b62ac0c09fde594da6ec"},
|
||||||
|
{file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7e03b06f2982aa98d4ddd082a210c3db200471da523f9ac197f2828e80e7770"},
|
||||||
|
{file = "msgpack-1.0.3-cp36-cp36m-win32.whl", hash = "sha256:3d875631ecab42f65f9dce6f55ce6d736696ced240f2634633188de2f5f21af9"},
|
||||||
|
{file = "msgpack-1.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:40fb89b4625d12d6027a19f4df18a4de5c64f6f3314325049f219683e07e678a"},
|
||||||
|
{file = "msgpack-1.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eef0cf8db3857b2b556213d97dd82de76e28a6524853a9beb3264983391dc1a"},
|
||||||
|
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d8c332f53ffff01953ad25131272506500b14750c1d0ce8614b17d098252fbc"},
|
||||||
|
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0903bd93cbd34653dd63bbfcb99d7539c372795201f39d16fdfde4418de43a"},
|
||||||
|
{file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf1e6bfed4860d72106f4e0a1ab519546982b45689937b40257cfd820650b920"},
|
||||||
|
{file = "msgpack-1.0.3-cp37-cp37m-win32.whl", hash = "sha256:d02cea2252abc3756b2ac31f781f7a98e89ff9759b2e7450a1c7a0d13302ff50"},
|
||||||
|
{file = "msgpack-1.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f30dd0dc4dfe6231ad253b6f9f7128ac3202ae49edd3f10d311adc358772dba"},
|
||||||
|
{file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f201d34dc89342fabb2a10ed7c9a9aaaed9b7af0f16a5923f1ae562b31258dea"},
|
||||||
|
{file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bb87f23ae7d14b7b3c21009c4b1705ec107cb21ee71975992f6aca571fb4a42a"},
|
||||||
|
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a3a5c4b16e9d0edb823fe54b59b5660cc8d4782d7bf2c214cb4b91a1940a8ef"},
|
||||||
|
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74da1e5fcf20ade12c6bf1baa17a2dc3604958922de8dc83cbe3eff22e8b611"},
|
||||||
|
{file = "msgpack-1.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73a80bd6eb6bcb338c1ec0da273f87420829c266379c8c82fa14c23fb586cfa1"},
|
||||||
|
{file = "msgpack-1.0.3-cp38-cp38-win32.whl", hash = "sha256:9fce00156e79af37bb6db4e7587b30d11e7ac6a02cb5bac387f023808cd7d7f4"},
|
||||||
|
{file = "msgpack-1.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:9b6f2d714c506e79cbead331de9aae6837c8dd36190d02da74cb409b36162e8a"},
|
||||||
|
{file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:89908aea5f46ee1474cc37fbc146677f8529ac99201bc2faf4ef8edc023c2bf3"},
|
||||||
|
{file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:973ad69fd7e31159eae8f580f3f707b718b61141838321c6fa4d891c4a2cca52"},
|
||||||
|
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da24375ab4c50e5b7486c115a3198d207954fe10aaa5708f7b65105df09109b2"},
|
||||||
|
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a598d0685e4ae07a0672b59792d2cc767d09d7a7f39fd9bd37ff84e060b1a996"},
|
||||||
|
{file = "msgpack-1.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c309a68cb5d6bbd0c50d5c71a25ae81f268c2dc675c6f4ea8ab2feec2ac4e2"},
|
||||||
|
{file = "msgpack-1.0.3-cp39-cp39-win32.whl", hash = "sha256:494471d65b25a8751d19c83f1a482fd411d7ca7a3b9e17d25980a74075ba0e88"},
|
||||||
|
{file = "msgpack-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:f01b26c2290cbd74316990ba84a14ac3d599af9cebefc543d241a66e785cf17d"},
|
||||||
|
{file = "msgpack-1.0.3.tar.gz", hash = "sha256:51fdc7fb93615286428ee7758cecc2f374d5ff363bdd884c7ea622a7a327a81e"},
|
||||||
|
]
|
||||||
multidict = [
|
multidict = [
|
||||||
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
|
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
|
||||||
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
|
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
|
||||||
@@ -1324,46 +1368,46 @@ six = [
|
|||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
]
|
]
|
||||||
time-machine = [
|
time-machine = [
|
||||||
{file = "time-machine-2.6.0.tar.gz", hash = "sha256:676d0e462f504eb1ef9903cbb92fe4d285af2080fbcbeece9e4b78db5be08687"},
|
{file = "time-machine-2.7.0.tar.gz", hash = "sha256:0aa0ccd531d7d98e71f7945b65d26d92d31ab74a21a111b9afe61b981c1eb7b2"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:53b557a82640dda052c36d45573df782f48257f55bd561397b622b1717621702"},
|
{file = "time_machine-2.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aac9e9fa665d0ed7d105ddaaebda9ef3a2de30aaaf56cfe894f15ba60de8ae09"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:995e3af3c886c9209fd9fa629d9eb9eaa45b84a090c163206eaf1f3d690fff62"},
|
{file = "time_machine-2.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:93c6baea546a8edffa57dc691cbb61e5070d6ec791ff7b4a025d4f34b9808516"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa84cd54a9533a45c5e29cf9d353e54fcca02706718be009564b749a65095c4e"},
|
{file = "time_machine-2.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f056cc4b9212424eb0796389cb019fb61ed0691674649f824585973f001ab66"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e81ea179b51abf7a9185cd8b77a416a910c1b4e95b8c1c1cc843247bfd797668"},
|
{file = "time_machine-2.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dacd42e9f4a81a7c9e308874802e801aab1fed119698958fc00920038a652145"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22461251ae1e2c1265d56e3df66ff7d3bffe9f1e077dd766a2b7fde9e0dcb609"},
|
{file = "time_machine-2.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d607303fe6c046b246f1befa82218624db7e92695950c4f6e11f416b8f61129"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9cec8e59f09b91e4a2321bd1a19ef08bdff44cdc749e5301d3b5e9952a546972"},
|
{file = "time_machine-2.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:adf738e2d21b265cab4dcb2dc3c0592087f44614a03657c87a6e79131e3be715"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d316b2cc4cc52df913640af3c9b5227b9e5c53b63016487448829bb5e42ddbdb"},
|
{file = "time_machine-2.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e0a774c3d94b6b4dd58a939ae886d4533ca9f308f71ab6e174d41aa092fcd807"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a53c4f2cbca166a8a69774101c3b2967ecc12b55d5d9df182de9e42c84dc2781"},
|
{file = "time_machine-2.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b1115628d39ebe7af89bb187634f119b58f163ee38d9a8369d779a496ac6105"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-win32.whl", hash = "sha256:884ba4ea8a2600ed97363e260d083750c4601e61e8011ab17137f662a522d3ae"},
|
{file = "time_machine-2.7.0-cp310-cp310-win32.whl", hash = "sha256:6ad4b6dfec23acc7b5549fcbe9d632347bd62781471d79e44001242821635393"},
|
||||||
{file = "time_machine-2.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:345377579d9239d44f53031f3b86115f47639a3e63bcb304d785c141311c166f"},
|
{file = "time_machine-2.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef7cf446a19536a6c4fcdc74c6023bf4c85f6ebe97c63b18b4bf97905bf7919d"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ab14bb97dca07b8e882677feed4ffe2e7019d604dfca36e916a67308a3e0031d"},
|
{file = "time_machine-2.7.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:252f53e72da83e876c004e312d15a2e7c920b84dc60a469ad3c5404c5aa0d2b1"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a93e6973b778c586db54ccf666c9af1f9648fa020dbdd815fc0f184a3a3ef24"},
|
{file = "time_machine-2.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f39695eb780795f7109ddf6b6c04d97f5610d75b08eb968deed3f0e09c43bb0"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d785189082d9add90ddd9bfeab1f257a3b28a3f302f2ff29e673bd0f226787"},
|
{file = "time_machine-2.7.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c6236ea7f2744f8b9ceab85c9cd71f0aeb03530e2b47377678c39fcd77aafb6"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91beb7ad1da8986776a71f14e8f38397eaea1dbc12aaf1907e68a597e2e71aaa"},
|
{file = "time_machine-2.7.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39aabce90c4c936abe78efe483a6b2fe99901ff431afbeab65c0815eeff66f0b"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8366b48826005b30a6cd7dc7d8ce6f801fd240eadebe9495becab3fea9b6a7d7"},
|
{file = "time_machine-2.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:75cdf9aa1b0fba833ca4ebafd738a88983a6597caa4b6ac4a9bfa83c6ca7d8fc"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9e69336008f7fb446d315006971a2beb52504465ef76430c5386ed0d0d28d5a4"},
|
{file = "time_machine-2.7.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:038e642d88ba5b76bfa77ec95bcf0d35d7fc6c6ea264fc0aa36e02eff09d3ad2"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:90d3c126e407991da64555ab3f36d2674f77ae328e514567b7a4471011f758b9"},
|
{file = "time_machine-2.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3cca3f11059ab6d39dc33495be94ecceaa3d6760b7a983a5c67e172d6b5f6781"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-win32.whl", hash = "sha256:e85b954529599291b24a6562f2a9f1131a1fddfaef1b51b588096dc4786c860e"},
|
{file = "time_machine-2.7.0-cp37-cp37m-win32.whl", hash = "sha256:1d40c3be8b075868e73e09a9f600ecf383ad30c8921806c7dc0915820b8fa1c7"},
|
||||||
{file = "time_machine-2.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3588a83acf930c71112b671b1e91d52f7e5f9e427cb55b4c0d27b2aeab219244"},
|
{file = "time_machine-2.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e125d7a406f8a3b48ddd3f836cf41be98de446db76147a2da8d1f1e82254946e"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a99e97227cd13baf0add2ac306392906490db219df07c7399fee54d000da486e"},
|
{file = "time_machine-2.7.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7c35a6971ebfe10ed4141a5e5da98cbb7b0f029385d7d97f47b19772e34cdd8a"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2ec280e2a7225533ec623d422ad8dbe4017473fc39b0a68411969a157299ecb4"},
|
{file = "time_machine-2.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c2c3a7f36e3c12f229c651a9e58e5a034d2360c27bea24bfebf95001ce1359b4"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:857ad51f7361e7c9239a90147339f771fdfba09fb9b60357f70e94e7ff75f3d6"},
|
{file = "time_machine-2.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16ba783c4836a7dc74221a6b937deb88805ccb208a246a160d716c426c074462"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:876f58eb6edf587d6faf91440c35016bfefa753c7412e65cc1138ee0df1afc31"},
|
{file = "time_machine-2.7.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b7ba3a382b96db3f47c9f93207aefdc2817563e3ec727dbda38399035bcd475"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20503e31c76cd1f731050e815788f9671e79f0db753ec5f2ac42962f36b7ad2"},
|
{file = "time_machine-2.7.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:514413eb7f0b55bd36a5e2ab67b357430c8038382ddb5896ff67f73668b1a23f"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5117e1a0bafbe8a7c975f8cd3f41f8148f996951fddb07c2bc7331afbbd6f329"},
|
{file = "time_machine-2.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62a8ae6dbb9ed623f7e42089661974a8fa256c4c64c5391d1f6c60da2b8d54c8"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:52eed92cac2e6f303d33f65b21a48803c0c173ef93bd87192aa06f92f3e63d4e"},
|
{file = "time_machine-2.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fcebeff41e50445100f3f881699d8deeda8f1c6dd80c0c0381b61977aae9dedc"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c8230b7a8c3f664cbe8e25101b684cb8dc2c5c7f97bf7d4b99fa4d193d685511"},
|
{file = "time_machine-2.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:985e773fa70a03b41ef33e1e77b0a655ca1503b77f60d52b613fe1707636b93d"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-win32.whl", hash = "sha256:31e2c58378e678054231b56544b42f51438b0423755e3345997afead6145db5f"},
|
{file = "time_machine-2.7.0-cp38-cp38-win32.whl", hash = "sha256:0579fa83e608ef4f1b16bd63e0a01c2bec5c4b3f8c2349a158af2c886cc8e0b5"},
|
||||||
{file = "time_machine-2.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c4f80e85ed52b4e22c8b966f50b82063b571bbd2ed2906c084d249337a1b1a0"},
|
{file = "time_machine-2.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc735fdfa2cbe00f63ed66c48cf32c13b91b9dd9817bec37bdb9c2df5ea09da8"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9c0b607c7b2b0c9be0a56f0ab41a7e1c5e0c251faee4d7a7d7a816fc4fa43ba5"},
|
{file = "time_machine-2.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a1ae378d4bd4101edd605e82da4859e0a5c509ddf2df0f8094160d795fc22777"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d7fa4d2a70ab088b967e273468b1e9e0020aa529843ed907b2a889c92e09766"},
|
{file = "time_machine-2.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:747825e968290c8ec98202a60411586edd9b8669cbbf31f61224240e5951d1fb"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182b5a30c6b6e2d761d181d42b71469b237e1dd06267428a008e032e5e33d4e3"},
|
{file = "time_machine-2.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd4797046126bfa1524a9ef8acac83282ce9365ad2cbcab0843ee2662e103502"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ef26ee01527d0ac44e73d27b5d58c04322d9bf0e46874545ff27c46adebf026"},
|
{file = "time_machine-2.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50585410381f9b9799fdd0ab7b1ac5009a341127cf9e72222bc2cb870eddf44d"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c088359f5d571ba6ab3c99fce5d55d433f96117c33b8f75c49a0ef610e6ae41e"},
|
{file = "time_machine-2.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd0b8cf6ad69ed54d2e11c2d5cc3681c6defc7ce022121312cead610d54685c3"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:20fb6e83d30a37294cb274124f99d97a1990d8919a9ac8517d8bec1ef370b1e8"},
|
{file = "time_machine-2.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:72d03615caf11eb47c7efeb48c839e307a18c272637d967b697a02f6561dad3c"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fb4d1146bac245908ad8d62e16e92c0e1d4b194499d7f6301107e837a7fd92aa"},
|
{file = "time_machine-2.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fa5a0a01f9562be8a13338255a9497b3c1fc8bb018582fa4a881184a9f480c9a"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b81076bbf81fa41279b232e5c0a50194345801b26fd0b4610ec040c5626a5ec1"},
|
{file = "time_machine-2.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:06473db7145f82992d47f02d04a164abe499ec3f4ab24fc9877d49a7e46ae7a0"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-win32.whl", hash = "sha256:7200f094198b7d382942caf6cc89d45464acf19b7534fde081f10f532021f39e"},
|
{file = "time_machine-2.7.0-cp39-cp39-win32.whl", hash = "sha256:b377991a5ead8f4d4c887293fde66cb1e8abb5c709e311c724016d16e2ef4c7d"},
|
||||||
{file = "time_machine-2.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:cc2d77ba51588ba3e1e539dbfd2459092159c577bf777b5c976e5cfbbf84d554"},
|
{file = "time_machine-2.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:5ba2f608684b19be35b6bc0dca362eca2ded0b7c6f1ff88ed1e55b8dfdfb869d"},
|
||||||
]
|
]
|
||||||
toml = [
|
toml = [
|
||||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||||
@@ -1408,30 +1452,31 @@ virtualenv = [
|
|||||||
{file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"},
|
{file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"},
|
||||||
]
|
]
|
||||||
watchdog = [
|
watchdog = [
|
||||||
{file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:177bae28ca723bc00846466016d34f8c1d6a621383b6caca86745918d55c7383"},
|
{file = "watchdog-2.1.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:676263bee67b165f16b05abc52acc7a94feac5b5ab2449b491f1a97638a79277"},
|
||||||
{file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d1cf7dfd747dec519486a98ef16097e6c480934ef115b16f18adb341df747a4"},
|
{file = "watchdog-2.1.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aa68d2d9a89d686fae99d28a6edf3b18595e78f5adf4f5c18fbfda549ac0f20c"},
|
||||||
{file = "watchdog-2.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f14ce6adea2af1bba495acdde0e510aecaeb13b33f7bd2f6324e551b26688ca"},
|
{file = "watchdog-2.1.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e2e51c53666850c3ecffe9d265fc5d7351db644de17b15e9c685dd3cdcd6f97"},
|
||||||
{file = "watchdog-2.1.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4d0e98ac2e8dd803a56f4e10438b33a2d40390a72750cff4939b4b274e7906fa"},
|
{file = "watchdog-2.1.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7721ac736170b191c50806f43357407138c6748e4eb3e69b071397f7f7aaeedd"},
|
||||||
{file = "watchdog-2.1.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:81982c7884aac75017a6ecc72f1a4fedbae04181a8665a34afce9539fc1b3fab"},
|
{file = "watchdog-2.1.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ce7376aed3da5fd777483fe5ebc8475a440c6d18f23998024f832134b2938e7b"},
|
||||||
{file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0b4a1fe6201c6e5a1926f5767b8664b45f0fcb429b62564a41f490ff1ce1dc7a"},
|
{file = "watchdog-2.1.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f9ee4c6bf3a1b2ed6be90a2d78f3f4bbd8105b6390c04a86eb48ed67bbfa0b0b"},
|
||||||
{file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6e6ae29b72977f2e1ee3d0b760d7ee47896cb53e831cbeede3e64485e5633cc8"},
|
{file = "watchdog-2.1.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:68dbe75e0fa1ba4d73ab3f8e67b21770fbed0651d32ce515cd38919a26873266"},
|
||||||
{file = "watchdog-2.1.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b9777664848160449e5b4260e0b7bc1ae0f6f4992a8b285db4ec1ef119ffa0e2"},
|
{file = "watchdog-2.1.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0c520009b8cce79099237d810aaa19bc920941c268578436b62013b2f0102320"},
|
||||||
{file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:19b36d436578eb437e029c6b838e732ed08054956366f6dd11875434a62d2b99"},
|
{file = "watchdog-2.1.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efcc8cbc1b43902571b3dce7ef53003f5b97fe4f275fe0489565fc6e2ebe3314"},
|
||||||
{file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b61acffaf5cd5d664af555c0850f9747cc5f2baf71e54bbac164c58398d6ca7b"},
|
{file = "watchdog-2.1.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:746e4c197ec1083581bb1f64d07d1136accf03437badb5ff8fcb862565c193b2"},
|
||||||
{file = "watchdog-2.1.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e877c70245424b06c41ac258023ea4bd0c8e4ff15d7c1368f17cd0ae6e351dd"},
|
{file = "watchdog-2.1.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ae17b6be788fb8e4d8753d8d599de948f0275a232416e16436363c682c6f850"},
|
||||||
{file = "watchdog-2.1.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d802d65262a560278cf1a65ef7cae4e2bc7ecfe19e5451349e4c67e23c9dc420"},
|
{file = "watchdog-2.1.8-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ddde157dc1447d8130cb5b8df102fad845916fe4335e3d3c3f44c16565becbb7"},
|
||||||
{file = "watchdog-2.1.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b3750ee5399e6e9c69eae8b125092b871ee9e2fcbd657a92747aea28f9056a5c"},
|
{file = "watchdog-2.1.8-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4978db33fc0934c92013ee163a9db158ec216099b69fce5aec790aba704da412"},
|
||||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_aarch64.whl", hash = "sha256:ed6d9aad09a2a948572224663ab00f8975fae242aa540509737bb4507133fa2d"},
|
{file = "watchdog-2.1.8-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b962de4d7d92ff78fb2dbc6a0cb292a679dea879a0eb5568911484d56545b153"},
|
||||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_armv7l.whl", hash = "sha256:b26e13e8008dcaea6a909e91d39b629a39635d1a8a7239dd35327c74f4388601"},
|
{file = "watchdog-2.1.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1e5d0fdfaa265c29dc12621913a76ae99656cf7587d03950dfeb3595e5a26102"},
|
||||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_i686.whl", hash = "sha256:0908bb50f6f7de54d5d31ec3da1654cb7287c6b87bce371954561e6de379d690"},
|
{file = "watchdog-2.1.8-py3-none-manylinux2014_armv7l.whl", hash = "sha256:036ed15f7cd656351bf4e17244447be0a09a61aaa92014332d50719fc5973bc0"},
|
||||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64.whl", hash = "sha256:bdcbf75580bf4b960fb659bbccd00123d83119619195f42d721e002c1621602f"},
|
{file = "watchdog-2.1.8-py3-none-manylinux2014_i686.whl", hash = "sha256:2962628a8777650703e8f6f2593065884c602df7bae95759b2df267bd89b2ef5"},
|
||||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:81a5861d0158a7e55fe149335fb2bbfa6f48cbcbd149b52dbe2cd9a544034bbd"},
|
{file = "watchdog-2.1.8-py3-none-manylinux2014_ppc64.whl", hash = "sha256:156ec3a94695ea68cfb83454b98754af6e276031ba1ae7ae724dc6bf8973b92a"},
|
||||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_s390x.whl", hash = "sha256:03b43d583df0f18782a0431b6e9e9965c5b3f7cf8ec36a00b930def67942c385"},
|
{file = "watchdog-2.1.8-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:47598fe6713fc1fee86b1ca85c9cbe77e9b72d002d6adeab9c3b608f8a5ead10"},
|
||||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ae934e34c11aa8296c18f70bf66ed60e9870fcdb4cc19129a04ca83ab23e7055"},
|
{file = "watchdog-2.1.8-py3-none-manylinux2014_s390x.whl", hash = "sha256:fed4de6e45a4f16e4046ea00917b4fe1700b97244e5d114f594b4a1b9de6bed8"},
|
||||||
{file = "watchdog-2.1.7-py3-none-win32.whl", hash = "sha256:49639865e3db4be032a96695c98ac09eed39bbb43fe876bb217da8f8101689a6"},
|
{file = "watchdog-2.1.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:24dedcc3ce75e150f2a1d704661f6879764461a481ba15a57dc80543de46021c"},
|
||||||
{file = "watchdog-2.1.7-py3-none-win_amd64.whl", hash = "sha256:340b875aecf4b0e6672076a6f05cfce6686935559bb6d34cebedee04126a9566"},
|
{file = "watchdog-2.1.8-py3-none-win32.whl", hash = "sha256:6ddf67bc9f413791072e3afb466e46cc72c6799ba73dea18439b412e8f2e3257"},
|
||||||
{file = "watchdog-2.1.7-py3-none-win_ia64.whl", hash = "sha256:351e09b6d9374d5bcb947e6ac47a608ec25b9d70583e9db00b2fcdb97b00b572"},
|
{file = "watchdog-2.1.8-py3-none-win_amd64.whl", hash = "sha256:88ef3e8640ef0a64b7ad7394b0f23384f58ac19dd759da7eaa9bc04b2898943f"},
|
||||||
{file = "watchdog-2.1.7.tar.gz", hash = "sha256:3fd47815353be9c44eebc94cc28fe26b2b0c5bd889dafc4a5a7cbdf924143480"},
|
{file = "watchdog-2.1.8-py3-none-win_ia64.whl", hash = "sha256:0fb60c7d31474b21acba54079ce9ff0136411183e9a591369417cddb1d7d00d7"},
|
||||||
|
{file = "watchdog-2.1.8.tar.gz", hash = "sha256:6d03149126864abd32715d4e9267d2754cede25a69052901399356ad3bc5ecff"},
|
||||||
]
|
]
|
||||||
yarl = [
|
yarl = [
|
||||||
{file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
|
{file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ rich = "^12.4.0"
|
|||||||
click = "8.1.2"
|
click = "8.1.2"
|
||||||
importlib-metadata = "^4.11.3"
|
importlib-metadata = "^4.11.3"
|
||||||
typing-extensions = { version = "^4.0.0", python = "<3.8" }
|
typing-extensions = { version = "^4.0.0", python = "<3.8" }
|
||||||
|
msgpack = "^1.0.3"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^6.2.3"
|
pytest = "^6.2.3"
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
transition: color 300ms linear, background 300ms linear;
|
transition: color 300ms linear, background 300ms linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
scrollbar-background: $panel-darken-2;
|
scrollbar-background: $panel-darken-2;
|
||||||
scrollbar-background-hover: $panel-darken-3;
|
scrollbar-background-hover: $panel-darken-3;
|
||||||
|
|||||||
@@ -246,8 +246,3 @@ class Animator:
|
|||||||
animation = self._animations[animation_key]
|
animation = self._animations[animation_key]
|
||||||
if animation(animation_time):
|
if animation(animation_time):
|
||||||
del self._animations[animation_key]
|
del self._animations[animation_key]
|
||||||
self.on_animation_frame()
|
|
||||||
|
|
||||||
def on_animation_frame(self) -> None:
|
|
||||||
# TODO: We should be able to do animation without refreshing everything
|
|
||||||
self.target.screen.refresh_layout()
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ from rich.style import Style
|
|||||||
from . import errors
|
from . import errors
|
||||||
from .geometry import Region, Offset, Size
|
from .geometry import Region, Offset, Size
|
||||||
|
|
||||||
|
|
||||||
from ._loop import loop_last
|
from ._loop import loop_last
|
||||||
from ._segment_tools import line_crop
|
from ._segment_tools import line_crop
|
||||||
from ._types import Lines
|
from ._types import Lines
|
||||||
@@ -38,7 +37,6 @@ else: # pragma: no cover
|
|||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .screen import Screen
|
|
||||||
from .widget import Widget
|
from .widget import Widget
|
||||||
|
|
||||||
|
|
||||||
@@ -59,6 +57,11 @@ class MapGeometry(NamedTuple):
|
|||||||
virtual_size: Size # The virtual size (scrollable region) of a widget if it is a container
|
virtual_size: Size # The virtual size (scrollable region) of a widget if it is a container
|
||||||
container_size: Size # The container size (area not occupied by scrollbars)
|
container_size: Size # The container size (area not occupied by scrollbars)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def visible_region(self) -> Region:
|
||||||
|
"""The Widget region after clipping."""
|
||||||
|
return self.clip.intersection(self.region)
|
||||||
|
|
||||||
|
|
||||||
CompositorMap: TypeAlias = "dict[Widget, MapGeometry]"
|
CompositorMap: TypeAlias = "dict[Widget, MapGeometry]"
|
||||||
|
|
||||||
@@ -78,7 +81,6 @@ class LayoutUpdate:
|
|||||||
new_line = Segment.line()
|
new_line = Segment.line()
|
||||||
move_to = Control.move_to
|
move_to = Control.move_to
|
||||||
for last, (y, line) in loop_last(enumerate(self.lines, self.region.y)):
|
for last, (y, line) in loop_last(enumerate(self.lines, self.region.y)):
|
||||||
yield Control.home()
|
|
||||||
yield move_to(x, y)
|
yield move_to(x, y)
|
||||||
yield from line
|
yield from line
|
||||||
if not last:
|
if not last:
|
||||||
@@ -144,6 +146,17 @@ class Compositor:
|
|||||||
# The points in each line where the line bisects the left and right edges of the widget
|
# The points in each line where the line bisects the left and right edges of the widget
|
||||||
self._cuts: list[list[int]] | None = None
|
self._cuts: list[list[int]] | None = None
|
||||||
|
|
||||||
|
# Regions that require an update
|
||||||
|
self._dirty_regions: set[Region] = set()
|
||||||
|
|
||||||
|
def add_dirty_regions(self, regions: Iterable[Region]) -> None:
|
||||||
|
"""Add dirty regions to be repainted next call to render.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
regions (Iterable[Region]): Regions that are "dirty" (changed since last render).
|
||||||
|
"""
|
||||||
|
self._dirty_regions.update(regions)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _regions_to_spans(
|
def _regions_to_spans(
|
||||||
cls, regions: Iterable[Region]
|
cls, regions: Iterable[Region]
|
||||||
@@ -198,11 +211,12 @@ class Compositor:
|
|||||||
self.root = parent
|
self.root = parent
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
# TODO: Handle virtual size
|
# Keep a copy of the old map because we're going to compare it with the update
|
||||||
|
old_map = self.map.copy()
|
||||||
|
old_widgets = old_map.keys()
|
||||||
map, widgets = self._arrange_root(parent)
|
map, widgets = self._arrange_root(parent)
|
||||||
|
new_widgets = map.keys()
|
||||||
|
|
||||||
old_widgets = set(self.map.keys())
|
|
||||||
new_widgets = set(map.keys())
|
|
||||||
# Newly visible widgets
|
# Newly visible widgets
|
||||||
shown_widgets = new_widgets - old_widgets
|
shown_widgets = new_widgets - old_widgets
|
||||||
# Newly hidden widgets
|
# Newly hidden widgets
|
||||||
@@ -212,7 +226,7 @@ class Compositor:
|
|||||||
self.map = map
|
self.map = map
|
||||||
self.widgets = widgets
|
self.widgets = widgets
|
||||||
|
|
||||||
# Copy renders if the size hasn't changed
|
# Get a map of regions
|
||||||
self.regions = {
|
self.regions = {
|
||||||
widget: (region, clip)
|
widget: (region, clip)
|
||||||
for widget, (region, _order, clip, _, _) in map.items()
|
for widget, (region, _order, clip, _, _) in map.items()
|
||||||
@@ -225,6 +239,21 @@ class Compositor:
|
|||||||
if widget in old_widgets and widget.size != region.size
|
if widget in old_widgets and widget.size != region.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Gets pairs of tuples of (Widget, MapGeometry) which have changed
|
||||||
|
# i.e. if something is moved / deleted / added
|
||||||
|
screen = size.region
|
||||||
|
if screen not in self._dirty_regions:
|
||||||
|
crop_screen = screen.intersection
|
||||||
|
changes: set[tuple[Widget, MapGeometry]] = (
|
||||||
|
self.map.items() ^ old_map.items()
|
||||||
|
)
|
||||||
|
self._dirty_regions.update(
|
||||||
|
[
|
||||||
|
crop_screen(map_geometry.visible_region)
|
||||||
|
for _, map_geometry in changes
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return ReflowResult(
|
return ReflowResult(
|
||||||
hidden=hidden_widgets,
|
hidden=hidden_widgets,
|
||||||
shown=shown_widgets,
|
shown=shown_widgets,
|
||||||
@@ -516,29 +545,32 @@ class Compositor:
|
|||||||
]
|
]
|
||||||
return segment_lines
|
return segment_lines
|
||||||
|
|
||||||
def render(self, regions: list[Region] | None = None) -> RenderableType:
|
def render(self) -> RenderableType:
|
||||||
"""Render a layout.
|
"""Render a layout.
|
||||||
|
|
||||||
Args:
|
|
||||||
clip (Optional[Region]): Region to clip to.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
SegmentLines: A renderable
|
SegmentLines: A renderable
|
||||||
"""
|
"""
|
||||||
width, height = self.size
|
width, height = self.size
|
||||||
screen_region = Region(0, 0, width, height)
|
screen_region = Region(0, 0, width, height)
|
||||||
if regions:
|
|
||||||
|
update_regions = self._dirty_regions.copy()
|
||||||
|
if screen_region in update_regions:
|
||||||
|
# If one of the updates is the entire screen, then we only need one update
|
||||||
|
update_regions.clear()
|
||||||
|
self._dirty_regions.clear()
|
||||||
|
|
||||||
|
if update_regions:
|
||||||
# Create a crop regions that surrounds all updates
|
# Create a crop regions that surrounds all updates
|
||||||
crop = Region.from_union(regions).intersection(screen_region)
|
crop = Region.from_union(list(update_regions)).intersection(screen_region)
|
||||||
spans = list(self._regions_to_spans(regions))
|
spans = list(self._regions_to_spans(update_regions))
|
||||||
is_rendered_line = {y for y, _, _ in spans}.__contains__
|
is_rendered_line = {y for y, _, _ in spans}.__contains__
|
||||||
else:
|
else:
|
||||||
crop = screen_region
|
crop = screen_region
|
||||||
spans = []
|
spans = []
|
||||||
is_rendered_line = lambda y: True
|
is_rendered_line = lambda y: True
|
||||||
|
|
||||||
_Segment = Segment
|
divide = Segment.divide
|
||||||
divide = _Segment.divide
|
|
||||||
|
|
||||||
# Maps each cut on to a list of segments
|
# Maps each cut on to a list of segments
|
||||||
cuts = self.cuts
|
cuts = self.cuts
|
||||||
@@ -569,7 +601,6 @@ class Compositor:
|
|||||||
else:
|
else:
|
||||||
render_x = render_region.x
|
render_x = render_region.x
|
||||||
relative_cuts = [cut - render_x for cut in final_cuts]
|
relative_cuts = [cut - render_x for cut in final_cuts]
|
||||||
# print(relative_cuts)
|
|
||||||
_, *cut_segments = divide(line, relative_cuts)
|
_, *cut_segments = divide(line, relative_cuts)
|
||||||
|
|
||||||
# Since we are painting front to back, the first segments for a cut "wins"
|
# Since we are painting front to back, the first segments for a cut "wins"
|
||||||
@@ -578,7 +609,7 @@ class Compositor:
|
|||||||
if chops_line[cut] is None:
|
if chops_line[cut] is None:
|
||||||
chops_line[cut] = segments
|
chops_line[cut] = segments
|
||||||
|
|
||||||
if regions:
|
if update_regions:
|
||||||
crop_y, crop_y2 = crop.y_extents
|
crop_y, crop_y2 = crop.y_extents
|
||||||
render_lines = self._assemble_chops(chops[crop_y:crop_y2])
|
render_lines = self._assemble_chops(chops[crop_y:crop_y2])
|
||||||
render_spans = [
|
render_spans = [
|
||||||
@@ -596,15 +627,13 @@ class Compositor:
|
|||||||
) -> RenderResult:
|
) -> RenderResult:
|
||||||
yield self.render()
|
yield self.render()
|
||||||
|
|
||||||
def update_widgets(self, widgets: set[Widget]) -> RenderableType | None:
|
def update_widgets(self, widgets: set[Widget]) -> None:
|
||||||
"""Update a given widget in the composition.
|
"""Update a given widget in the composition.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
console (Console): Console instance.
|
console (Console): Console instance.
|
||||||
widget (Widget): Widget to update.
|
widget (Widget): Widget to update.
|
||||||
|
|
||||||
Returns:
|
|
||||||
LayoutUpdate | None: A renderable or None if nothing to render.
|
|
||||||
"""
|
"""
|
||||||
regions: list[Region] = []
|
regions: list[Region] = []
|
||||||
add_region = regions.append
|
add_region = regions.append
|
||||||
@@ -613,5 +642,4 @@ class Compositor:
|
|||||||
update_region = region.intersection(clip)
|
update_region = region.intersection(clip)
|
||||||
if update_region:
|
if update_region:
|
||||||
add_region(update_region)
|
add_region(update_region)
|
||||||
update = self.render(regions or None)
|
self.add_dirty_regions(regions)
|
||||||
return update
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from datetime import datetime
|
||||||
import inspect
|
import inspect
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
@@ -28,10 +30,8 @@ else:
|
|||||||
import rich
|
import rich
|
||||||
import rich.repr
|
import rich.repr
|
||||||
from rich.console import Console, RenderableType
|
from rich.console import Console, RenderableType
|
||||||
from rich.control import Control
|
|
||||||
from rich.measure import Measurement
|
from rich.measure import Measurement
|
||||||
from rich.protocol import is_renderable
|
from rich.protocol import is_renderable
|
||||||
from rich.screen import Screen as ScreenRenderable
|
|
||||||
from rich.segment import Segments
|
from rich.segment import Segments
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
from rich.traceback import Traceback
|
from rich.traceback import Traceback
|
||||||
@@ -192,7 +192,6 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
self.registry: set[MessagePump] = set()
|
self.registry: set[MessagePump] = set()
|
||||||
self.devtools = DevtoolsClient()
|
self.devtools = DevtoolsClient()
|
||||||
self._return_value: ReturnType | None = None
|
self._return_value: ReturnType | None = None
|
||||||
self._focus_timer: Timer | None = None
|
|
||||||
|
|
||||||
self.css_monitor = (
|
self.css_monitor = (
|
||||||
FileMonitor(css_path, self._on_css_change)
|
FileMonitor(css_path, self._on_css_change)
|
||||||
@@ -263,10 +262,6 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
Returns:
|
Returns:
|
||||||
Widget | None: Newly focused widget, or None for no focus.
|
Widget | None: Newly focused widget, or None for no focus.
|
||||||
"""
|
"""
|
||||||
if self._focus_timer:
|
|
||||||
# Cancel the timer that clears the show focus class
|
|
||||||
# We will be creating a new timer to extend the time until the focus is hidden
|
|
||||||
self._focus_timer.stop_no_wait()
|
|
||||||
focusable_widgets = self.focus_chain
|
focusable_widgets = self.focus_chain
|
||||||
|
|
||||||
if not focusable_widgets:
|
if not focusable_widgets:
|
||||||
@@ -284,12 +279,10 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
self.set_focus(focusable_widgets[0])
|
self.set_focus(focusable_widgets[0])
|
||||||
else:
|
else:
|
||||||
# Only move the focus if we are currently showing the focus
|
# Only move the focus if we are currently showing the focus
|
||||||
if direction and self.has_class("-show-focus"):
|
if direction:
|
||||||
current_index = (current_index + direction) % len(focusable_widgets)
|
current_index = (current_index + direction) % len(focusable_widgets)
|
||||||
self.set_focus(focusable_widgets[current_index])
|
self.set_focus(focusable_widgets[current_index])
|
||||||
|
|
||||||
self._focus_timer = self.set_timer(2, self.hide_focus)
|
|
||||||
self.add_class("-show-focus")
|
|
||||||
return self.focused
|
return self.focused
|
||||||
|
|
||||||
def show_focus(self) -> Widget | None:
|
def show_focus(self) -> Widget | None:
|
||||||
@@ -316,15 +309,6 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
"""
|
"""
|
||||||
return self._move_focus(-1)
|
return self._move_focus(-1)
|
||||||
|
|
||||||
def hide_focus(self) -> None:
|
|
||||||
"""Hide the focus.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Widget | None: Newly focused widget, or None for no focus.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.remove_class("-show-focus")
|
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
"""Yield child widgets for a container."""
|
"""Yield child widgets for a container."""
|
||||||
return
|
return
|
||||||
@@ -427,6 +411,49 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def action_screenshot(self, path: str | None = None) -> None:
|
||||||
|
"""Action to save a screenshot."""
|
||||||
|
self.save_screenshot(path)
|
||||||
|
|
||||||
|
def export_screenshot(self) -> str:
|
||||||
|
"""Export a SVG screenshot of the current screen.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (str | None, optional): Path of the SVG to save, or None to
|
||||||
|
generate a path automatically. Defaults to None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
console = Console(
|
||||||
|
width=self.console.width,
|
||||||
|
height=self.console.height,
|
||||||
|
file=io.StringIO(),
|
||||||
|
force_terminal=True,
|
||||||
|
color_system="truecolor",
|
||||||
|
record=True,
|
||||||
|
)
|
||||||
|
console.print(self.screen._compositor)
|
||||||
|
return console.export_svg(title=self.title)
|
||||||
|
|
||||||
|
def save_screenshot(self, path: str | None = None) -> str:
|
||||||
|
"""Save a screenshot of the current screen.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (str | None, optional): Path to SVG to save or None to pick
|
||||||
|
a filename automatically. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Filename of screenshot.
|
||||||
|
"""
|
||||||
|
if path is None:
|
||||||
|
svg_path = f"{self.title.lower()}_{datetime.now().isoformat()}.svg"
|
||||||
|
svg_path = svg_path.replace("/", "_").replace("\\", "_")
|
||||||
|
else:
|
||||||
|
svg_path = path
|
||||||
|
screenshot_svg = self.export_screenshot()
|
||||||
|
with open(svg_path, "w") as svg_file:
|
||||||
|
svg_file.write(screenshot_svg)
|
||||||
|
return svg_path
|
||||||
|
|
||||||
def bind(
|
def bind(
|
||||||
self,
|
self,
|
||||||
keys: str,
|
keys: str,
|
||||||
@@ -525,7 +552,6 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
|
|
||||||
def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
||||||
self.register(self.screen, *anon_widgets, **widgets)
|
self.register(self.screen, *anon_widgets, **widgets)
|
||||||
self.screen.refresh()
|
|
||||||
|
|
||||||
def push_screen(self, screen: Screen | None = None) -> Screen:
|
def push_screen(self, screen: Screen | None = None) -> Screen:
|
||||||
"""Push a new screen on the screen stack.
|
"""Push a new screen on the screen stack.
|
||||||
@@ -730,7 +756,6 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
widgets = list(self.compose())
|
widgets = list(self.compose())
|
||||||
if widgets:
|
if widgets:
|
||||||
self.mount(*widgets)
|
self.mount(*widgets)
|
||||||
self.screen.refresh()
|
|
||||||
|
|
||||||
async def on_idle(self) -> None:
|
async def on_idle(self) -> None:
|
||||||
"""Perform actions when there are no messages in the queue."""
|
"""Perform actions when there are no messages in the queue."""
|
||||||
@@ -816,13 +841,7 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
try:
|
try:
|
||||||
if self._sync_available:
|
if self._sync_available:
|
||||||
console.file.write("\x1bP=1s\x1b\\")
|
console.file.write("\x1bP=1s\x1b\\")
|
||||||
console.print(
|
console.print(self.screen._compositor)
|
||||||
ScreenRenderable(
|
|
||||||
Control.home(),
|
|
||||||
self.screen._compositor,
|
|
||||||
Control.home(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if self._sync_available:
|
if self._sync_available:
|
||||||
console.file.write("\x1bP=2s\x1b\\")
|
console.file.write("\x1bP=2s\x1b\\")
|
||||||
console.file.flush()
|
console.file.flush()
|
||||||
@@ -846,10 +865,15 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
return
|
return
|
||||||
if not self._closed:
|
if not self._closed:
|
||||||
console = self.console
|
console = self.console
|
||||||
|
if self._sync_available:
|
||||||
|
console.file.write("\x1bP=1s\x1b\\")
|
||||||
try:
|
try:
|
||||||
console.print(renderable)
|
console.print(renderable)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
self.on_exception(error)
|
self.on_exception(error)
|
||||||
|
if self._sync_available:
|
||||||
|
console.file.write("\x1bP=2s\x1b\\")
|
||||||
|
console.file.flush()
|
||||||
|
|
||||||
def measure(self, renderable: RenderableType, max_width=100_000) -> int:
|
def measure(self, renderable: RenderableType, max_width=100_000) -> int:
|
||||||
"""Get the optimal width for a widget or renderable.
|
"""Get the optimal width for a widget or renderable.
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
|
||||||
import datetime
|
import datetime
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
|
import msgpack
|
||||||
import pickle
|
import pickle
|
||||||
|
from time import time
|
||||||
from asyncio import Queue, Task, QueueFull
|
from asyncio import Queue, Task, QueueFull
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Type, Any, NamedTuple
|
from typing import Type, Any, NamedTuple
|
||||||
@@ -97,7 +98,7 @@ class DevtoolsClient:
|
|||||||
self.update_console_task: Task | None = None
|
self.update_console_task: Task | None = None
|
||||||
self.console: DevtoolsConsole = DevtoolsConsole(file=StringIO())
|
self.console: DevtoolsConsole = DevtoolsConsole(file=StringIO())
|
||||||
self.websocket: ClientWebSocketResponse | None = None
|
self.websocket: ClientWebSocketResponse | None = None
|
||||||
self.log_queue: Queue[str | Type[ClientShutdown]] | None = None
|
self.log_queue: Queue[str | bytes | Type[ClientShutdown]] | None = None
|
||||||
self.spillover: int = 0
|
self.spillover: int = 0
|
||||||
|
|
||||||
async def connect(self) -> None:
|
async def connect(self) -> None:
|
||||||
@@ -144,7 +145,10 @@ class DevtoolsClient:
|
|||||||
if log is ClientShutdown:
|
if log is ClientShutdown:
|
||||||
log_queue.task_done()
|
log_queue.task_done()
|
||||||
break
|
break
|
||||||
await websocket.send_str(log)
|
if isinstance(log, str):
|
||||||
|
await websocket.send_str(log)
|
||||||
|
else:
|
||||||
|
await websocket.send_bytes(log)
|
||||||
log_queue.task_done()
|
log_queue.task_done()
|
||||||
|
|
||||||
self.log_queue_task = asyncio.create_task(send_queued_logs())
|
self.log_queue_task = asyncio.create_task(send_queued_logs())
|
||||||
@@ -203,17 +207,18 @@ class DevtoolsClient:
|
|||||||
segments = self.console.export_segments()
|
segments = self.console.export_segments()
|
||||||
|
|
||||||
encoded_segments = self._encode_segments(segments)
|
encoded_segments = self._encode_segments(segments)
|
||||||
message = json.dumps(
|
message: bytes | None = msgpack.packb(
|
||||||
{
|
{
|
||||||
"type": "client_log",
|
"type": "client_log",
|
||||||
"payload": {
|
"payload": {
|
||||||
"timestamp": int(datetime.datetime.utcnow().timestamp()),
|
"timestamp": int(time()),
|
||||||
"path": getattr(log.caller, "filename", ""),
|
"path": getattr(log.caller, "filename", ""),
|
||||||
"line_number": getattr(log.caller, "lineno", 0),
|
"line_number": getattr(log.caller, "lineno", 0),
|
||||||
"encoded_segments": encoded_segments,
|
"segments": encoded_segments,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
assert message is not None
|
||||||
try:
|
try:
|
||||||
if self.log_queue:
|
if self.log_queue:
|
||||||
self.log_queue.put_nowait(message)
|
self.log_queue.put_nowait(message)
|
||||||
@@ -233,15 +238,15 @@ class DevtoolsClient:
|
|||||||
except QueueFull:
|
except QueueFull:
|
||||||
self.spillover += 1
|
self.spillover += 1
|
||||||
|
|
||||||
def _encode_segments(self, segments: list[Segment]) -> str:
|
@classmethod
|
||||||
"""Pickle and Base64 encode the list of Segments
|
def _encode_segments(cls, segments: list[Segment]) -> bytes:
|
||||||
|
"""Pickle a list of Segments
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
segments (list[Segment]): A list of Segments to encode
|
segments (list[Segment]): A list of Segments to encode
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The Segment list pickled with pickle protocol v3, then base64 encoded
|
bytes: The Segment list pickled with the latest protocol.
|
||||||
"""
|
"""
|
||||||
pickled = pickle.dumps(segments, protocol=3)
|
pickled = pickle.dumps(segments, protocol=4)
|
||||||
encoded = base64.b64encode(pickled)
|
return pickled
|
||||||
return str(encoded, encoding="utf-8")
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
@@ -72,19 +72,13 @@ class DevConsoleLog:
|
|||||||
def __rich_console__(
|
def __rich_console__(
|
||||||
self, console: Console, options: ConsoleOptions
|
self, console: Console, options: ConsoleOptions
|
||||||
) -> RenderResult:
|
) -> RenderResult:
|
||||||
local_time = (
|
local_time = datetime.fromtimestamp(self.unix_timestamp)
|
||||||
datetime.fromtimestamp(self.unix_timestamp)
|
|
||||||
.replace(tzinfo=timezone.utc)
|
|
||||||
.astimezone(tz=datetime.now().astimezone().tzinfo)
|
|
||||||
)
|
|
||||||
timezone_name = local_time.tzname()
|
|
||||||
table = Table.grid(expand=True)
|
table = Table.grid(expand=True)
|
||||||
table.add_column()
|
|
||||||
table.add_column()
|
|
||||||
file_link = escape(f"file://{Path(self.path).absolute()}")
|
file_link = escape(f"file://{Path(self.path).absolute()}")
|
||||||
file_and_line = escape(f"{Path(self.path).name}:{self.line_number}")
|
file_and_line = escape(f"{Path(self.path).name}:{self.line_number}")
|
||||||
table.add_row(
|
table.add_row(
|
||||||
f"[dim]{local_time.time()} {timezone_name}",
|
f"[dim]{local_time.time()}",
|
||||||
Align.right(
|
Align.right(
|
||||||
Text(f"{file_and_line}", style=Style(dim=True, link=file_link))
|
Text(f"{file_and_line}", style=Style(dim=True, link=file_link))
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from aiohttp.abc import Request
|
|||||||
from aiohttp.web_ws import WebSocketResponse
|
from aiohttp.web_ws import WebSocketResponse
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.markup import escape
|
from rich.markup import escape
|
||||||
|
import msgpack
|
||||||
|
|
||||||
from textual.devtools.renderables import (
|
from textual.devtools.renderables import (
|
||||||
DevConsoleLog,
|
DevConsoleLog,
|
||||||
@@ -160,26 +161,25 @@ class ClientHandler:
|
|||||||
"""
|
"""
|
||||||
last_message_time: float | None = None
|
last_message_time: float | None = None
|
||||||
while True:
|
while True:
|
||||||
message_json = await self.incoming_queue.get()
|
message = await self.incoming_queue.get()
|
||||||
if message_json is None:
|
if message is None:
|
||||||
self.incoming_queue.task_done()
|
self.incoming_queue.task_done()
|
||||||
break
|
break
|
||||||
|
|
||||||
type = message_json["type"]
|
type = message["type"]
|
||||||
if type == "client_log":
|
if type == "client_log":
|
||||||
path = message_json["payload"]["path"]
|
path = message["payload"]["path"]
|
||||||
line_number = message_json["payload"]["line_number"]
|
line_number = message["payload"]["line_number"]
|
||||||
timestamp = message_json["payload"]["timestamp"]
|
timestamp = message["payload"]["timestamp"]
|
||||||
encoded_segments = message_json["payload"]["encoded_segments"]
|
encoded_segments = message["payload"]["segments"]
|
||||||
decoded_segments = base64.b64decode(encoded_segments)
|
segments = pickle.loads(encoded_segments)
|
||||||
segments = pickle.loads(decoded_segments)
|
|
||||||
message_time = time()
|
message_time = time()
|
||||||
if (
|
if (
|
||||||
last_message_time is not None
|
last_message_time is not None
|
||||||
and message_time - last_message_time > 1
|
and message_time - last_message_time > 1
|
||||||
):
|
):
|
||||||
# Print a rule if it has been longer than a second since the last message
|
# Print a rule if it has been longer than a second since the last message
|
||||||
self.service.console.rule("")
|
self.service.console.rule()
|
||||||
self.service.console.print(
|
self.service.console.print(
|
||||||
DevConsoleLog(
|
DevConsoleLog(
|
||||||
segments=segments,
|
segments=segments,
|
||||||
@@ -190,7 +190,7 @@ class ClientHandler:
|
|||||||
)
|
)
|
||||||
last_message_time = message_time
|
last_message_time = message_time
|
||||||
elif type == "client_spillover":
|
elif type == "client_spillover":
|
||||||
spillover = int(message_json["payload"]["spillover"])
|
spillover = int(message["payload"]["spillover"])
|
||||||
info_renderable = DevConsoleNotice(
|
info_renderable = DevConsoleNotice(
|
||||||
f"Discarded {spillover} messages", level="warning"
|
f"Discarded {spillover} messages", level="warning"
|
||||||
)
|
)
|
||||||
@@ -219,21 +219,26 @@ class ClientHandler:
|
|||||||
await self.service.send_server_info(client_handler=self)
|
await self.service.send_server_info(client_handler=self)
|
||||||
async for message in self.websocket:
|
async for message in self.websocket:
|
||||||
message = cast(WSMessage, message)
|
message = cast(WSMessage, message)
|
||||||
if message.type == WSMsgType.TEXT:
|
|
||||||
|
if message.type in (WSMsgType.TEXT, WSMsgType.BINARY):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
message_json = json.loads(message.data)
|
if isinstance(message.data, bytes):
|
||||||
|
message = msgpack.unpackb(message.data)
|
||||||
|
else:
|
||||||
|
message = json.loads(message.data)
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
self.service.console.print(escape(str(message.data)))
|
self.service.console.print(escape(str(message.data)))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
type = message_json.get("type")
|
type = message.get("type")
|
||||||
if not type:
|
if not type:
|
||||||
continue
|
continue
|
||||||
if (
|
if (
|
||||||
type in QUEUEABLE_TYPES
|
type in QUEUEABLE_TYPES
|
||||||
and not self.service.shutdown_event.is_set()
|
and not self.service.shutdown_event.is_set()
|
||||||
):
|
):
|
||||||
await self.incoming_queue.put(message_json)
|
await self.incoming_queue.put(message)
|
||||||
elif message.type == WSMsgType.ERROR:
|
elif message.type == WSMsgType.ERROR:
|
||||||
self.service.console.print(
|
self.service.console.print(
|
||||||
DevConsoleNotice("Websocket error occurred", level="error")
|
DevConsoleNotice("Websocket error occurred", level="error")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Functions and classes to manage terminal geometry (anything involving coordinate
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, cast, Iterable, NamedTuple, Tuple, Union, TypeVar
|
from typing import Any, cast, NamedTuple, Sequence, Tuple, Union, TypeVar
|
||||||
|
|
||||||
SpacingDimensions = Union[int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]]
|
SpacingDimensions = Union[int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]]
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ class Region(NamedTuple):
|
|||||||
height: int = 0
|
height: int = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_union(cls, regions: list[Region]) -> Region:
|
def from_union(cls, regions: Sequence[Region]) -> Region:
|
||||||
"""Create a Region from the union of other regions.
|
"""Create a Region from the union of other regions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -94,13 +94,10 @@ class Screen(Widget):
|
|||||||
|
|
||||||
def _on_update(self) -> None:
|
def _on_update(self) -> None:
|
||||||
"""Called by the _update_timer."""
|
"""Called by the _update_timer."""
|
||||||
|
|
||||||
# Render widgets together
|
# Render widgets together
|
||||||
if self._dirty_widgets:
|
if self._dirty_widgets:
|
||||||
self.log(dirty=self._dirty_widgets)
|
self._compositor.update_widgets(self._dirty_widgets)
|
||||||
display_update = self._compositor.update_widgets(self._dirty_widgets)
|
self.app.display(self._compositor.render())
|
||||||
if display_update is not None:
|
|
||||||
self.app.display(display_update)
|
|
||||||
self._dirty_widgets.clear()
|
self._dirty_widgets.clear()
|
||||||
self._update_timer.pause()
|
self._update_timer.pause()
|
||||||
|
|
||||||
@@ -109,8 +106,8 @@ class Screen(Widget):
|
|||||||
if not self.size:
|
if not self.size:
|
||||||
return
|
return
|
||||||
# This paint the entire screen, so replaces the batched dirty widgets
|
# This paint the entire screen, so replaces the batched dirty widgets
|
||||||
|
self._compositor.update_widgets(self._dirty_widgets)
|
||||||
self._update_timer.pause()
|
self._update_timer.pause()
|
||||||
self._dirty_widgets.clear()
|
|
||||||
try:
|
try:
|
||||||
hidden, shown, resized = self._compositor.reflow(self, self.size)
|
hidden, shown, resized = self._compositor.reflow(self, self.size)
|
||||||
|
|
||||||
@@ -140,7 +137,10 @@ class Screen(Widget):
|
|||||||
except Exception as error:
|
except Exception as error:
|
||||||
self.app.on_exception(error)
|
self.app.on_exception(error)
|
||||||
return
|
return
|
||||||
self.app.refresh()
|
|
||||||
|
display_update = self._compositor.render()
|
||||||
|
if display_update is not None:
|
||||||
|
self.app.display(display_update)
|
||||||
|
|
||||||
async def handle_update(self, message: messages.Update) -> None:
|
async def handle_update(self, message: messages.Update) -> None:
|
||||||
message.stop()
|
message.stop()
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ class Widget(DOMNode):
|
|||||||
has_focus = Reactive(False)
|
has_focus = Reactive(False)
|
||||||
descendant_has_focus = Reactive(False)
|
descendant_has_focus = Reactive(False)
|
||||||
mouse_over = Reactive(False)
|
mouse_over = Reactive(False)
|
||||||
scroll_x = Reactive(0.0, repaint=False)
|
scroll_x = Reactive(0.0, repaint=False, layout=True)
|
||||||
scroll_y = Reactive(0.0, repaint=False)
|
scroll_y = Reactive(0.0, repaint=False, layout=True)
|
||||||
scroll_target_x = Reactive(0.0, repaint=False)
|
scroll_target_x = Reactive(0.0, repaint=False)
|
||||||
scroll_target_y = Reactive(0.0, repaint=False)
|
scroll_target_y = Reactive(0.0, repaint=False)
|
||||||
show_vertical_scrollbar = Reactive(False, layout=True)
|
show_vertical_scrollbar = Reactive(False, layout=True)
|
||||||
@@ -431,16 +431,13 @@ class Widget(DOMNode):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True if the scroll position changed, otherwise False.
|
bool: True if the scroll position changed, otherwise False.
|
||||||
"""
|
"""
|
||||||
screen = self.screen
|
|
||||||
try:
|
try:
|
||||||
widget_geometry = screen.find_widget(widget)
|
widget_region = widget.content_region
|
||||||
container_geometry = screen.find_widget(self)
|
container_region = self.content_region
|
||||||
except errors.NoWidget:
|
except errors.NoWidget:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
widget_region = widget.content_region + widget_geometry.region.origin
|
|
||||||
container_region = self.content_region + container_geometry.region.origin
|
|
||||||
|
|
||||||
if widget_region in container_region:
|
if widget_region in container_region:
|
||||||
# Widget is visible, nothing to do
|
# Widget is visible, nothing to do
|
||||||
return False
|
return False
|
||||||
@@ -610,10 +607,8 @@ class Widget(DOMNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def content_region(self) -> Region:
|
def content_region(self) -> Region:
|
||||||
"""A region relative to the Widget origin that contains the content."""
|
"""Gets an absolute region containing the content (minus padding and border)."""
|
||||||
x, y = self.styles.content_gutter.top_left
|
return self.region.shrink(self.styles.content_gutter)
|
||||||
width, height = self._container_size
|
|
||||||
return Region(x, y, width, height)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def content_offset(self) -> Offset:
|
def content_offset(self) -> Offset:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import time_machine
|
import time_machine
|
||||||
@@ -6,22 +6,23 @@ from rich.align import Align
|
|||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.segment import Segment
|
from rich.segment import Segment
|
||||||
|
|
||||||
|
import msgpack
|
||||||
from tests.utilities.render import wait_for_predicate
|
from tests.utilities.render import wait_for_predicate
|
||||||
from textual.devtools.renderables import DevConsoleLog, DevConsoleNotice
|
from textual.devtools.renderables import DevConsoleLog, DevConsoleNotice
|
||||||
|
|
||||||
TIMESTAMP = 1649166819
|
TIMESTAMP = 1649166819
|
||||||
WIDTH = 40
|
WIDTH = 40
|
||||||
# The string "Hello, world!" is encoded in the payload below
|
# The string "Hello, world!" is encoded in the payload below
|
||||||
EXAMPLE_LOG = {
|
_EXAMPLE_LOG = {
|
||||||
"type": "client_log",
|
"type": "client_log",
|
||||||
"payload": {
|
"payload": {
|
||||||
"encoded_segments": "gASVQgAAAAAAAABdlCiMDHJpY2guc2VnbWVudJSMB1NlZ"
|
"segments": b"\x80\x04\x955\x00\x00\x00\x00\x00\x00\x00]\x94\x8c\x0crich.segment\x94\x8c\x07Segment\x94\x93\x94\x8c\rHello, world!\x94NN\x87\x94\x81\x94a.",
|
||||||
"21lbnSUk5SMDUhlbGxvLCB3b3JsZCGUTk6HlIGUaAOMAQqUTk6HlIGUZS4=",
|
|
||||||
"line_number": 123,
|
"line_number": 123,
|
||||||
"path": "abc/hello.py",
|
"path": "abc/hello.py",
|
||||||
"timestamp": TIMESTAMP,
|
"timestamp": TIMESTAMP,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
EXAMPLE_LOG = msgpack.packb(_EXAMPLE_LOG)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
@@ -48,15 +49,10 @@ def test_log_message_render(console):
|
|||||||
right: Align = right_cells[0]
|
right: Align = right_cells[0]
|
||||||
|
|
||||||
# Since we can't guarantee the timezone the tests will run in...
|
# Since we can't guarantee the timezone the tests will run in...
|
||||||
local_time = (
|
local_time = datetime.fromtimestamp(TIMESTAMP)
|
||||||
datetime.fromtimestamp(TIMESTAMP)
|
|
||||||
.replace(tzinfo=timezone.utc)
|
|
||||||
.astimezone(tz=datetime.now().astimezone().tzinfo)
|
|
||||||
)
|
|
||||||
timezone_name = local_time.tzname()
|
|
||||||
string_timestamp = local_time.time()
|
string_timestamp = local_time.time()
|
||||||
|
|
||||||
assert left == f"[dim]{string_timestamp} {timezone_name}"
|
assert left == f"[dim]{string_timestamp}"
|
||||||
assert right.align == "right"
|
assert right.align == "right"
|
||||||
assert "hello.py:123" in right.renderable
|
assert "hello.py:123" in right.renderable
|
||||||
|
|
||||||
@@ -69,7 +65,7 @@ def test_internal_message_render(console):
|
|||||||
|
|
||||||
|
|
||||||
async def test_devtools_valid_client_log(devtools):
|
async def test_devtools_valid_client_log(devtools):
|
||||||
await devtools.websocket.send_json(EXAMPLE_LOG)
|
await devtools.websocket.send_bytes(EXAMPLE_LOG)
|
||||||
assert devtools.is_connected
|
assert devtools.is_connected
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import time_machine
|
|||||||
from aiohttp.web_ws import WebSocketResponse
|
from aiohttp.web_ws import WebSocketResponse
|
||||||
from rich.console import ConsoleDimensions
|
from rich.console import ConsoleDimensions
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
|
import msgpack
|
||||||
|
|
||||||
from tests.utilities.render import wait_for_predicate
|
from tests.utilities.render import wait_for_predicate
|
||||||
from textual.devtools.client import DevtoolsClient
|
from textual.devtools.client import DevtoolsClient
|
||||||
@@ -27,36 +28,39 @@ async def test_devtools_client_is_connected(devtools):
|
|||||||
assert devtools.is_connected
|
assert devtools.is_connected
|
||||||
|
|
||||||
|
|
||||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
@time_machine.travel(datetime.utcfromtimestamp(TIMESTAMP))
|
||||||
async def test_devtools_log_places_encodes_and_queues_message(devtools):
|
async def test_devtools_log_places_encodes_and_queues_message(devtools):
|
||||||
|
|
||||||
await devtools._stop_log_queue_processing()
|
await devtools._stop_log_queue_processing()
|
||||||
devtools.log(DevtoolsLog("Hello, world!", CALLER))
|
devtools.log(DevtoolsLog("Hello, world!", CALLER))
|
||||||
queued_log = await devtools.log_queue.get()
|
queued_log = await devtools.log_queue.get()
|
||||||
queued_log_json = json.loads(queued_log)
|
queued_log_data = msgpack.unpackb(queued_log)
|
||||||
assert queued_log_json == {
|
print(repr(queued_log_data))
|
||||||
|
assert queued_log_data == {
|
||||||
"type": "client_log",
|
"type": "client_log",
|
||||||
"payload": {
|
"payload": {
|
||||||
"timestamp": TIMESTAMP,
|
"timestamp": 1649166819,
|
||||||
"path": CALLER_PATH,
|
"path": "a/b/c.py",
|
||||||
"line_number": CALLER_LINENO,
|
"line_number": 123,
|
||||||
"encoded_segments": "gANdcQAoY3JpY2guc2VnbWVudApTZWdtZW50CnEBWA0AAABIZWxsbywgd29ybGQhcQJOTodxA4FxBGgBWAEAAAAKcQVOTodxBoFxB2Uu",
|
"segments": b"\x80\x04\x95B\x00\x00\x00\x00\x00\x00\x00]\x94(\x8c\x0crich.segment\x94\x8c\x07Segment\x94\x93\x94\x8c\rHello, world!\x94NN\x87\x94\x81\x94h\x03\x8c\x01\n\x94NN\x87\x94\x81\x94e.",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
@time_machine.travel(datetime.utcfromtimestamp(TIMESTAMP))
|
||||||
async def test_devtools_log_places_encodes_and_queues_many_logs_as_string(devtools):
|
async def test_devtools_log_places_encodes_and_queues_many_logs_as_string(devtools):
|
||||||
await devtools._stop_log_queue_processing()
|
await devtools._stop_log_queue_processing()
|
||||||
devtools.log(DevtoolsLog(("hello", "world"), CALLER))
|
devtools.log(DevtoolsLog(("hello", "world"), CALLER))
|
||||||
queued_log = await devtools.log_queue.get()
|
queued_log = await devtools.log_queue.get()
|
||||||
queued_log_json = json.loads(queued_log)
|
queued_log_data = msgpack.unpackb(queued_log)
|
||||||
assert queued_log_json == {
|
print(repr(queued_log_data))
|
||||||
|
assert queued_log_data == {
|
||||||
"type": "client_log",
|
"type": "client_log",
|
||||||
"payload": {
|
"payload": {
|
||||||
"timestamp": TIMESTAMP,
|
"timestamp": 1649166819,
|
||||||
"path": CALLER_PATH,
|
"path": "a/b/c.py",
|
||||||
"line_number": CALLER_LINENO,
|
"line_number": 123,
|
||||||
"encoded_segments": "gANdcQAoY3JpY2guc2VnbWVudApTZWdtZW50CnEBWAsAAABoZWxsbyB3b3JsZHECTk6HcQOBcQRoAVgBAAAACnEFTk6HcQaBcQdlLg==",
|
"segments": b"\x80\x04\x95@\x00\x00\x00\x00\x00\x00\x00]\x94(\x8c\x0crich.segment\x94\x8c\x07Segment\x94\x93\x94\x8c\x0bhello world\x94NN\x87\x94\x81\x94h\x03\x8c\x01\n\x94NN\x87\x94\x81\x94e.",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ from datetime import datetime
|
|||||||
|
|
||||||
import time_machine
|
import time_machine
|
||||||
|
|
||||||
|
import msgpack
|
||||||
from textual.devtools.redirect_output import StdoutRedirector
|
from textual.devtools.redirect_output import StdoutRedirector
|
||||||
|
|
||||||
TIMESTAMP = 1649166819
|
TIMESTAMP = 1649166819
|
||||||
|
|
||||||
|
|
||||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
@time_machine.travel(datetime.utcfromtimestamp(TIMESTAMP))
|
||||||
async def test_print_redirect_to_devtools_only(devtools):
|
async def test_print_redirect_to_devtools_only(devtools):
|
||||||
await devtools._stop_log_queue_processing()
|
await devtools._stop_log_queue_processing()
|
||||||
|
|
||||||
@@ -19,14 +20,15 @@ async def test_print_redirect_to_devtools_only(devtools):
|
|||||||
assert devtools.log_queue.qsize() == 1
|
assert devtools.log_queue.qsize() == 1
|
||||||
|
|
||||||
queued_log = await devtools.log_queue.get()
|
queued_log = await devtools.log_queue.get()
|
||||||
queued_log_json = json.loads(queued_log)
|
queued_log_data = msgpack.unpackb(queued_log)
|
||||||
payload = queued_log_json["payload"]
|
print(repr(queued_log_data))
|
||||||
|
payload = queued_log_data["payload"]
|
||||||
|
|
||||||
assert queued_log_json["type"] == "client_log"
|
assert queued_log_data["type"] == "client_log"
|
||||||
assert payload["timestamp"] == TIMESTAMP
|
assert payload["timestamp"] == TIMESTAMP
|
||||||
assert (
|
assert (
|
||||||
payload["encoded_segments"]
|
payload["segments"]
|
||||||
== "gANdcQAoY3JpY2guc2VnbWVudApTZWdtZW50CnEBWA0AAABIZWxsbywgd29ybGQhcQJOTodxA4FxBGgBWAEAAAAKcQVOTodxBoFxB2Uu"
|
== b"\x80\x04\x95B\x00\x00\x00\x00\x00\x00\x00]\x94(\x8c\x0crich.segment\x94\x8c\x07Segment\x94\x93\x94\x8c\rHello, world!\x94NN\x87\x94\x81\x94h\x03\x8c\x01\n\x94NN\x87\x94\x81\x94e."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -86,8 +88,10 @@ async def test_print_multiple_args_batched_as_one_log(devtools, in_memory_logfil
|
|||||||
|
|
||||||
assert queued_log_json["type"] == "client_log"
|
assert queued_log_json["type"] == "client_log"
|
||||||
assert payload["timestamp"] == TIMESTAMP
|
assert payload["timestamp"] == TIMESTAMP
|
||||||
assert payload[
|
assert (
|
||||||
"encoded_segments"] == "gANdcQAoY3JpY2guc2VnbWVudApTZWdtZW50CnEBWBQAAABIZWxsbyB3b3JsZCBtdWx0aXBsZXECTk6HcQOBcQRoAVgBAAAACnEFTk6HcQaBcQdlLg=="
|
payload["encoded_segments"]
|
||||||
|
== "gANdcQAoY3JpY2guc2VnbWVudApTZWdtZW50CnEBWBQAAABIZWxsbyB3b3JsZCBtdWx0aXBsZXECTk6HcQOBcQRoAVgBAAAACnEFTk6HcQaBcQdlLg=="
|
||||||
|
)
|
||||||
assert len(payload["path"]) > 0
|
assert len(payload["path"]) > 0
|
||||||
assert payload["line_number"] != 0
|
assert payload["line_number"] != 0
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,6 @@ def test_animator():
|
|||||||
|
|
||||||
animator()
|
animator()
|
||||||
assert animate_test.foo == 0
|
assert animate_test.foo == 0
|
||||||
assert animator._on_animation_frame_called
|
|
||||||
|
|
||||||
animator._time = 5
|
animator._time = 5
|
||||||
animator()
|
animator()
|
||||||
|
|||||||
@@ -31,26 +31,6 @@ async def test_focus_chain():
|
|||||||
assert focused == ["foo", "Paul", "baz"]
|
assert focused == ["foo", "Paul", "baz"]
|
||||||
|
|
||||||
|
|
||||||
async def test_show_focus():
|
|
||||||
app = App()
|
|
||||||
app.push_screen(Screen())
|
|
||||||
app.screen.add_children(
|
|
||||||
Focusable(id="foo"),
|
|
||||||
NonFocusable(id="bar"),
|
|
||||||
Focusable(Focusable(id="Paul"), id="container1"),
|
|
||||||
NonFocusable(Focusable(id="Jessica"), id="container2"),
|
|
||||||
Focusable(id="baz"),
|
|
||||||
)
|
|
||||||
|
|
||||||
focused = [widget.id for widget in app.focus_chain]
|
|
||||||
assert focused == ["foo", "Paul", "baz"]
|
|
||||||
|
|
||||||
assert app.focused is None
|
|
||||||
assert not app.has_class("-show-focus")
|
|
||||||
app.show_focus()
|
|
||||||
assert app.has_class("-show-focus")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_focus_next_and_previous():
|
async def test_focus_next_and_previous():
|
||||||
|
|
||||||
app = App()
|
app = App()
|
||||||
|
|||||||
Reference in New Issue
Block a user