2 Commits
1.3.1 ... 1.4.0

29 changed files with 142 additions and 33 deletions

View File

@@ -8,7 +8,7 @@ are designed to achieve identical contrast with the accents, and thus any one
of the options can be selected to change the feeling of the palette without of the options can be selected to change the feeling of the palette without
sacrificing readability. sacrificing readability.
![Theme preview](images/repo_preview_four_split.png) ![Theme preview](images/repo_preview_primary.png)
_(Preview of default light and dark theme variants)_ _(Preview of default light and dark theme variants)_
See screenshots for the full set of theme variants in [THEMES](THEMES.md) (also See screenshots for the full set of theme variants in [THEMES](THEMES.md) (also

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 KiB

View File

@@ -54,6 +54,7 @@ def register_parser(subparsers: _SubparserType) -> None:
# particularly good measure of perceptual distinction, so we'd prefer the # particularly good measure of perceptual distinction, so we'd prefer the
# former. # former.
parser.add_argument( parser.add_argument(
"-l",
"--l-base", "--l-base",
type=int, type=int,
default=20, default=20,
@@ -82,7 +83,7 @@ def register_parser(subparsers: _SubparserType) -> None:
parser.add_argument( parser.add_argument(
"--term-fg-gap", "--term-fg-gap",
type=int, type=int,
default=60, default=65,
help="Terminal foreground lightness gap (default: 60)", help="Terminal foreground lightness gap (default: 60)",
) )

View File

@@ -16,6 +16,8 @@ L_max: int = parameters.get("L_max", 98)
L_step: int = parameters.get("L_step", 5) L_step: int = parameters.get("L_step", 5)
L_points: list[int] = list(range(L_min, L_max+1)) L_points: list[int] = list(range(L_min, L_max+1))
# L-space just affects accuracy of chroma max
L_space = np.arange(0, 100 + L_step, L_step) L_space = np.arange(0, 100 + L_step, L_step)
monotone_C_map = parameters.get("monotone_C_map", {}) monotone_C_map = parameters.get("monotone_C_map", {})

View File

@@ -1,6 +1,8 @@
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from coloraide import Color
from monobiome.palette import compute_hlc_map
from monobiome.constants import ( from monobiome.constants import (
h_map, h_map,
L_space, L_space,
@@ -52,8 +54,14 @@ def plot_hue_chroma_bounds() -> None:
fig.subplots_adjust(top=0.9) fig.subplots_adjust(top=0.9)
handles, labels = axes[-1].get_legend_handles_labels() handles, labels = axes[-1].get_legend_handles_labels()
unique = dict(zip(labels, handles)) unique = dict(zip(labels, handles, strict=True))
fig.legend(unique.values(), unique.keys(), loc='lower center', bbox_to_anchor=(0.5, -0.06), ncol=3) fig.legend(
unique.values(),
unique.keys(),
loc='lower center',
bbox_to_anchor=(0.5, -0.06),
ncol=3
)
plt.suptitle("$C^*$ curves for hue groups") plt.suptitle("$C^*$ curves for hue groups")
plt.show() plt.show()
@@ -87,11 +95,12 @@ def plot_hue_chroma_star() -> None:
fig.show() fig.show()
def palette_image(palette, cell_size=40, keys=None): def palette_image(
if keys is None: palette: dict[str, dict[int, str]],
names = list(palette.keys()) cell_size: int = 40,
else: keys: list[str] | None = None
names = keys ) -> None:
names = list(palette.keys()) if keys is None else keys
row_count = len(names) row_count = len(names)
col_counts = [len(palette[n]) for n in names] col_counts = [len(palette[n]) for n in names]
@@ -117,8 +126,14 @@ def palette_image(palette, cell_size=40, keys=None):
return img, names, lightness_keys_per_row, cell_size, max_cols return img, names, lightness_keys_per_row, cell_size, max_cols
def show_palette(palette, cell_size=40, keys=None): def show_palette(
img, names, keys, cell_size, max_cols = palette_image(palette, cell_size, keys=keys) palette: dict[str, dict[int, str]],
cell_size: int = 40,
keys: list[str] | None = None
) -> None:
img, names, keys, cell_size, max_cols = palette_image(
palette, cell_size, keys=keys
)
fig_w = img.shape[1] / 100 fig_w = img.shape[1] / 100
fig_h = img.shape[0] / 100 fig_h = img.shape[0] / 100
@@ -130,15 +145,12 @@ def show_palette(palette, cell_size=40, keys=None):
ytick_pos = [(i + 0.5) * cell_size for i in range(len(names))] ytick_pos = [(i + 0.5) * cell_size for i in range(len(names))]
ax.set_yticks(ytick_pos) ax.set_yticks(ytick_pos)
ax.set_yticklabels(names) ax.set_yticklabels(names)
ax.set_ylim(img.shape[0], 0) # ensures rows render w/o half-cells
ax.set_ylim(img.shape[0], 0) # ensures rows render correctly without half-cells
plt.show() plt.show()
if __name__ == "__main__": if __name__ == "__main__":
from monobiome.constants import OKLCH_hL_dict
keys = [ keys = [
"alpine", "alpine",
"badlands", "badlands",
@@ -172,5 +184,6 @@ if __name__ == "__main__":
"blue", "blue",
] ]
show_palette(OKLCH_hL_dict, cell_size=25, keys=keys) hlc_map = compute_hlc_map("oklch")
# show_palette(OKLCH_hL_dict, cell_size=1, keys=term_keys) show_palette(hlc_map, cell_size=25, keys=keys)
# show_palette(hlc_map, cell_size=1, keys=term_keys)

View File

@@ -98,7 +98,7 @@ def generate_scheme_groups(
metric_map = { metric_map = {
"wcag": lambda mc,ac: ac.contrast(mc, method='wcag21'), "wcag": lambda mc,ac: ac.contrast(mc, method='wcag21'),
"oklch": lambda mc,ac: mc.distance(ac, space="oklch"), "oklch": oklch_distance,
"lightness": lambda mc,ac: abs(mc.coords()[0]-ac.coords()[0])*100, "lightness": lambda mc,ac: abs(mc.coords()[0]-ac.coords()[0])*100,
} }
@@ -131,6 +131,9 @@ def generate_scheme_groups(
("distance", distance), ("distance", distance),
("l_base", l_base), ("l_base", l_base),
("l_step", l_step), ("l_step", l_step),
("fg_gap", fg_gap),
("grey_gap", grey_gap),
("term_fg_gap", term_fg_gap),
] ]
# note how selection_bg steps up by `l_step`, selection_fg steps down by # note how selection_bg steps up by `l_step`, selection_fg steps down by
@@ -157,7 +160,7 @@ def generate_scheme_groups(
accent_pairs = [ accent_pairs = [
("black", f"f{{{{{biome}.l{l_base}}}}}"), ("black", f"f{{{{{biome}.l{l_base}}}}}"),
("grey", f"f{{{{{biome}.l{l_base+grey_gap}}}}}"), ("grey", f"f{{{{{biome}.l{l_base+grey_gap}}}}}"),
("white", f"f{{{{{biome}.l{l_base+term_fg_gap-l_step}}}}}"), ("white", f"f{{{{{biome}.l{l_base+term_fg_gap-2*l_step}}}}}"),
] ]
for color_name, mb_accent in accent_color_map.items(): for color_name, mb_accent in accent_color_map.items():
aL = int(100*accent_colors[mb_accent].coords()[0]) aL = int(100*accent_colors[mb_accent].coords()[0])
@@ -184,29 +187,42 @@ def generate_scheme(
term_color_map: dict[str, str], term_color_map: dict[str, str],
vim_color_map: dict[str, str], vim_color_map: dict[str, str],
) -> str: ) -> str:
l_sys = l_base
l_app = l_base + l_step
term_bright_offset = 10
# negate gaps if mode is light
if mode == "light":
l_step *= -1
fg_gap *= -1
grey_gap *= -1
term_fg_gap *= -1
term_bright_offset *= -1
meta, _, mt, ac = generate_scheme_groups( meta, _, mt, ac = generate_scheme_groups(
mode, biome, metric, distance, mode, biome, metric, distance,
l_base, l_step, l_sys, l_step,
fg_gap, grey_gap, term_fg_gap, fg_gap, grey_gap, term_fg_gap,
full_color_map full_color_map
) )
_, term, _, term_norm_ac = generate_scheme_groups( _, term, _, term_norm_ac = generate_scheme_groups(
mode, biome, metric, distance, mode, biome, metric, distance,
l_base + l_step, l_step, l_app, l_step,
fg_gap, grey_gap, term_fg_gap, fg_gap, grey_gap, term_fg_gap,
term_color_map term_color_map
) )
_, _, _, term_bright_ac = generate_scheme_groups( _, _, _, term_bright_ac = generate_scheme_groups(
mode, biome, metric, distance, mode, biome, metric, distance,
l_base + l_step + 10, l_step, l_app + term_bright_offset, l_step,
fg_gap, grey_gap, term_fg_gap, fg_gap, grey_gap, term_fg_gap,
term_color_map term_color_map
) )
_, _, vim_mt, vim_ac = generate_scheme_groups( _, _, vim_mt, vim_ac = generate_scheme_groups(
mode, biome, metric, distance, mode, biome, metric, distance,
l_base + l_step, l_step, l_app, l_step,
fg_gap, grey_gap, term_fg_gap, fg_gap, grey_gap, term_fg_gap,
vim_color_map vim_color_map
) )

View File

@@ -1,3 +1,4 @@
import math
from types import GenericAlias from types import GenericAlias
from argparse import ArgumentParser, _SubParsersAction from argparse import ArgumentParser, _SubParsersAction
@@ -6,5 +7,29 @@ from coloraide import Color
_SubParsersAction.__class_getitem__ = classmethod(GenericAlias) _SubParsersAction.__class_getitem__ = classmethod(GenericAlias)
_SubparserType = _SubParsersAction[ArgumentParser] _SubparserType = _SubParsersAction[ArgumentParser]
def oklch_distance(mc: Color, ac: Color) -> float: def oklch_distance(xc: Color, yc: Color) -> float:
return mc.distance(ac, space="oklch") """
Compute the distance between two colors in OKLCH space.
Note: `xc` and `yc` are presumed to be OKLCH colors already, such that
`.coords()` yields an `(l, c, h)` triple directly rather than first
requiring conversion. When we can make this assumption, we save roughly an
order of magnitude in runtime.
1. `xc.distance(yc, space="oklch")`: 500k evals takes ~2s
2. This method: 500k evals takes ~0.2s
"""
l1, c1, h1 = xc.coords()
l2, c2, h2 = yc.coords()
rad1 = h1 / 180 * math.pi
rad2 = h2 / 180 * math.pi
x1, y1 = c1 * math.cos(rad1), c1 * math.sin(rad1)
x2, y2 = c2 * math.cos(rad2), c2 * math.sin(rad2)
dx = x1 - x2
dy = y1 - y2
dz = l1 - l2
return (dx**2 + dy**2 + dz**2)**0.5

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "monobiome" name = "monobiome"
version = "1.4.0" version = "1.3.1"
description = "Monobiome color palette" description = "Monobiome color palette"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
@@ -18,6 +18,7 @@ dependencies = [
"numpy>=2.3.4", "numpy>=2.3.4",
"pillow>=12.0.0", "pillow>=12.0.0",
"plotly>=6.3.1", "plotly>=6.3.1",
"pyqt5>=5.15.11",
"scipy>=1.16.2", "scipy>=1.16.2",
] ]

View File

@@ -7,20 +7,22 @@ prefix=${1:-}
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
render_script="$script_dir/render.sh" render_script="$script_dir/render.sh"
#biomes=(alpine) biomes=(alpine badlands chaparral savanna grassland tundra reef heathland moorland)
#modes=(light dark) modes=(light)
biomes=(alpine)
modes=(dark)
for biome in "${biomes[@]}"; do for biome in "${biomes[@]}"; do
for mode in "${modes[@]}"; do for mode in "${modes[@]}"; do
echo "Applying $biome-$mode theme" echo "Applying $biome-$mode theme"
monobiome scheme "$mode" "$biome" \
-d 0.42 \
-l 85 \
-o ~/.config/symconf/groups/theme/monobiome-none.toml
symconf config \ symconf config \
-a kitty,nvim \ -a kitty,nvim \
-m "$mode" \ -m "$mode" \
-s "default-$biome-monobiome" \ -s monobiome \
-T font=Berkeley -T font=Berkeley
sleep 2 sleep 1
echo "Taking screenshot..." echo "Taking screenshot..."
"$render_script" 800 600 "images/render/$prefix-$biome-$mode.png" nvim \ "$render_script" 800 600 "images/render/$prefix-$biome-$mode.png" nvim \

BIN
test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

51
uv.lock generated
View File

@@ -622,7 +622,7 @@ wheels = [
[[package]] [[package]]
name = "monobiome" name = "monobiome"
version = "1.4.0" version = "1.3.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "coloraide" }, { name = "coloraide" },
@@ -634,6 +634,7 @@ dependencies = [
{ name = "numpy" }, { name = "numpy" },
{ name = "pillow" }, { name = "pillow" },
{ name = "plotly" }, { name = "plotly" },
{ name = "pyqt5" },
{ name = "scipy" }, { name = "scipy" },
] ]
@@ -653,6 +654,7 @@ requires-dist = [
{ name = "numpy", specifier = ">=2.3.4" }, { name = "numpy", specifier = ">=2.3.4" },
{ name = "pillow", specifier = ">=12.0.0" }, { name = "pillow", specifier = ">=12.0.0" },
{ name = "plotly", specifier = ">=6.3.1" }, { name = "plotly", specifier = ">=6.3.1" },
{ name = "pyqt5", specifier = ">=5.15.11" },
{ name = "scipy", specifier = ">=1.16.2" }, { name = "scipy", specifier = ">=1.16.2" },
] ]
@@ -1021,6 +1023,53 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" },
] ]
[[package]]
name = "pyqt5"
version = "5.15.11"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyqt5-qt5" },
{ name = "pyqt5-sip" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0e/07/c9ed0bd428df6f87183fca565a79fee19fa7c88c7f00a7f011ab4379e77a/PyQt5-5.15.11.tar.gz", hash = "sha256:fda45743ebb4a27b4b1a51c6d8ef455c4c1b5d610c90d2934c7802b5c1557c52", size = 3216775, upload-time = "2024-07-19T08:39:57.756Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/11/64/42ec1b0bd72d87f87bde6ceb6869f444d91a2d601f2e67cd05febc0346a1/PyQt5-5.15.11-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c8b03dd9380bb13c804f0bdb0f4956067f281785b5e12303d529f0462f9afdc2", size = 6579776, upload-time = "2024-07-19T08:39:19.775Z" },
{ url = "https://files.pythonhosted.org/packages/49/f5/3fb696f4683ea45d68b7e77302eff173493ac81e43d63adb60fa760b9f91/PyQt5-5.15.11-cp38-abi3-macosx_11_0_x86_64.whl", hash = "sha256:6cd75628f6e732b1ffcfe709ab833a0716c0445d7aec8046a48d5843352becb6", size = 7016415, upload-time = "2024-07-19T08:39:32.977Z" },
{ url = "https://files.pythonhosted.org/packages/b4/8c/4065950f9d013c4b2e588fe33cf04e564c2322842d84dbcbce5ba1dc28b0/PyQt5-5.15.11-cp38-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:cd672a6738d1ae33ef7d9efa8e6cb0a1525ecf53ec86da80a9e1b6ec38c8d0f1", size = 8188103, upload-time = "2024-07-19T08:39:40.561Z" },
{ url = "https://files.pythonhosted.org/packages/f3/f0/ae5a5b4f9b826b29ea4be841b2f2d951bcf5ae1d802f3732b145b57c5355/PyQt5-5.15.11-cp38-abi3-win32.whl", hash = "sha256:76be0322ceda5deecd1708a8d628e698089a1cea80d1a49d242a6d579a40babd", size = 5433308, upload-time = "2024-07-19T08:39:46.932Z" },
{ url = "https://files.pythonhosted.org/packages/56/d5/68eb9f3d19ce65df01b6c7b7a577ad3bbc9ab3a5dd3491a4756e71838ec9/PyQt5-5.15.11-cp38-abi3-win_amd64.whl", hash = "sha256:bdde598a3bb95022131a5c9ea62e0a96bd6fb28932cc1619fd7ba211531b7517", size = 6865864, upload-time = "2024-07-19T08:39:53.572Z" },
]
[[package]]
name = "pyqt5-qt5"
version = "5.15.18"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/90/bf01ac2132400997a3474051dd680a583381ebf98b2f5d64d4e54138dc42/pyqt5_qt5-5.15.18-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:8bb997eb903afa9da3221a0c9e6eaa00413bbeb4394d5706118ad05375684767", size = 39715743, upload-time = "2025-11-09T12:56:42.936Z" },
{ url = "https://files.pythonhosted.org/packages/24/8e/76366484d9f9dbe28e3bdfc688183433a7b82e314216e9b14c89e5fab690/pyqt5_qt5-5.15.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c656af9c1e6aaa7f59bf3d8995f2fa09adbf6762b470ed284c31dca80d686a26", size = 36798484, upload-time = "2025-11-09T12:56:59.998Z" },
{ url = "https://files.pythonhosted.org/packages/9a/46/ffe177f99f897a59dc237a20059020427bd2d3853d713992b8081933ddfe/pyqt5_qt5-5.15.18-py3-none-manylinux2014_x86_64.whl", hash = "sha256:bf2457e6371969736b4f660a0c153258fa03dbc6a181348218e6f05421682af7", size = 60864590, upload-time = "2025-11-09T12:57:26.724Z" },
]
[[package]]
name = "pyqt5-sip"
version = "12.17.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1e/4a/195cf4d2a7e1ff480b4cabcd51aa5c0068c03a19a97282317536e4a82e1e/pyqt5_sip-12.17.2.tar.gz", hash = "sha256:7f66565c2a13d34d8ad6aad08e953d355ea3fe466d991d51aa5a0966a5289f05", size = 104246, upload-time = "2025-12-06T13:19:06.821Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4b/3e/f5c7bc43668147ddc00a1a579f22639dffdbfb9470ce3a5bc1cf27e0d541/pyqt5_sip-12.17.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0bd1a8e59124a90a05f078bceeb9d4d93c3986c349030487c202fffde6612969", size = 124612, upload-time = "2025-12-06T13:18:49.614Z" },
{ url = "https://files.pythonhosted.org/packages/b9/41/63f81a53704425092558f1ec17adbed11787f4322e60a849e0539516b3aa/pyqt5_sip-12.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:55ff374feb4bad783241649c7b946e05d7e83d60b0755526ed8fb25bf54e3408", size = 282364, upload-time = "2025-12-06T13:18:51.179Z" },
{ url = "https://files.pythonhosted.org/packages/b5/cd/2b749a174e61394085d61cafb7dc3c11ddf40307edfb2d71cb9b71b7f320/pyqt5_sip-12.17.2-cp312-cp312-win32.whl", hash = "sha256:45dc6e2121d175fdab1431c448fd3e88c97caf873a33cb65efa2e9ad0056337b", size = 49521, upload-time = "2025-12-06T13:18:53.155Z" },
{ url = "https://files.pythonhosted.org/packages/73/ac/7f6d6a6a4505b251f1174092f09d5611c2ed66602c40d3411d93a1d2a95f/pyqt5_sip-12.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:e3bb16e43afd68dd013228075876cf8f8b1a7d86ba67767dd2c6a97be677c18d", size = 58003, upload-time = "2025-12-06T13:18:52.119Z" },
{ url = "https://files.pythonhosted.org/packages/38/b1/78432c271b2a5477f5fe1ad9eb69cdc482430230b8d552cf5cee393d7862/pyqt5_sip-12.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cfeee3c27f28c091d6a46f8befe9afcfafc76846846bedf1112d403a7299e864", size = 124589, upload-time = "2025-12-06T13:18:54.942Z" },
{ url = "https://files.pythonhosted.org/packages/e8/d9/6451973300f7dffe70476cad7fc4a59ffe08417ee4add6afb3288c91bd85/pyqt5_sip-12.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b5df33e198d5d7cccc8e081f80eb97b8d70100f887362074a029a6c19cb92c8b", size = 282040, upload-time = "2025-12-06T13:18:57.019Z" },
{ url = "https://files.pythonhosted.org/packages/c6/1e/241d9ddef5cb1bb3e3b5839b6f8c05ae727e196be82b4646ea4ef9475ef7/pyqt5_sip-12.17.2-cp313-cp313-win32.whl", hash = "sha256:2c0a278b8fc289d34d4e62bbb9ef6da96b45cc9ab3f6886397b1490d2b4a5604", size = 49497, upload-time = "2025-12-06T13:19:00.012Z" },
{ url = "https://files.pythonhosted.org/packages/fd/33/a393163b6299a7e0743fad86fbcb06cf219878fbdd629ee6cb46d2a4d9f7/pyqt5_sip-12.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:7e0d663b583a4d3ac63c9fbade2228de6ee628b44a025f5fd964b97dbbcbebc9", size = 58075, upload-time = "2025-12-06T13:18:58.069Z" },
{ url = "https://files.pythonhosted.org/packages/58/29/b4943def737d3f8876bfd4f9af1909892ae1998099695b3e81870c39aaa7/pyqt5_sip-12.17.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6f03c25a18294f2d66befc4f2adf3f35fceba877b937dc8a94783fa7da8b7345", size = 124591, upload-time = "2025-12-06T13:19:02.105Z" },
{ url = "https://files.pythonhosted.org/packages/18/62/e7ac79bb080d4e5a7d7fea50ca7d9231a7ded07e01f24d4e123f089e1630/pyqt5_sip-12.17.2-cp314-cp314-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a6d716801d512643b7b1f50dfbdcd16408fe9a6df907d8627b4ad82190604bec", size = 282412, upload-time = "2025-12-06T13:19:03.787Z" },
{ url = "https://files.pythonhosted.org/packages/92/11/b63ee88ffc2e04af90ced17dbe0d774f5f4e51122c13f8118e565707954e/pyqt5_sip-12.17.2-cp314-cp314-win32.whl", hash = "sha256:c617c29524fdcf826e619d77ffd0d6142622f8422adc2608ecc89edd3e605339", size = 50713, upload-time = "2025-12-06T13:19:05.851Z" },
{ url = "https://files.pythonhosted.org/packages/d0/70/efe47083dea494613fc41da55f25c07b4e73bb90c98dee8fe87afbfbc303/pyqt5_sip-12.17.2-cp314-cp314-win_amd64.whl", hash = "sha256:b008755d2222a064ec90c525fce5df3fe9d410371e47c43a21c049e07683b7fb", size = 59620, upload-time = "2025-12-06T13:19:04.829Z" },
]
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "8.4.2" version = "8.4.2"