talos-bar 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ .venv/
2
+ dist/
3
+ __pycache__/
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Zackees
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.5
2
+ Name: talos-bar
3
+ Version: 0.0.1
4
+ Summary: Cross-platform desktop bar (PySide6 + QML) extended with Python glue
5
+ License-Expression: BSD-3-Clause
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: pyside6-essentials>=6.7
9
+ Description-Content-Type: text/markdown
10
+
11
+ # talos-bar
12
+
13
+ A cross-platform desktop bar (Linux, Windows, macOS) for the Talos HQ setup.
14
+ It replaces the KDE Plasma top panel defined in `~/dev/nixos`, so the same
15
+ bar follows us onto any machine.
16
+
17
+ - UI: PySide6 (Qt 6, LGPLv3) + QML, GPU-rendered by Qt Quick
18
+ - Logic: plain Python, so agents can iterate without a compile step
19
+ - Glue: drop a Python file in `glue/` to add bar items, timers and click
20
+ handlers, with hot reload. This is the main way agents extend the bar.
21
+ - Platform docking: one small module per OS (layer-shell, AppBar, NSPanel)
22
+
23
+ Status: design phase. See:
24
+
25
+ - [docs/DESIGN.md](docs/DESIGN.md) — architecture and decisions
26
+ - [docs/MIGRATION.md](docs/MIGRATION.md) — moving the NixOS panel widgets here
27
+
28
+ ## Development
29
+
30
+ ```sh
31
+ uv sync
32
+ uv run talos-bar # run the bar
33
+ uv run ruff check . && uv run ruff format --check .
34
+ uv run basedpyright # strict mode
35
+ uv run pytest
36
+ ```
37
+
38
+ Glue lives in `src/talos_bar/glue/builtin/` and `~/.config/talos-bar/glue/`.
39
+ Edit a glue file while the bar runs and it reloads.
40
+
41
+ On NixOS the PyPI PySide6 wheel needs system libraries (libstdc++, zlib,
42
+ krb5, xcb-*) on `LD_LIBRARY_PATH`; a Nix dev shell for this is still to do.
@@ -0,0 +1,32 @@
1
+ # talos-bar
2
+
3
+ A cross-platform desktop bar (Linux, Windows, macOS) for the Talos HQ setup.
4
+ It replaces the KDE Plasma top panel defined in `~/dev/nixos`, so the same
5
+ bar follows us onto any machine.
6
+
7
+ - UI: PySide6 (Qt 6, LGPLv3) + QML, GPU-rendered by Qt Quick
8
+ - Logic: plain Python, so agents can iterate without a compile step
9
+ - Glue: drop a Python file in `glue/` to add bar items, timers and click
10
+ handlers, with hot reload. This is the main way agents extend the bar.
11
+ - Platform docking: one small module per OS (layer-shell, AppBar, NSPanel)
12
+
13
+ Status: design phase. See:
14
+
15
+ - [docs/DESIGN.md](docs/DESIGN.md) — architecture and decisions
16
+ - [docs/MIGRATION.md](docs/MIGRATION.md) — moving the NixOS panel widgets here
17
+
18
+ ## Development
19
+
20
+ ```sh
21
+ uv sync
22
+ uv run talos-bar # run the bar
23
+ uv run ruff check . && uv run ruff format --check .
24
+ uv run basedpyright # strict mode
25
+ uv run pytest
26
+ ```
27
+
28
+ Glue lives in `src/talos_bar/glue/builtin/` and `~/.config/talos-bar/glue/`.
29
+ Edit a glue file while the bar runs and it reloads.
30
+
31
+ On NixOS the PyPI PySide6 wheel needs system libraries (libstdc++, zlib,
32
+ krb5, xcb-*) on `LD_LIBRARY_PATH`; a Nix dev shell for this is still to do.
@@ -0,0 +1,144 @@
1
+ # talos-bar design
2
+
3
+ ## Goals
4
+
5
+ 1. One bar that runs on Linux (Wayland and X11), Windows 10/11 and macOS.
6
+ 2. Fast to appear and smooth to draw: GPU rendering, no webview.
7
+ 3. Easy for an agent to change: Python + QML, edit and relaunch, no compile.
8
+ 4. Licensing that permits closed or commercial distribution.
9
+
10
+ Non-goals: being a full desktop shell (task manager, launcher dock, pager).
11
+ Those stay with the host desktop.
12
+
13
+ ## Stack decision
14
+
15
+ | Choice | Decision | Why |
16
+ |---|---|---|
17
+ | Toolkit | **PySide6** (Qt 6) | Official Qt for Python, **LGPLv3**: closed apps are fine with dynamic linking. PyQt6 is GPL/commercial, so it is rejected. |
18
+ | UI language | **QML / Qt Quick** | GPU scene graph (Vulkan/Metal/D3D11/OpenGL); Python only drives state. Reuses the existing plasmoid QML. |
19
+ | Rust native core | Not in v1 | `kernal-api`'s Python package is only a compatibility stub today. Bindings (PyO3) come later, only if needed. |
20
+ | Packaging | `uv` for dev; Nuitka (or PyInstaller) per-OS for release | Nuitka also improves cold start. |
21
+
22
+ ## Architecture
23
+
24
+ ```
25
+ talos_bar/
26
+ __main__.py # entry: python -m talos_bar
27
+ app.py # QGuiApplication, QQmlApplicationEngine, per-screen bars
28
+ config.py # TOML config (~/.config/talos-bar/config.toml)
29
+ platform/
30
+ __init__.py # dock(window, edge, height) -> picks backend
31
+ wayland.py # LayerShellQt: anchor, exclusive zone, layer=top
32
+ x11.py # _NET_WM_STRUT_PARTIAL + _NET_WM_WINDOW_TYPE_DOCK
33
+ windows.py # ctypes SHAppBarMessage (ABM_NEW/ABM_SETPOS), Mica/Acrylic via DWM
34
+ macos.py # pyobjc: NSPanel, NSStatusWindowLevel, NSVisualEffectView
35
+ widgets/ # one Python "model" per widget, exposed to QML
36
+ clock.py # multi-time-zone clock
37
+ agenda.py # wraps calendar-py
38
+ tasks.py # wraps tasks_cli (GitHub-synced checklist)
39
+ qml/
40
+ Bar.qml # the strip: blur/opacity background, widget row
41
+ Clock.qml
42
+ Agenda.qml # popup list
43
+ Tasks.qml # popup checklist
44
+ ```
45
+
46
+ ### Data flow
47
+
48
+ - Each widget is a `QObject` with `Property`/`Signal`s registered to QML.
49
+ - Slow work (calendar sync, GitHub calls) runs in a worker thread or
50
+ `asyncio` via `qasync`. It never runs on the GUI thread.
51
+ - QML binds to properties only. No business logic in QML, so the backends can
52
+ be tested headless with pytest.
53
+
54
+ ### Glue layer (the main extension point)
55
+
56
+ The current panel is held together by small scripts: plasmoid
57
+ `executable` DataSources, qdbus calls, KWin scripts and Python helpers.
58
+ talos-bar makes that kind of glue a supported feature. **Glue is written in
59
+ Python**, and an agent adds it by dropping in one file. The agent does not
60
+ edit the bar or write QML.
61
+
62
+ ```
63
+ talos_bar/glue/ # glue that ships with the repo
64
+ ~/.config/talos-bar/glue/ # personal or machine-local glue, same API
65
+ ```
66
+
67
+ A glue module is ordinary Python and uses a small decorator API:
68
+
69
+ ```python
70
+ from talos_bar.glue import item, every, on_click, run, notify
71
+
72
+
73
+ @item("standup", label="Standup") # adds a text/icon slot to the bar
74
+ @every(seconds=60) # how often refresh runs (off the GUI thread)
75
+ def refresh(ctx):
76
+ nxt = ctx.backends.agenda.next_event()
77
+ return f"{nxt.title} in {nxt.minutes_until}m" if nxt else ""
78
+
79
+
80
+ @on_click("standup")
81
+ def open_meeting(ctx):
82
+ run(["xdg-open", ctx.backends.agenda.next_event().url])
83
+ ```
84
+
85
+ The API covers:
86
+
87
+ - `item` / `popup`: a text/icon slot or a list popup, rendered by built-in QML.
88
+ Glue can't break the layout.
89
+ - `every`, `on_event` (screen change, wake, fullscreen), `on_click`, `on_scroll`.
90
+ - `run` (subprocess, with a timeout), `notify`, `ctx.config`, `ctx.cache`,
91
+ and `ctx.platform` (open a URL, focus an app, OS name), all cross-platform.
92
+ - `ctx.backends.*`: the agenda and tasks backends, so glue can reuse them.
93
+
94
+ Rules that keep the glue safe to change:
95
+
96
+ - **Hot reload:** a file watcher reloads a module when it changes. No restart.
97
+ - **Isolation:** each glue module runs in a worker thread with a timeout. An
98
+ exception shows a red dot on that item and goes to the log. The bar keeps running.
99
+ - **OS-specific glue:** use `@item(..., platforms={"linux"})`, or branch on
100
+ `ctx.platform.os`. Glue that needs qdbus/KWin stays Linux-only. Everything
101
+ else should work on every OS.
102
+ - **Testing:** `talos-bar glue test <file>` runs a module headless against
103
+ fake backends, so agents can check their changes without a display.
104
+
105
+ ### Platform docking contract
106
+
107
+ ```python
108
+ def dock(window: QQuickWindow, screen: QScreen, edge: Edge, thickness: int) -> Docking
109
+ ```
110
+
111
+ `Docking` exposes `reserve(bool)` (take screen space or float) and `close()`.
112
+ Behaviour per OS:
113
+
114
+ | OS | Reserves space | Notes |
115
+ |---|---|---|
116
+ | Wayland (KWin, Hyprland, Sway) | yes, exclusive zone | Needs the compositor to support `wlr-layer-shell`. GNOME does not, so GNOME falls back to a floating window. |
117
+ | X11 | yes, strut | |
118
+ | Windows | yes, AppBar | Handle `ABN_FULLSCREENAPP` to hide under fullscreen apps. |
119
+ | macOS | **no** | The system menu bar owns the top edge. Float an NSPanel just below it, or use a bottom edge. |
120
+
121
+ ### Look
122
+
123
+ - Background: 70% opacity plus shadow, matching the current panel colorizer setting.
124
+ - Blur: KWin blur protocol on Linux, `DWMWA_SYSTEMBACKDROP_TYPE` on Windows,
125
+ `NSVisualEffectView` on macOS. Fall back to plain translucency.
126
+ - Height 36 logical px, centered, sized to contents (matches today's panel).
127
+ - Follows each screen's DPI; one bar per screen, configurable.
128
+
129
+ ## Performance budget
130
+
131
+ | Metric | Target |
132
+ |---|---|
133
+ | Cold start to visible bar | under 800 ms (Nuitka build), under 1.5 s (`uv run`) |
134
+ | Idle CPU | about 0% (clock ticks once per second, or per minute without seconds) |
135
+ | Resident memory | under 150 MB |
136
+
137
+ Measure these early. If cold start misses its target, use Nuitka and
138
+ precompiled QML (`qmlcachegen`) before changing the architecture.
139
+
140
+ ## Open questions
141
+
142
+ - macOS: a bar below the menu bar, or `NSStatusItem` menu extras instead?
143
+ - Should talos-bar also absorb the bottom dock's custom launchers later?
144
+ - When should `kernal-api` get PyO3 bindings, and which modules first (process, http, tray)?
@@ -0,0 +1,87 @@
1
+ # Migrating the NixOS top panel into talos-bar
2
+
3
+ ## Starting point
4
+
5
+ The NixOS top panel is declared in `~/dev/nixos/system/configuration.nix`
6
+ (around line 3190): `location = "top"`, screen 0, centered, `lengthMode = "fit"`,
7
+ height 36, `hiding = "windowsgobelow"`. It holds:
8
+
9
+ | Current widget | Source | Moves to |
10
+ |---|---|---|
11
+ | `luisbocanegra.panel.colorizer` (70% opacity, shadow) | third-party plasmoid | `qml/Bar.qml` background, plus blur in `platform/*` |
12
+ | `digitalClock`, 9 zones (Local, New York, Chicago, Denver, Sydney, Luxembourg, Moscow, Tokyo, UTC), city format | stock Plasma | `widgets/clock.py` + `qml/Clock.qml`; zones in config.toml |
13
+ | `org.niteris.calendar-agenda` | `nixos/system/calendar-agenda/` (483-line QML) + `nixos/system/calendar-py/` | `widgets/agenda.py` imports calendar-py; QML ported |
14
+ | `org.niteris.tasks` | `nixos/system/tasks/` (applet + `tasks_cli.py`) | `widgets/tasks.py` imports tasks_cli; QML ported |
15
+
16
+ Out of scope: the bottom dock (launchers, pager, task manager) stays in Plasma.
17
+
18
+ ## Phase 0: scaffold (about 1 day)
19
+
20
+ - `pyproject.toml` (uv), PySide6, pytest, ruff.
21
+ - `python -m talos_bar` opens an undecorated 36 px window with a clock.
22
+
23
+ ## Phase 1: Linux parity, side by side with Plasma (about 3–5 days)
24
+
25
+ 1. Wayland docking via LayerShellQt (in nixpkgs as `kdePackages.layer-shell-qt`).
26
+ 2. Clock widget with the nine zones.
27
+ 3. **Backends:** move `calendar-py` and `tasks_cli.py` into this repo as
28
+ `talos_bar/backends/` (keep their tests). Until the move, depend on them
29
+ by path. Drop the `Plasma5Support.DataSource { engine: "executable" }`
30
+ subprocess bridge and call the Python functions directly.
31
+ 4. **QML port:** replace `org.kde.plasma.*`, `Plasma5Support` and Kirigami
32
+ imports with `QtQuick.Controls`. Replace `plasmoid.*` config with
33
+ properties from `config.py`. Keep the layout and behaviour as they are.
34
+ 5. Run talos-bar next to the Plasma panel on another screen or edge until it
35
+ matches.
36
+
37
+ ## Phase 1b: convert existing glue into Python glue modules (ongoing)
38
+
39
+ Go through every ad-hoc bridge that feeds the panel and rewrite it as a
40
+ `talos_bar/glue/*.py` module (see "Glue layer" in DESIGN.md):
41
+
42
+ - Plasmoid `executable` DataSources and the shell strings they run.
43
+ - `qdbus` / KWin-script snippets in `configuration.nix` that the panel uses
44
+ (Overview button, desktop moves). These become Linux-only glue.
45
+ - Python helpers wired to the bar (e.g. `launch-origin/kitty_bridge.py`,
46
+ `voxtype-popup.py`) if they surface state on the bar.
47
+
48
+ Do one conversion per commit, and record each in the table below. Once a
49
+ piece of glue is a module, an agent can change it without touching Nix.
50
+
51
+ | Old glue | New module | Platforms | Done |
52
+ |---|---|---|---|
53
+ | | | | |
54
+
55
+ ## Phase 2: switch over on NixOS (about 1 day)
56
+
57
+ - Package talos-bar in `~/dev/nixos` (a `buildPythonApplication` in
58
+ `system/pkgs/talos-bar/`) and start it with a systemd user service.
59
+ - Remove the top panel block from `configuration.nix`, and remove the
60
+ agenda/tasks plasmoid wiring in `calendar.nix` and `tasks.nix` (keep the
61
+ sync services those widgets rely on).
62
+ - Keep the old panel block in git history as the rollback.
63
+
64
+ ## Phase 3: Windows and macOS (about 1.5–2 weeks)
65
+
66
+ - `platform/windows.py`: AppBar registration, work-area reservation,
67
+ fullscreen hiding, Mica backdrop.
68
+ - `platform/macos.py`: NSPanel at status level below the menu bar,
69
+ vibrancy, no Dock icon (`LSUIElement`).
70
+ - Check that the calendar and tasks backends have no Linux-only paths (XDG
71
+ directories, `notify-send`, etc.). Use `platformdirs` for paths.
72
+
73
+ ## Phase 4: packaging and polish (about 1 week)
74
+
75
+ - Nuitka builds per OS in CI (add workflows only when asked).
76
+ - Autostart: systemd user unit, Windows Startup shortcut / Run key, macOS LaunchAgent.
77
+ - Multi-monitor (one bar per screen, or primary only) and per-screen DPI.
78
+ - Measure against the performance budget in DESIGN.md.
79
+
80
+ ## Risks
81
+
82
+ | Risk | Mitigation |
83
+ |---|---|
84
+ | GNOME has no layer-shell | Floating window fallback; document the limitation |
85
+ | Python + Qt cold start | Nuitka + `qmlcachegen`; it is a login-once process |
86
+ | calendar-py assumes Linux | Audit in Phase 3; `platformdirs` |
87
+ | macOS cannot reserve the top edge | Accept a floating bar, or go to `NSStatusItem` |
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "talos-bar"
3
+ version = "0.0.1"
4
+ description = "Cross-platform desktop bar (PySide6 + QML) extended with Python glue"
5
+ readme = "README.md"
6
+ license = "BSD-3-Clause"
7
+ requires-python = ">=3.10"
8
+ dependencies = ["PySide6-Essentials>=6.7"]
9
+
10
+ [project.scripts]
11
+ talos-bar = "talos_bar.__main__:main"
12
+
13
+ [dependency-groups]
14
+ dev = ["pytest>=8", "ruff>=0.6", "basedpyright>=1.20"]
15
+
16
+ [build-system]
17
+ requires = ["hatchling>=1.26"]
18
+ build-backend = "hatchling.build"
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["src/talos_bar"]
22
+
23
+ [tool.ruff]
24
+ line-length = 110
25
+ target-version = "py310"
26
+ src = ["src", "tests"]
27
+
28
+ [tool.ruff.lint]
29
+ select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF", "ANN"]
30
+
31
+ [tool.basedpyright]
32
+ include = ["src", "tests"]
33
+ pythonVersion = "3.10"
34
+ typeCheckingMode = "strict"
35
+ reportMissingTypeStubs = false
File without changes
@@ -0,0 +1,6 @@
1
+ import sys
2
+
3
+ from talos_bar.app import main
4
+
5
+ if __name__ == "__main__":
6
+ sys.exit(main())
@@ -0,0 +1,138 @@
1
+ """Bar application: loads glue, runs it off the GUI thread, feeds QML."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import traceback
7
+ from pathlib import Path
8
+ from typing import cast
9
+
10
+ from PySide6.QtCore import (
11
+ Property,
12
+ QFileSystemWatcher,
13
+ QObject,
14
+ QRunnable,
15
+ QThreadPool,
16
+ QTimer,
17
+ QUrl,
18
+ Signal,
19
+ Slot,
20
+ )
21
+ from PySide6.QtGui import QGuiApplication, QWindow
22
+ from PySide6.QtQml import QQmlApplicationEngine
23
+
24
+ from talos_bar import glue, platform
25
+
26
+ QML = Path(__file__).parent / "qml" / "Bar.qml"
27
+
28
+
29
+ class _Job(QRunnable):
30
+ def __init__(self, bar: Bar, item: glue.Item, fn: glue.RefreshFn) -> None:
31
+ super().__init__()
32
+ self.bar, self.item, self.fn = bar, item, fn
33
+
34
+ def run(self) -> None:
35
+ try:
36
+ out = self.fn(self.bar.ctx)
37
+ self.bar.result.emit(self.item.id, out if out is not None else "", False)
38
+ except Exception:
39
+ traceback.print_exc()
40
+ self.bar.result.emit(self.item.id, "", True)
41
+
42
+
43
+ class Bar(QObject):
44
+ itemsChanged = Signal()
45
+ result = Signal(str, object, bool)
46
+
47
+ def __init__(self) -> None:
48
+ super().__init__()
49
+ self.ctx = glue.Context()
50
+ self._items: dict[str, glue.Item] = {}
51
+ self._view: dict[str, dict[str, object]] = {}
52
+ self._timers: dict[str, QTimer] = {}
53
+ self._pool = QThreadPool.globalInstance()
54
+ self.result.connect(self._on_result)
55
+ self._watch = QFileSystemWatcher(self)
56
+ self._watch.directoryChanged.connect(self._changed)
57
+ self._watch.fileChanged.connect(self._changed)
58
+ self.reload()
59
+
60
+ @Property(list, notify=itemsChanged) # pyright: ignore[reportArgumentType]
61
+ def items(self) -> list[dict[str, object]]:
62
+ return [self._view[i] for i in self._items]
63
+
64
+ @Slot(str)
65
+ def _changed(self, _path: str) -> None:
66
+ self.reload()
67
+
68
+ @Slot()
69
+ def reload(self) -> None:
70
+ for t in self._timers.values():
71
+ t.stop()
72
+ self._timers.clear()
73
+ self._items.clear()
74
+ files: list[Path] = []
75
+ for d in glue.glue_dirs():
76
+ if d.is_dir():
77
+ files += sorted(p for p in d.glob("*.py") if not p.name.startswith("_"))
78
+ self._watch.addPath(str(d))
79
+ for f in files:
80
+ self._watch.addPath(str(f))
81
+ for it in glue.load_file(f):
82
+ self._items[it.id] = it
83
+ self._view.setdefault(it.id, {"id": it.id, "text": it.label, "tooltip": "", "error": False})
84
+ self._schedule(it)
85
+ self.itemsChanged.emit()
86
+
87
+ def _schedule(self, it: glue.Item) -> None:
88
+ if not it.refresh:
89
+ return
90
+ t = QTimer(self)
91
+ t.setInterval(int(it.interval * 1000))
92
+ refresh = it.refresh
93
+ t.timeout.connect(lambda: self._pool.start(_Job(self, it, refresh)))
94
+ t.start()
95
+ self._timers[it.id] = t
96
+ self._pool.start(_Job(self, it, refresh))
97
+
98
+ @Slot(str, object, bool)
99
+ def _on_result(self, item_id: str, out: object, error: bool) -> None:
100
+ if item_id not in self._view:
101
+ return
102
+ v = self._view[item_id]
103
+ v["error"] = error
104
+ if isinstance(out, dict):
105
+ rich = cast("glue.Rich", out)
106
+ v["text"] = rich.get("text", str(v["text"]))
107
+ v["tooltip"] = rich.get("tooltip", "")
108
+ elif not error:
109
+ v["text"] = str(out)
110
+ self.itemsChanged.emit()
111
+
112
+ @Slot(str)
113
+ def click(self, item_id: str) -> None:
114
+ it = self._items.get(item_id)
115
+ if it is None or it.click is None:
116
+ return
117
+ on_click, refresh = it.click, it.refresh
118
+
119
+ def clicked(ctx: glue.Context) -> glue.Output:
120
+ on_click(ctx)
121
+ return refresh(ctx) if refresh else None
122
+
123
+ self._pool.start(_Job(self, it, clicked))
124
+
125
+
126
+ def main(argv: list[str] | None = None) -> int:
127
+ app = QGuiApplication(argv or sys.argv)
128
+ app.setApplicationName("talos-bar")
129
+ bar = Bar()
130
+ engine = QQmlApplicationEngine()
131
+ engine.rootContext().setContextProperty("bar", bar)
132
+ engine.load(QUrl.fromLocalFile(str(QML)))
133
+ if not engine.rootObjects():
134
+ return 1
135
+ root = engine.rootObjects()[0]
136
+ assert isinstance(root, QWindow)
137
+ platform.dock(root, height=36)
138
+ return app.exec()
@@ -0,0 +1,115 @@
1
+ """Glue API: drop a Python file in a glue directory to add items to the bar.
2
+
3
+ from talos_bar.glue import Context, Output, every, item, on_click
4
+
5
+ @item("hello", label="Hello")
6
+ @every(seconds=5)
7
+ def refresh(ctx: Context) -> Output:
8
+ return "hi"
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import importlib.util
14
+ import subprocess
15
+ import sys
16
+ import traceback
17
+ from collections.abc import Callable
18
+ from dataclasses import dataclass, field
19
+ from pathlib import Path
20
+ from typing import Any, TypedDict
21
+
22
+
23
+ class Rich(TypedDict, total=False):
24
+ text: str
25
+ tooltip: str
26
+
27
+
28
+ Output = str | Rich | None
29
+ RefreshFn = Callable[["Context"], Output]
30
+ ClickFn = Callable[["Context"], None]
31
+
32
+
33
+ @dataclass
34
+ class Item:
35
+ id: str
36
+ label: str
37
+ refresh: RefreshFn | None = None
38
+ interval: float = 60.0
39
+ click: ClickFn | None = None
40
+ platforms: frozenset[str] | None = None
41
+ source: Path | None = None
42
+
43
+
44
+ @dataclass
45
+ class Context:
46
+ os: str = sys.platform
47
+ cache: dict[str, Any] = field(default_factory=dict[str, Any])
48
+
49
+
50
+ # Filled while a glue module is imported; read back by load_file().
51
+ _registry: dict[str, Item] = {}
52
+ _intervals: dict[int, float] = {}
53
+
54
+
55
+ def item(id: str, label: str = "", platforms: set[str] | None = None) -> Callable[[RefreshFn], RefreshFn]:
56
+ def deco(fn: RefreshFn) -> RefreshFn:
57
+ it = _registry.setdefault(id, Item(id=id, label=label or id))
58
+ it.refresh = fn
59
+ it.interval = _intervals.get(id_of(fn), it.interval)
60
+ it.platforms = frozenset(platforms) if platforms else None
61
+ return fn
62
+
63
+ return deco
64
+
65
+
66
+ def every(seconds: float) -> Callable[[RefreshFn], RefreshFn]:
67
+ """Refresh interval; place it below @item."""
68
+
69
+ def deco(fn: RefreshFn) -> RefreshFn:
70
+ _intervals[id_of(fn)] = seconds
71
+ return fn
72
+
73
+ return deco
74
+
75
+
76
+ def on_click(id: str) -> Callable[[ClickFn], ClickFn]:
77
+ def deco(fn: ClickFn) -> ClickFn:
78
+ _registry.setdefault(id, Item(id=id, label=id)).click = fn
79
+ return fn
80
+
81
+ return deco
82
+
83
+
84
+ def run(cmd: list[str], timeout: float = 10.0) -> str:
85
+ return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, check=False).stdout
86
+
87
+
88
+ def id_of(fn: object) -> int:
89
+ return id(fn)
90
+
91
+
92
+ def load_file(path: Path) -> list[Item]:
93
+ """Import one glue file and return the items it registered (empty on error)."""
94
+ _registry.clear()
95
+ _intervals.clear()
96
+ spec = importlib.util.spec_from_file_location(f"talos_glue_{path.stem}", path)
97
+ if spec is None or spec.loader is None:
98
+ return []
99
+ try:
100
+ spec.loader.exec_module(importlib.util.module_from_spec(spec))
101
+ except Exception:
102
+ traceback.print_exc()
103
+ return []
104
+ items = [i for i in _registry.values() if i.platforms is None or os_key() in i.platforms]
105
+ for i in items:
106
+ i.source = path
107
+ return items
108
+
109
+
110
+ def glue_dirs() -> list[Path]:
111
+ return [Path(__file__).parent / "builtin", Path.home() / ".config" / "talos-bar" / "glue"]
112
+
113
+
114
+ def os_key() -> str:
115
+ return {"win32": "windows", "darwin": "macos"}.get(sys.platform, "linux")
@@ -0,0 +1,29 @@
1
+ """World clock: local time, with other zones in the tooltip."""
2
+
3
+ from datetime import datetime
4
+ from zoneinfo import ZoneInfo
5
+
6
+ from talos_bar.glue import Context, Output, every, item
7
+
8
+ ZONES = [
9
+ "America/New_York",
10
+ "America/Chicago",
11
+ "America/Denver",
12
+ "Australia/Sydney",
13
+ "Europe/Luxembourg",
14
+ "Europe/Moscow",
15
+ "Asia/Tokyo",
16
+ "UTC",
17
+ ]
18
+
19
+
20
+ def _city(zone: str) -> str:
21
+ return zone.split("/")[-1].replace("_", " ")
22
+
23
+
24
+ @item("clock", label="Clock")
25
+ @every(seconds=1)
26
+ def refresh(ctx: Context) -> Output:
27
+ now = datetime.now().astimezone()
28
+ tip = "\n".join(f"{_city(z)}: {now.astimezone(ZoneInfo(z)):%H:%M}" for z in ZONES)
29
+ return {"text": f"{now:%a %d %b %H:%M:%S}", "tooltip": tip}
@@ -0,0 +1,15 @@
1
+ """Example glue: edit this file while the bar runs and it reloads."""
2
+
3
+ from talos_bar.glue import Context, Output, every, item, on_click
4
+
5
+
6
+ @item("hello", label="Hello")
7
+ @every(seconds=5)
8
+ def refresh(ctx: Context) -> Output:
9
+ clicks: int = ctx.cache.get("clicks", 0)
10
+ return f"glue ok ({clicks} clicks)"
11
+
12
+
13
+ @on_click("hello")
14
+ def click(ctx: Context) -> None:
15
+ ctx.cache["clicks"] = ctx.cache.get("clicks", 0) + 1
@@ -0,0 +1,27 @@
1
+ """Edge docking. The spike only floats the bar at the top centre of the
2
+ primary screen; real space reservation (layer-shell, X11 strut, AppBar,
3
+ NSPanel) lands per OS as described in docs/DESIGN.md."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from PySide6.QtCore import Qt
8
+ from PySide6.QtGui import QGuiApplication, QWindow
9
+
10
+
11
+ def dock(window: QWindow, height: int) -> None:
12
+ window.setFlags(
13
+ Qt.WindowType.FramelessWindowHint | Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.Tool
14
+ )
15
+ geo = QGuiApplication.primaryScreen().availableGeometry()
16
+
17
+ def place() -> None:
18
+ window.setY(geo.y())
19
+ window.setX(geo.x() + (geo.width() - window.width()) // 2)
20
+
21
+ def on_width(_w: int) -> None:
22
+ place()
23
+
24
+ window.widthChanged.connect(on_width)
25
+ window.setHeight(height)
26
+ place()
27
+ window.show()
@@ -0,0 +1,37 @@
1
+ import QtQuick
2
+ import QtQuick.Controls
3
+
4
+ Window {
5
+ id: root
6
+ visible: false
7
+ color: "transparent"
8
+ width: row.implicitWidth + 32
9
+ height: 36
10
+
11
+ Rectangle {
12
+ anchors.fill: parent
13
+ radius: 8
14
+ color: Qt.rgba(0.08, 0.09, 0.11, 0.7)
15
+ border.color: Qt.rgba(1, 1, 1, 0.08)
16
+ }
17
+
18
+ Row {
19
+ id: row
20
+ anchors.centerIn: parent
21
+ spacing: 20
22
+
23
+ Repeater {
24
+ model: bar.items
25
+ delegate: Label {
26
+ required property var modelData
27
+ text: modelData.text
28
+ color: modelData.error ? "#ff6b6b" : "#e8e8ea"
29
+ font.pixelSize: 14
30
+ ToolTip.visible: modelData.tooltip !== "" && hover.hovered
31
+ ToolTip.text: modelData.tooltip
32
+ HoverHandler { id: hover }
33
+ TapHandler { onTapped: bar.click(modelData.id) }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,22 @@
1
+ from pathlib import Path
2
+
3
+ from talos_bar import glue
4
+
5
+
6
+ def test_builtin_glue_loads_and_refreshes() -> None:
7
+ builtin = glue.glue_dirs()[0]
8
+ ids: dict[str, glue.Item] = {}
9
+ for f in sorted(builtin.glob("*.py")):
10
+ for it in glue.load_file(f):
11
+ ids[it.id] = it
12
+ assert {"clock", "hello"} <= ids.keys()
13
+ assert ids["clock"].interval == 1
14
+ ctx = glue.Context()
15
+ assert ids["clock"].refresh is not None
16
+ assert "text" in ids["clock"].refresh(ctx) # type: ignore[operator]
17
+
18
+
19
+ def test_broken_glue_is_isolated(tmp_path: Path) -> None:
20
+ bad = tmp_path / "bad.py"
21
+ bad.write_text("raise RuntimeError('boom')\n")
22
+ assert glue.load_file(bad) == []
@@ -0,0 +1,264 @@
1
+ version = 1
2
+ revision = 5
3
+ requires-python = ">=3.10"
4
+
5
+ [[package]]
6
+ name = "basedpyright"
7
+ version = "1.40.2"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ dependencies = [
10
+ { name = "nodejs-wheel-binaries" },
11
+ ]
12
+ sdist = { url = "https://files.pythonhosted.org/packages/3a/36/1cf637b15704e4c1f09f8bcc48ab3bfaff089e7466565bd34d8d1dcc03e9/basedpyright-1.40.2.tar.gz", hash = "sha256:d4db36d095230379acc66c7e5fba7e4b50ca902f6e803d696782dd031cb8d7d7", size = 25559682, upload-time = "2026-10-04T11:28:30.415Z" }
13
+ wheels = [
14
+ { url = "https://files.pythonhosted.org/packages/de/53/0031b22e118264a57a3b64dfc0c8f0659b81af5bccd419ac62933013c601/basedpyright-1.40.2-py3-none-any.whl", hash = "sha256:3a53aeb93a86675ded0e12bc27bd8476c1742356de07ad8cab67bff18110c690", size = 13694487, upload-time = "2026-10-04T11:28:26.271Z" },
15
+ ]
16
+
17
+ [[package]]
18
+ name = "colorama"
19
+ version = "0.4.6"
20
+ source = { registry = "https://pypi.org/simple" }
21
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
22
+ wheels = [
23
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
24
+ ]
25
+
26
+ [[package]]
27
+ name = "exceptiongroup"
28
+ version = "1.3.1"
29
+ source = { registry = "https://pypi.org/simple" }
30
+ dependencies = [
31
+ { name = "typing-extensions" },
32
+ ]
33
+ sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
34
+ wheels = [
35
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
36
+ ]
37
+
38
+ [[package]]
39
+ name = "iniconfig"
40
+ version = "2.3.1"
41
+ source = { registry = "https://pypi.org/simple" }
42
+ sdist = { url = "https://files.pythonhosted.org/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" }
43
+ wheels = [
44
+ { url = "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" },
45
+ ]
46
+
47
+ [[package]]
48
+ name = "nodejs-wheel-binaries"
49
+ version = "24.19.0"
50
+ source = { registry = "https://pypi.org/simple" }
51
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/76/7e97195e14346598565a0de4ca8bdbd5e634b3fb5b1ba590b7b1b89f8a63/nodejs_wheel_binaries-24.19.0.tar.gz", hash = "sha256:db217eef8cab8551667863379b08db4d9067403f6cbbe87481eb40edceb8aa9b", size = 8058, upload-time = "2026-08-19T21:47:19.671Z" }
52
+ wheels = [
53
+ { url = "https://files.pythonhosted.org/packages/8c/52/0774b52c7be8151ad9d5aff44edc100c3f13d6d8eb3765f63ffa40e69fe8/nodejs_wheel_binaries-24.19.0-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:e12cbfd69089504e42fb14194ce734a9dcf3eb38c820ca63dd511d36fb964e9c", size = 56047203, upload-time = "2026-08-19T21:46:43.448Z" },
54
+ { url = "https://files.pythonhosted.org/packages/67/3a/4fdbbfecf2c23d52c0e3f68de7f7c1b3c97a26d328c69c5f6c49c48e340e/nodejs_wheel_binaries-24.19.0-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:1c890adf4b7e6556ccc1ca66c866bb81884b6c9a581dee4e530dc7f78fb9d514", size = 56219459, upload-time = "2026-08-19T21:46:48.45Z" },
55
+ { url = "https://files.pythonhosted.org/packages/5f/a8/0147149415195c59b8a72a594916bfb80d6be4d586f9fbfda313889e0efc/nodejs_wheel_binaries-24.19.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:4e029dadfae1295876063c96b236f673487e8c27379fe146c1e2250283520227", size = 60588256, upload-time = "2026-08-19T21:46:53.298Z" },
56
+ { url = "https://files.pythonhosted.org/packages/f4/89/6631d0982353da1bb7bc00bb1988f702822c62b42634f57999ee53b5c337/nodejs_wheel_binaries-24.19.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:4196a947bcc883f2003ab101762d729f3e99b5e86b75bd09151563403e2eceb8", size = 61123607, upload-time = "2026-08-19T21:46:58.117Z" },
57
+ { url = "https://files.pythonhosted.org/packages/32/a2/fa30f0841e4602995782e124359f9b910c7b481d98decf61ef0b2fc3ebfb/nodejs_wheel_binaries-24.19.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:352e048ab4dd35e7de5f338d1cc4fcbf77a0e93da30bf7336a8217ee246b31d7", size = 62632842, upload-time = "2026-08-19T21:47:03.42Z" },
58
+ { url = "https://files.pythonhosted.org/packages/18/01/22d97ca72213f66cc386ee638db30c2e62757fdf761c6029074bced83d1c/nodejs_wheel_binaries-24.19.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:28d078b2ced9e2069516e652dc4b1380e7a1a7f2d3934eccd1611586d283ba4c", size = 63250653, upload-time = "2026-08-19T21:47:07.938Z" },
59
+ { url = "https://files.pythonhosted.org/packages/88/d1/e3be8fa327a795bcaf7a19cd84299e338a7bce32ff0665fdce9cfa22573c/nodejs_wheel_binaries-24.19.0-py2.py3-none-win_amd64.whl", hash = "sha256:67e3abeb9c3830cae8c8487ae8a2af7cc27dfa75af06145cee5ca7d1857c81bd", size = 42448503, upload-time = "2026-08-19T21:47:12.093Z" },
60
+ { url = "https://files.pythonhosted.org/packages/1d/37/34cf28ba1691a060174948a9927fe61091982d6048b2e403071a9acce443/nodejs_wheel_binaries-24.19.0-py2.py3-none-win_arm64.whl", hash = "sha256:d9074c665ea68b04e183d82482c86dc907d3a9bd15eb6cf85542cb785266bb36", size = 40090155, upload-time = "2026-08-19T21:47:16.032Z" },
61
+ ]
62
+
63
+ [[package]]
64
+ name = "packaging"
65
+ version = "26.3"
66
+ source = { registry = "https://pypi.org/simple" }
67
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
68
+ wheels = [
69
+ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
70
+ ]
71
+
72
+ [[package]]
73
+ name = "pluggy"
74
+ version = "1.7.0"
75
+ source = { registry = "https://pypi.org/simple" }
76
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" }
77
+ wheels = [
78
+ { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" },
79
+ ]
80
+
81
+ [[package]]
82
+ name = "pygments"
83
+ version = "2.21.0"
84
+ source = { registry = "https://pypi.org/simple" }
85
+ sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
86
+ wheels = [
87
+ { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
88
+ ]
89
+
90
+ [[package]]
91
+ name = "pyside6-essentials"
92
+ version = "6.12.0"
93
+ source = { registry = "https://pypi.org/simple" }
94
+ dependencies = [
95
+ { name = "shiboken6" },
96
+ ]
97
+ wheels = [
98
+ { url = "https://files.pythonhosted.org/packages/e1/90/d528f8dd3f9f20712bc719e10c698d27c998e1dd76ed3f33b6fb606d0972/pyside6_essentials-6.12.0-cp310-abi3-macosx_14_0_universal2.whl", hash = "sha256:0867b709a724db28b161227c7d6ecb5ed10cc09226a53542417ac6dee1308239", size = 116002775, upload-time = "2026-10-08T09:09:54.453Z" },
99
+ { url = "https://files.pythonhosted.org/packages/7b/3c/7597d8dcb36aa8315427635ec6ec9d850ffbad4ce78b9118ac854889f2d3/pyside6_essentials-6.12.0-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:16f4b5e41daf49235ae048458243da9efe5f3d8040f9cc5aa454ce69893d3bd9", size = 84346433, upload-time = "2026-10-08T09:10:02.825Z" },
100
+ { url = "https://files.pythonhosted.org/packages/5e/4e/f4fd4c016719be832ec49d3c87940459e87fd666179a0a199a3998d07916/pyside6_essentials-6.12.0-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:08c5841063fc1df69b7fa1ee405f539e66576a83fc48004be3e2c5e35ccbfa69", size = 83810102, upload-time = "2026-10-08T09:10:09.13Z" },
101
+ { url = "https://files.pythonhosted.org/packages/03/61/9a7e32a37b224f68506785bfb0f288d516098ec71681782d72b695a6c95d/pyside6_essentials-6.12.0-cp310-abi3-win_amd64.whl", hash = "sha256:c9a95102aa23c1f86516a30d5a9be37552d324eee7e567eb7508973f1218cb42", size = 82153795, upload-time = "2026-10-08T09:10:15.104Z" },
102
+ { url = "https://files.pythonhosted.org/packages/92/11/43afb3d0e7c25dd09f0eb7e44f1283dadb5fa8d8b517249fe3fde02e6702/pyside6_essentials-6.12.0-cp310-abi3-win_arm64.whl", hash = "sha256:c76aa689989bf9bb9b45535e0a81c6733294df720b0eed1308fa30d2606b98cc", size = 62073456, upload-time = "2026-10-08T09:10:20.202Z" },
103
+ ]
104
+
105
+ [[package]]
106
+ name = "pytest"
107
+ version = "9.1.1"
108
+ source = { registry = "https://pypi.org/simple" }
109
+ dependencies = [
110
+ { name = "colorama", marker = "sys_platform == 'win32'" },
111
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
112
+ { name = "iniconfig" },
113
+ { name = "packaging" },
114
+ { name = "pluggy" },
115
+ { name = "pygments" },
116
+ { name = "tomli", marker = "python_full_version < '3.11'" },
117
+ ]
118
+ sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
119
+ wheels = [
120
+ { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
121
+ ]
122
+
123
+ [[package]]
124
+ name = "ruff"
125
+ version = "0.17.2"
126
+ source = { registry = "https://pypi.org/simple" }
127
+ sdist = { url = "https://files.pythonhosted.org/packages/ae/58/690cf56da80c8f2c8bf12042b6017beac2c34fe961f65420bcb3fb7fac38/ruff-0.17.2.tar.gz", hash = "sha256:84d632402d14c23b488f941db23712d6736a013225162062d34558c3f3c780eb", size = 5333400, upload-time = "2026-10-22T18:38:00.181Z" }
128
+ wheels = [
129
+ { url = "https://files.pythonhosted.org/packages/98/b8/8b6d69c57e8fe1cc6af1aa7493b72a1a5b5a8afe34694e302025441729d4/ruff-0.17.2-py3-none-linux_armv6l.whl", hash = "sha256:96a65e755d8dba094707fb23ab602111d9a31ec2835b3a6992a1914a8fcb211e", size = 10183857, upload-time = "2026-10-22T18:37:14.956Z" },
130
+ { url = "https://files.pythonhosted.org/packages/e8/61/12cc2ff7b2fa90a268ce1e2162c23aaf76e601d22623f6a31b0eeaa764b0/ruff-0.17.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:39c5b55c960515cdb32be16e69efb5734c589c06a0eb38a22694524a336b889d", size = 10210904, upload-time = "2026-10-22T18:37:17.985Z" },
131
+ { url = "https://files.pythonhosted.org/packages/e8/7c/b8850a8e4ad29f64a827dd6c6902db4a9aa538fb2b9eef4e26084001a3bb/ruff-0.17.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:92a80d2f44584dabb0189b3a6c393e37e525e04942838482010575cb81945be7", size = 9408439, upload-time = "2026-10-22T18:37:20.964Z" },
132
+ { url = "https://files.pythonhosted.org/packages/b7/b6/6a64fae86ca5e5e29d5adc311e5481de726cedf9aee607435a16c31c442a/ruff-0.17.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7d0906677d89bbf3cf73cde64b5bbe2ab35df3ae43e49ee2627301e91265bac", size = 10047078, upload-time = "2026-10-22T18:37:23.717Z" },
133
+ { url = "https://files.pythonhosted.org/packages/10/19/23d376547715175aa1bd46df48df6aacc7c6439d9daf19cac15975180aa5/ruff-0.17.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cfd783590edf2b77317882631900f6a13ea2e8eacefe6ebf2370d2715299160", size = 9990478, upload-time = "2026-10-22T18:37:26.584Z" },
134
+ { url = "https://files.pythonhosted.org/packages/93/6c/34cda32cbe13adac5c6b291a307a5570d8ebd37c0c86339f0fe628e55632/ruff-0.17.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122258f6ecde0e0dd1bb60b24263c3edde4338caf723fca711cc729f9bfd414a", size = 10673624, upload-time = "2026-10-22T18:37:29.767Z" },
135
+ { url = "https://files.pythonhosted.org/packages/bf/10/5d762a4dbc851d8bb47863cf3a28a61bf31c804d393b70d891fac899f5d1/ruff-0.17.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f8fba429d6ac4128ab29cfb8fa6240d2fa671d04adf7f84d3b22fc7b6b6bea4", size = 11351797, upload-time = "2026-10-22T18:37:32.535Z" },
136
+ { url = "https://files.pythonhosted.org/packages/3f/04/a0e92a81faf5c81c1c98d4374c8401734ccf2b8cca7d0170cb0fec08c6e2/ruff-0.17.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:583bd67bb9e7061dd09bc46a0a884bae2bbd214dbcd48e509813612619683869", size = 10949414, upload-time = "2026-10-22T18:37:35.501Z" },
137
+ { url = "https://files.pythonhosted.org/packages/c3/d8/59894cf630d2d86882457a2a6a0a1533410c5c805bfbac6f6eb4b5aef69d/ruff-0.17.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c60f93fec7142dba5b8f487f94bf2f6231dae24dc11e4c48648347c8b0720004", size = 10528000, upload-time = "2026-10-22T18:37:37.88Z" },
138
+ { url = "https://files.pythonhosted.org/packages/ab/95/d6e30c51e851d37e10762ad91fa1e76ddaad998589b5667962a3cfbc9edd/ruff-0.17.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c555a877f8f41447e318101cfefea9bf12dfa9f82eedf90c376e57e069f917d0", size = 10571779, upload-time = "2026-10-22T18:37:40.444Z" },
139
+ { url = "https://files.pythonhosted.org/packages/f4/5a/a7e1d74fb464250137d3d4eca645d323536533b672004d04ee0e46e0caf3/ruff-0.17.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ba405cbd5ec7bda1f555d5e52be1fd68d02608d4d95d24591bdac92cfacb579f", size = 10051707, upload-time = "2026-10-22T18:37:42.812Z" },
140
+ { url = "https://files.pythonhosted.org/packages/6e/f2/02fca29ca7999380f0350a7212fe755c11ab9c22d61327356cce8decdbc6/ruff-0.17.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8c21b4ab24485358f17291a4f8dc5399b834e963b8efedd920fe4fdb02081bb4", size = 9998227, upload-time = "2026-10-22T18:37:45.224Z" },
141
+ { url = "https://files.pythonhosted.org/packages/d1/00/55aa604a093dfdc1526e570fae0bb7d37a2221c7b4728d5f080b2add16ba/ruff-0.17.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5875a16b0de70b43cb81eabea5ae5e8f5d1b2fd1b5e6ed07f80cfe5019d0bd5a", size = 10377692, upload-time = "2026-10-22T18:37:47.704Z" },
142
+ { url = "https://files.pythonhosted.org/packages/e4/5a/a7a640f47bb906ec8fa233ea84319d503e6dee02b59e3901bd312ebd1f9b/ruff-0.17.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2d086be452eed686aa51271ecf2cbe5037956517cc6ee39a17b4489ac12f8bbe", size = 10790587, upload-time = "2026-10-22T18:37:50.191Z" },
143
+ { url = "https://files.pythonhosted.org/packages/2c/d4/c3b74e1b4808438ea7f14bb914478025e2d51261f60774a54a0fa222ec46/ruff-0.17.2-py3-none-win32.whl", hash = "sha256:6bce7d1f5165701ae18f5df85c65afe78cddfa6c5fb4b9bdb0014d60388f3edd", size = 9870537, upload-time = "2026-10-22T18:37:52.486Z" },
144
+ { url = "https://files.pythonhosted.org/packages/1c/9a/c7da96c2c96d20704cf9027f7a5121da272a8c05f1d1531ba949e3b3df04/ruff-0.17.2-py3-none-win_amd64.whl", hash = "sha256:1b59d447f197bef9ef48a22ba1a6016137753c6099e9bac9c36eb566e422424c", size = 10282826, upload-time = "2026-10-22T18:37:55.2Z" },
145
+ { url = "https://files.pythonhosted.org/packages/81/60/a3604c79cb02777b6cb84da894104021986f06c4bc41375102cc95049a32/ruff-0.17.2-py3-none-win_arm64.whl", hash = "sha256:195ad918027f817c119310ca89a42d347887dfc28934aaa869325e9c5dc9d962", size = 9827036, upload-time = "2026-10-22T18:37:58.031Z" },
146
+ ]
147
+
148
+ [[package]]
149
+ name = "shiboken6"
150
+ version = "6.12.0"
151
+ source = { registry = "https://pypi.org/simple" }
152
+ wheels = [
153
+ { url = "https://files.pythonhosted.org/packages/b5/45/11536fba5f50a57671eb14441c6098096f3e57074e54c9d7d66039a5831e/shiboken6-6.12.0-cp310-abi3-macosx_14_0_universal2.whl", hash = "sha256:47105e05baf57d35453e07d240bbe59cb4b8d9565377ea5134ff9fb37d4f5c02", size = 479653, upload-time = "2026-10-08T09:10:41.506Z" },
154
+ { url = "https://files.pythonhosted.org/packages/49/c2/a04fa003b4360e99d8e3fc2829abacf37396ace63ea1cd616402209ec7c6/shiboken6-6.12.0-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bde565bb0890044b5c7f63808adddede5abc241e9395a2bfed61919884d6240", size = 279268, upload-time = "2026-10-08T09:10:42.982Z" },
155
+ { url = "https://files.pythonhosted.org/packages/97/ca/a4415e6218e0dff764fe2b2d30db2fc8f499a08ace07634232ade3f73adc/shiboken6-6.12.0-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:ff72a72b3277b418902d562b987b33a6f6818bed9fe40a49d634d09bc67f620e", size = 276562, upload-time = "2026-10-08T09:10:44.25Z" },
156
+ { url = "https://files.pythonhosted.org/packages/2a/b6/bee97e7a2edeab72aafabb46ee5adc939790a638d107860b4cad7a20e407/shiboken6-6.12.0-cp310-abi3-win_amd64.whl", hash = "sha256:a1906cb8116869178c64b6bab52623bbc8b4ceed7b30bfc3c9aa0f6e67f6438e", size = 1265476, upload-time = "2026-10-08T09:10:45.627Z" },
157
+ { url = "https://files.pythonhosted.org/packages/8c/d5/5894d35f8a470ca8acff0d1cd8f15b1c94cbfdfe0ce4a6ec0fcf10b95bd1/shiboken6-6.12.0-cp310-abi3-win_arm64.whl", hash = "sha256:da382e68f0815b31b6dc2bce11a465b148c4f42c37b91546a361de1576567d10", size = 1838851, upload-time = "2026-10-08T09:10:47.123Z" },
158
+ ]
159
+
160
+ [[package]]
161
+ name = "talos-bar"
162
+ version = "0.0.1"
163
+ source = { editable = "." }
164
+ dependencies = [
165
+ { name = "pyside6-essentials" },
166
+ ]
167
+
168
+ [package.dev-dependencies]
169
+ dev = [
170
+ { name = "basedpyright" },
171
+ { name = "pytest" },
172
+ { name = "ruff" },
173
+ ]
174
+
175
+ [package.metadata]
176
+ requires-dist = [{ name = "pyside6-essentials", specifier = ">=6.7" }]
177
+
178
+ [package.metadata.requires-dev]
179
+ dev = [
180
+ { name = "basedpyright", specifier = ">=1.20" },
181
+ { name = "pytest", specifier = ">=8" },
182
+ { name = "ruff", specifier = ">=0.6" },
183
+ ]
184
+
185
+ [[package]]
186
+ name = "tomli"
187
+ version = "2.5.0"
188
+ source = { registry = "https://pypi.org/simple" }
189
+ sdist = { url = "https://files.pythonhosted.org/packages/b0/78/9ad63712633ed3ab5cc1a648d863d7e7da371e9425e209555a0fe711b695/tomli-2.5.0.tar.gz", hash = "sha256:264507556cd8b8c8e7c6ee037cdf443a463f03f4c958e57195e3d369711b8ff6", size = 17662, upload-time = "2026-10-07T12:23:37.892Z" }
190
+ wheels = [
191
+ { url = "https://files.pythonhosted.org/packages/22/a6/ab99b60ee52acd949684febabc3005d0045d0f66bebd9cdebd67372d26dd/tomli-2.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4dc1c1781f2f716de763d1e9a7b34c6a894e167e291c7c5d16c72f7a9538545", size = 163901, upload-time = "2026-10-07T12:22:15.601Z" },
192
+ { url = "https://files.pythonhosted.org/packages/bc/00/ee01b7ed4579180fff07142d290257f25ba786f23f3ec6005f620933c2f5/tomli-2.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eff8babca5a7999bc137acbc7482a8b7e17ffca5075ab41f5d770ab408c7bfef", size = 163756, upload-time = "2026-10-07T12:22:16.957Z" },
193
+ { url = "https://files.pythonhosted.org/packages/72/c2/4efebf65372f6583185f79799312109dddb61102d47e5c33dcfd1a297aca/tomli-2.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86665cee9c4835b7a7f1e8ec2c719b5258d4dc782887aded5a8ae7352a96843b", size = 268038, upload-time = "2026-10-07T12:22:18.135Z" },
194
+ { url = "https://files.pythonhosted.org/packages/53/07/5850468e925d898abb36038666f9c333a94d2a223e802a8ba5b6d319d23f/tomli-2.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e369fd63331746182360977b1892bfc215476a30d61612d732425311639f56", size = 276422, upload-time = "2026-10-07T12:22:19.567Z" },
195
+ { url = "https://files.pythonhosted.org/packages/b4/87/f293984cdcf83c054196d4fd3dad44fc68ae55b4b8c44bc76cef360c3150/tomli-2.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ad1ea345759240d6463efa0ed1c704402752e49aa21476620738d74d72d8aa1", size = 272616, upload-time = "2026-10-07T12:22:20.794Z" },
196
+ { url = "https://files.pythonhosted.org/packages/ce/ce/db582886b3c1219d3fec93ebd669332482e5aee7a91e0f7838d84f2d1759/tomli-2.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96243987194634bd411066ce40c952e108f86af04db533ecd8ac3ff2a85b1885", size = 276593, upload-time = "2026-10-07T12:22:22.12Z" },
197
+ { url = "https://files.pythonhosted.org/packages/bf/72/7619b87dea4261fc27dd7b54c4461c129c1f7d9bb7ba3aec89c797a431b8/tomli-2.5.0-cp311-cp311-win32.whl", hash = "sha256:610b27d99f28ec5f191c7064a48f3ddb179a1fe6ca73d571483ae859f57b605e", size = 101830, upload-time = "2026-10-07T12:22:23.651Z" },
198
+ { url = "https://files.pythonhosted.org/packages/1e/74/220106da34502304b6751a2a9b8a9fbca6c3fd47e737a2e2e3da7c61c9db/tomli-2.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:c804ae44fe7b4bab5da295e4f980a1ff04670bca9d23fe0a4e887e08ebd741a8", size = 112742, upload-time = "2026-10-07T12:22:24.972Z" },
199
+ { url = "https://files.pythonhosted.org/packages/27/99/7d9c8b41837a7773613e169504147375c157a290167aa59ad74a085f521f/tomli-2.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:cfac177ebd6236003846ea339981f71457cb6eb748f23381eb257e45092e3980", size = 109332, upload-time = "2026-10-07T12:22:26.117Z" },
200
+ { url = "https://files.pythonhosted.org/packages/52/ed/7baa86f87493646a594de388c7c1c40a39dd0461f7e9c0359cbeefc91fe8/tomli-2.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f4a40d03fb9f63424f0979855bdeaf44dd7696b8d59501822c10ed30ba532df", size = 164854, upload-time = "2026-10-07T12:22:27.444Z" },
201
+ { url = "https://files.pythonhosted.org/packages/a5/b1/44c0341f2224397855723c7a8a39f718ea6fcbcc3dacc66e5aeca0f334e3/tomli-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ebf8d19b17bd0daeb7b7dec81a946a439b753942fd0210d6e96c532249eea6b", size = 164074, upload-time = "2026-10-07T12:22:28.679Z" },
202
+ { url = "https://files.pythonhosted.org/packages/23/04/e2d5b7d3fba47adedb23de616c16d428ea076c79a3d8e1d95d649ffe197e/tomli-2.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf0b5e8e0f68ebb494356e577c06c139161efd8d3b9050f93b39b7c26cc54ff0", size = 274274, upload-time = "2026-10-07T12:22:29.804Z" },
203
+ { url = "https://files.pythonhosted.org/packages/43/90/6090e706ff27a6f89f4a40578e3324b95c3cd8c4150868aabf33a8f414c3/tomli-2.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cf74416bdc94ae458b14e37286c1073081850ac8459a00d0c5efef5d44294c6", size = 286435, upload-time = "2026-10-07T12:22:31.297Z" },
204
+ { url = "https://files.pythonhosted.org/packages/0a/9e/a2c40768df16c408f22430afb0a73e9d7e5f79c950884954649d1146b74d/tomli-2.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:61ea1ebe1e55a34ea8199cc8dbff398d35027b82271c8ac4802fd3a1fd5b1bcc", size = 278119, upload-time = "2026-10-07T12:22:32.601Z" },
205
+ { url = "https://files.pythonhosted.org/packages/12/25/3c0cb485b98e9cfac495629b1c93c87ccf0b72fbe9d2689fd8fe62c6d5a3/tomli-2.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed53f7e89bb04f6d9e8e7799112360b0c4d5cbff067de0814c98c37c39b920f7", size = 286177, upload-time = "2026-10-07T12:22:33.745Z" },
206
+ { url = "https://files.pythonhosted.org/packages/77/8b/0144c65f0e37e51c18d04ae15c21b19431c165002d0131fe9aa8b0b8b1e8/tomli-2.5.0-cp312-cp312-win32.whl", hash = "sha256:e7ad033e27a516a233bea839cdb77b80146facb3b4f40bf02cd0cac165cdd5c2", size = 102760, upload-time = "2026-10-07T12:22:34.887Z" },
207
+ { url = "https://files.pythonhosted.org/packages/de/32/5d6d8f42fc9a05fce69354e00ff256484192f5f2fc9a2165718fa0de61ec/tomli-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd05de8c1698f8413dd7d869492693a0bf2211543b787ac78cd5e7536af1a6d7", size = 112722, upload-time = "2026-10-07T12:22:36.162Z" },
208
+ { url = "https://files.pythonhosted.org/packages/30/65/df18032218db0fb9b769fb23c8039a051f15c811993995ea04c350273a32/tomli-2.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:069435bd5480429b98c5e5afb02ab21c219b6f0064680671c6dc0d46817346ea", size = 109534, upload-time = "2026-10-07T12:22:37.296Z" },
209
+ { url = "https://files.pythonhosted.org/packages/42/e5/51736d70da209350969e15aca5c5ab6e2ce1ea87a0a892a6c13aec172a86/tomli-2.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:943276cf269e0071948d9ff697159c1735e623c1151d88abb09b74659ef0cbea", size = 163328, upload-time = "2026-10-07T12:22:38.373Z" },
210
+ { url = "https://files.pythonhosted.org/packages/ec/55/086f80dab4ab497602644274e6dea7ec5dd0b4e262e443a8ad3bb7edee2d/tomli-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:463b16086865b97facd8d0b3fb4cb7c544e3f58d2a69dc3113d6db9653fdb043", size = 162246, upload-time = "2026-10-07T12:22:39.673Z" },
211
+ { url = "https://files.pythonhosted.org/packages/aa/eb/3ecc94459f3635c92321f4e7bde571323fdb2267c50e19e3188a281eae3b/tomli-2.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1245a6638fc4bb0a60af38a7d45413db34a13842027c77597c712c998c62fdf0", size = 272655, upload-time = "2026-10-07T12:22:41.08Z" },
212
+ { url = "https://files.pythonhosted.org/packages/c0/d7/494fd1f0c37a621f1ad9975c2efadb523e8101f144ed6edb2e7fe64738f2/tomli-2.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d8bac3d603c97e6854424e5b2b5b741bdbde387e09f162fb0446812b4a8362b", size = 283595, upload-time = "2026-10-07T12:22:42.222Z" },
213
+ { url = "https://files.pythonhosted.org/packages/70/51/bb8d62b1317e6640866f6949b2d5855e5300f2c99d46de1cd245570bba65/tomli-2.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:21e4cae4114aba25aa0d4f85cdf486d290fb35c0954d7bba536248da64d43066", size = 276253, upload-time = "2026-10-07T12:22:43.625Z" },
214
+ { url = "https://files.pythonhosted.org/packages/66/f4/f46bd7f0763cd47de2db697dca9257c6a4adfd1a93b018cc75c8190ed5a8/tomli-2.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bbaefc84548d754be821bba7c4141c4787dda182f9e77f2f87b71213529efa7b", size = 283582, upload-time = "2026-10-07T12:22:44.983Z" },
215
+ { url = "https://files.pythonhosted.org/packages/ac/03/70f2bcb2923a6db37818d917e124270a7f4cfd38ea576f5aa753a91c0ef5/tomli-2.5.0-cp313-cp313-win32.whl", hash = "sha256:abdbf6313b8d9efe157edeb7ab6eae4de064b1300ad31abf73755154b30abe68", size = 102628, upload-time = "2026-10-07T12:22:46.508Z" },
216
+ { url = "https://files.pythonhosted.org/packages/dc/98/d52024bb5b0ff68b4f0d276d867f634c84a67319a7e9f6b7708a37742333/tomli-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd4dc129784e0c5335bd4e61dfcc4487499a013419e655cf2da1d091b7e0efdc", size = 113301, upload-time = "2026-10-07T12:22:47.647Z" },
217
+ { url = "https://files.pythonhosted.org/packages/6f/f2/540db3a70572a8c23a28aba3e9c358ce0ffffbafc990905c1343aa265b31/tomli-2.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:69491c143d2fe063046e0301e62a810bed338fa4d1ce0fd870c27dc1e09b0d84", size = 109744, upload-time = "2026-10-07T12:22:48.925Z" },
218
+ { url = "https://files.pythonhosted.org/packages/e4/49/caf6b307766eb9567664a8707e9d6be5fcc0e8903f18781c6677a60d80c7/tomli-2.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d3182ee2d887e507bd67319a0a61105d1dd33facc111329559a233b772c1a105", size = 162899, upload-time = "2026-10-07T12:22:50.088Z" },
219
+ { url = "https://files.pythonhosted.org/packages/d3/c8/68cfce773a2733a49c74f99d627fb461bd990756860099eac25617889585/tomli-2.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:521345fd1f19d45b8df87657aaa38b6f2ca3800059fadf428e7ebf479a383646", size = 162080, upload-time = "2026-10-07T12:22:51.558Z" },
220
+ { url = "https://files.pythonhosted.org/packages/7e/b2/e5bb8651fdad593f670501a7d718b1a7f73f064d44dea15e04c04dfef45d/tomli-2.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e95c7614e705bfe2b04b27aa124adec59752d15813df37e2156747cab3a006b", size = 273380, upload-time = "2026-10-07T12:22:52.918Z" },
221
+ { url = "https://files.pythonhosted.org/packages/8d/d2/9e2d7f8b1dfe0e2b34c245986ebd55c4c553ea4ce6c47c443b332673253f/tomli-2.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac2027d37c3afbdf4bdd377f2676f6f1d2122a5be1f1137b49dced590b37e75", size = 283228, upload-time = "2026-10-07T12:22:54.173Z" },
222
+ { url = "https://files.pythonhosted.org/packages/ba/df/ec7b876b7b1a2718bd74a3743c076fff565b04029ba33e8f61fac262739f/tomli-2.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c414be4ed9d3cac80c42e348fa5a956117d1a48227f48026e31f59cb4a7671eb", size = 277189, upload-time = "2026-10-07T12:22:55.342Z" },
223
+ { url = "https://files.pythonhosted.org/packages/7d/7b/e192d9eed0b9cb80da799f4d77052297fb9a2c3cc9b19f571f56ea88add6/tomli-2.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9b03d7dc168353b4132965bde20feceabaa470e570c6f59660dfae59b1f9eeb3", size = 283632, upload-time = "2026-10-07T12:22:56.735Z" },
224
+ { url = "https://files.pythonhosted.org/packages/84/50/ff94454e75461d75623e47401ed323d65c10aab8fe9033242c20cd2fdf32/tomli-2.5.0-cp314-cp314-win32.whl", hash = "sha256:6f041843c4d3a37245c0c056fd955b186bf8b1fb85690cbe40b81230891dc34b", size = 103535, upload-time = "2026-10-07T12:22:58.084Z" },
225
+ { url = "https://files.pythonhosted.org/packages/54/0b/bdacf05f963bd6026ebf6eeb0beda847d1d60e03e440725c64a4e08a0afd/tomli-2.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:f4b653094e18f9031102d3a1da5c729c8f222d85225b18037dac621695e46e1a", size = 114621, upload-time = "2026-10-07T12:22:59.2Z" },
226
+ { url = "https://files.pythonhosted.org/packages/61/99/53f438fa6ae4f9d4ed0ddde3e7242b3bdc34b48c8f9948b72b9e9b127676/tomli-2.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:3f89d10c1ff6a38d992c27fc8a4816af71a909e08a40ec66934240b1e74347c3", size = 111572, upload-time = "2026-10-07T12:23:00.479Z" },
227
+ { url = "https://files.pythonhosted.org/packages/b9/20/1f88f19427d380a40e90a770e087489eaafe4aeee070ae88ed2bbec00acd/tomli-2.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e9e15b4a6c7dd6b85b5fbab29488a73f1f70de516942308daa266bf0e0aeb0d4", size = 171814, upload-time = "2026-10-07T12:23:01.914Z" },
228
+ { url = "https://files.pythonhosted.org/packages/d0/56/cbe5079c9f9a54b9b3e27fc82f08f3cb36edee75561679f53d2380c801d6/tomli-2.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e12bbcd32897272fb05929110362ae9ff4c1b9bb26bd9e971e71dcd3275b4c3d", size = 171324, upload-time = "2026-10-07T12:23:03.18Z" },
229
+ { url = "https://files.pythonhosted.org/packages/2b/30/1d53fd3b0f1cb3ba542e345ec32c26aefdddc4e829e4f3429af8a4f27782/tomli-2.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20aa36de8f2cf87237143bc1fa1aae8d6612c09118f4da21c6a684db5dd1f6f9", size = 297441, upload-time = "2026-10-07T12:23:04.345Z" },
230
+ { url = "https://files.pythonhosted.org/packages/66/d9/0800acb6a111686f764c1b91ef15cc42a20a66a46013bb42220f1d2c61c1/tomli-2.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22185fad8a1e622f064e78008018a0dd3323550dcb479cb7a1d296888d74024f", size = 307476, upload-time = "2026-10-07T12:23:05.671Z" },
231
+ { url = "https://files.pythonhosted.org/packages/e8/63/30a8f3cd51b5bec37f04744bad0b0dc6160df84aad4f27b0e9283d66f221/tomli-2.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:984012f71908165449a951de2050d52f276bfe3aa5d5f570f63ddad814370374", size = 296113, upload-time = "2026-10-07T12:23:07.202Z" },
232
+ { url = "https://files.pythonhosted.org/packages/ab/18/0b9ffc597e69c5a1e20a7823cb60d54b39a9f54e91edcb8574f022186758/tomli-2.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f79203b3965b4000e91808aaa7c040206093f2b8bf86f455982f2274c9ccf442", size = 307725, upload-time = "2026-10-07T12:23:08.508Z" },
233
+ { url = "https://files.pythonhosted.org/packages/ab/c7/18f8baae0b5607a60e8e19b4a7fedee43a8ff6458e3896dcbbadeeac9c22/tomli-2.5.0-cp314-cp314t-win32.whl", hash = "sha256:91294a9fb94a75542f6e46e4a2ae709bd8d9b51134098cae5cf3bea5478b6d03", size = 108546, upload-time = "2026-10-07T12:23:09.956Z" },
234
+ { url = "https://files.pythonhosted.org/packages/72/34/4cca9739254130627bde87500b3f2b512154fe2f278efa7e2a5e10ad4bcb/tomli-2.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f15e3e0b835a6d68b10c86bf80a3149780498d6911c93c3ffd1861d19f9200f1", size = 117814, upload-time = "2026-10-07T12:23:11.486Z" },
235
+ { url = "https://files.pythonhosted.org/packages/7d/fb/afa530d47dd80a78fce43beac6bc6e00f84558eafcffbc6f37b21e80d056/tomli-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6664b7ae7af7294256c53960a6103077f4914cec8ff98479c352f622c6f6b2f0", size = 115188, upload-time = "2026-10-07T12:23:12.728Z" },
236
+ { url = "https://files.pythonhosted.org/packages/66/98/316fdc00f8c0939e6fe50461dd343c162d3ad51d1286eb25b7db54361d50/tomli-2.5.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a525685c2f97da40762b8695eb7aa0af4c8344ca1905c73e4e29cb04d34607dc", size = 162775, upload-time = "2026-10-07T12:23:13.941Z" },
237
+ { url = "https://files.pythonhosted.org/packages/c5/22/7b10fa5bb01c9539f53f69b619361b19350acc73657772ea7ac70ba309a8/tomli-2.5.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9dbb18c1cfb2f6517942fc9314437f66aa06d94436ffb1f06102ef3572f35276", size = 161406, upload-time = "2026-10-07T12:23:15.215Z" },
238
+ { url = "https://files.pythonhosted.org/packages/9c/e7/1a069d86dfd20f1f84f71c63faed9f83c1d890bc06c27d82dc7d888fb573/tomli-2.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:752e8b1aa6a4367ef8bf6a1a1e005540f7ed055ba36d7193796812ca5404eb52", size = 273855, upload-time = "2026-10-07T12:23:16.471Z" },
239
+ { url = "https://files.pythonhosted.org/packages/ae/83/d1ef43d1687d092ab9c235455c76e6e709483b346b056f086095c7c263a5/tomli-2.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47300f9bf791808f77d82747691c4bb09cb14bdf3060cca99b42cdc4361d5a7", size = 284910, upload-time = "2026-10-07T12:23:18.166Z" },
240
+ { url = "https://files.pythonhosted.org/packages/cc/05/f4d9cf7de61822ece0c3873f30d291e324911c71a378b8bfe5ced13fd9f5/tomli-2.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:19b0dd8749f4ea2f112c5fcfb3c5248390c899d7e2e173f1d91abee1fa0ff391", size = 277723, upload-time = "2026-10-07T12:23:19.355Z" },
241
+ { url = "https://files.pythonhosted.org/packages/42/28/78262493141fa543151cf005760c3cb01d09fc28a11f993c05109902cb8c/tomli-2.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:57b1c3b01fab802e2899bc3d168dca320e14165e2fd9fd584760fb4ca5826859", size = 285115, upload-time = "2026-10-07T12:23:20.698Z" },
242
+ { url = "https://files.pythonhosted.org/packages/1a/b9/e1dab9a30bcb677b5cc5cee810609cfd64f24306a3055767dd3fda00b1e0/tomli-2.5.0-cp315-cp315-win32.whl", hash = "sha256:667e521b37a6c5ccaa044202c235b530f90177ffe2cd4a64ecc213c7dd535feb", size = 103475, upload-time = "2026-10-07T12:23:21.941Z" },
243
+ { url = "https://files.pythonhosted.org/packages/4c/bd/31a3790c11d6ea95fcf5e6022ac0f8d0543c9b61120b730fc481bd43d3b4/tomli-2.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:d747252933c8a65ef6bd8da0fbb7ce28a90eb6119d8cd00772cd528aa07b68d5", size = 114589, upload-time = "2026-10-07T12:23:23.098Z" },
244
+ { url = "https://files.pythonhosted.org/packages/47/a2/4f6310fa699364f0e3af7ee3af88dddd9af066d33e716a0265bbe2b3ea84/tomli-2.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:75dbcde8751b0a960aa3de173aa5e894d590755c6d7758b7e774c06f1dc3cbdd", size = 111493, upload-time = "2026-10-07T12:23:24.233Z" },
245
+ { url = "https://files.pythonhosted.org/packages/68/14/00853f0b396d8971107ae1921bb5b322fdee1650d2f16bf06c20adb532e5/tomli-2.5.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2419c2a189551987b59d80e63ec355671283336f41c6b9b89462df679c7d0c57", size = 171380, upload-time = "2026-10-07T12:23:25.512Z" },
246
+ { url = "https://files.pythonhosted.org/packages/89/ad/fa6949321dadee46b27363974fb197b94c911c3b0f7a5fd26d7dc18fc2a0/tomli-2.5.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dc598040da8d42cf20f0be588ed7004f46db12a0ac6c32e03a59dccedaaadcd", size = 170553, upload-time = "2026-10-07T12:23:26.855Z" },
247
+ { url = "https://files.pythonhosted.org/packages/53/aa/3056c919eb3e084df3752b2cf5f865dcc04af0b27dba2f66d7b28af4633a/tomli-2.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49096930c8d886c9bbdab62d2d0d17ce823ddeea522309a190b36245d5b49e01", size = 294428, upload-time = "2026-10-07T12:23:28.132Z" },
248
+ { url = "https://files.pythonhosted.org/packages/96/b2/faeeb5d8769ea3832021d73e892c8391eae7b4b4f8b55a789127bd8b18a9/tomli-2.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8ade5023067f99fe72b88accd30d0ea05a158e9e32a11f124e731ea9695313f", size = 304909, upload-time = "2026-10-07T12:23:29.381Z" },
249
+ { url = "https://files.pythonhosted.org/packages/f6/52/f094c09e73fb654b621716d019acb5d29bdfd1be01df80c281d552bda48d/tomli-2.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b69564772b5c8f22ea5f498dff08cfa825045b4d4c4400529000bdf818aa3b2a", size = 293220, upload-time = "2026-10-07T12:23:30.608Z" },
250
+ { url = "https://files.pythonhosted.org/packages/86/f5/0c30541078ca4b505ce3bd76ed931facbfec524dd018535d691d1af0a6d2/tomli-2.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8ff3a2ca028c7eee0c777f9a092038d0a594a9fa04e215f929a22c329e2cb142", size = 305705, upload-time = "2026-10-07T12:23:32.181Z" },
251
+ { url = "https://files.pythonhosted.org/packages/05/74/590e7d19d6a118fc5cc5704ff358e21d95b8573f6b9443b1519f29ca8825/tomli-2.5.0-cp315-cp315t-win32.whl", hash = "sha256:62fc1bc8eb03e3a9cadfca713d65614ed8e09d974a283295ffe3a831976b4dc5", size = 108432, upload-time = "2026-10-07T12:23:33.496Z" },
252
+ { url = "https://files.pythonhosted.org/packages/1c/b8/63a75cfb27a17c38550e44025d3a6e7be64516fd8608a3b75703bf37d81b/tomli-2.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f3fcbc57b1791fa6cbe5d8434179d51de12be1a4811469529f47f6e7487a2571", size = 117281, upload-time = "2026-10-07T12:23:34.648Z" },
253
+ { url = "https://files.pythonhosted.org/packages/72/01/e8c1debb2173973372934c68fc8e46170ab60ef23ed4592dff4dec6e8993/tomli-2.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d2ba24db8a9376921b5e87b4762b9adb0f3f1deaea68f2b8b0bb2c11efb9c3e7", size = 115069, upload-time = "2026-10-07T12:23:35.77Z" },
254
+ { url = "https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl", hash = "sha256:32a7b79ac57a2e83670ce329ccf675798bc5a2094783a63676866b70503f2e2b", size = 14765, upload-time = "2026-10-07T12:23:36.875Z" },
255
+ ]
256
+
257
+ [[package]]
258
+ name = "typing-extensions"
259
+ version = "4.16.0"
260
+ source = { registry = "https://pypi.org/simple" }
261
+ sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
262
+ wheels = [
263
+ { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
264
+ ]