Initial VimKit extraction

This commit is contained in:
2026-07-11 16:12:25 -04:00
commit 12c4e5cae0
22 changed files with 3267 additions and 0 deletions

31
.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Python bytecode and caches
__pycache__/
*.py[oc]
*.pyd
*.pyo
*.so
.pytest_cache/
.mypy_cache/
.ruff_cache/
.coverage
.coverage.*
htmlcov/
# Build artifacts
build/
dist/
wheels/
*.egg-info
site/
# Virtual environments
.venv
venv/
# Editors and local tooling
.DS_Store
.idea/
.vscode/
# uv and local Python state
.python-version

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Raelon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

52
README.md Normal file
View File

@@ -0,0 +1,52 @@
# VimKit
Reusable Textual vim-style editor components extracted from Fleet.
## What is here
- `VimEditor`: the core modal editor widget
- `VimWorkbench`: editor plus VimKit-owned top bar, command bar, and status bar
- `VimExtension`: extension hook for `:` commands and editor integrations
- `VimHost`: host interface for project-specific behaviors like named directories
## Status
The package is source-isolated and can be developed independently from Fleet.
## Local development
```bash
uv sync
uv run python -m compileall src
```
## Install
```bash
pip install vimkit
```
## Using from another project
During development, add it as a local path dependency:
```bash
uv add --editable ../VimKit
```
Later, switch to a Git dependency or the published PyPI package.
## First release flow
1. Bump the version in `pyproject.toml`.
2. Sync dev tools with `uv sync --dev`.
3. Build distributions with `uv build`.
4. Validate artifacts with `uv run twine check dist/*`.
5. Upload with `uv run twine upload dist/*`.
PyPI will prompt for your username and password unless you use an API token.
## Notes
- VimKit is released under the MIT license.
- If you later mirror this to GitHub, PyPI Trusted Publishing is preferable to long-lived credentials.

38
pyproject.toml Normal file
View File

@@ -0,0 +1,38 @@
[project]
name = "vimkit"
version = "0.1.0"
description = "Reusable Textual vim-style editor widgets and extension hooks."
readme = "README.md"
authors = [
{ name = "Raelon", email = "tty303@proton.me" }
]
license = "MIT"
license-files = ["LICENSE"]
requires-python = ">=3.14"
keywords = ["textual", "tui", "vim", "editor", "terminal-ui"]
classifiers = [
"Development Status :: 3 - Alpha",
"Framework :: Textual",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.14",
"Topic :: Software Development :: User Interfaces",
"Topic :: Terminals",
"Typing :: Typed",
]
dependencies = [
"textual[syntax]>=8.2.8",
]
[project.urls]
Homepage = "https://git.th3r00t.net/th3r00t/VimKit"
Source = "https://git.th3r00t.net/th3r00t/VimKit"
[dependency-groups]
dev = [
"twine>=6.1.0",
]
[build-system]
requires = ["uv_build>=0.11.23,<0.12.0"]
build-backend = "uv_build"

41
src/vimkit/__init__.py Normal file
View File

@@ -0,0 +1,41 @@
"""Reusable VimKit package decoupled from FleetCommand."""
__version__ = "0.1.0"
from vimkit.area import VimTextArea
from vimkit.clipboard import copy_text_to_clipboard
from vimkit.commands import COMMANDS, NORMAL_MOTIONS
from vimkit.editor import VimEditor
from vimkit.extensions import VimExtension
from vimkit.host import VimHost
from vimkit.messages import BufferPathChanged, ModeChanged, QuitRequested, Submitted
from vimkit.modes import VimMode
from vimkit.modals import TextModal
from vimkit.panels import CommandBar, FleetPrompt, WhichKeyPanel
from vimkit.state import VimSessionState
from vimkit.theme import register_cyberdream
from vimkit.workbench import VimStatusBar, VimTopBar, VimWorkbench
__all__ = [
"BufferPathChanged",
"COMMANDS",
"NORMAL_MOTIONS",
"CommandBar",
"FleetPrompt",
"ModeChanged",
"QuitRequested",
"Submitted",
"TextModal",
"VimHost",
"VimEditor",
"VimMode",
"VimTextArea",
"VimExtension",
"VimSessionState",
"VimStatusBar",
"VimTopBar",
"VimWorkbench",
"WhichKeyPanel",
"__version__",
"copy_text_to_clipboard",
"register_cyberdream",
]

766
src/vimkit/area.py Normal file
View File

@@ -0,0 +1,766 @@
"""TextArea implementation for vimkit modal editing behavior."""
from __future__ import annotations
from typing import TYPE_CHECKING
from textual.binding import Binding
from textual.events import Key
from textual.widgets import TextArea
from vimkit.clipboard import copy_text_to_clipboard
from vimkit.modes import VimMode
from vimkit.panels import WhichKeyPanel
if TYPE_CHECKING:
from vimkit.editor import VimEditor
_vim_clipboard: str = ""
_vim_clipboard_linewise: bool = False
class VimTextArea(TextArea):
BINDINGS = [
Binding("ctrl+j", "submit_chat", "Send", show=False, priority=True),
Binding("ctrl+enter", "submit_chat", "Send", show=False, priority=True),
Binding("shift+enter", "submit_chat", "Send", show=False, priority=True),
Binding("enter", "handle_enter", "Enter", show=False, priority=True),
Binding("escape", "to_normal", "Normal", show=False, priority=True),
Binding("ctrl+z", "undo", "Undo", show=False, priority=False),
Binding("ctrl+r", "redo", "Redo", show=False, priority=False),
]
def _outer(self) -> "VimEditor | None":
outer = self.parent
from vimkit.editor import VimEditor
return outer if isinstance(outer, VimEditor) else None
def action_submit_chat(self) -> None:
outer = self._outer()
if outer is None or outer.mode == VimMode.COMMAND:
return
text = self.text.strip()
if text:
outer.post_message(outer.Submitted(outer, text))
def action_newline(self) -> None:
outer = self._outer()
if outer is not None and outer.mode == VimMode.INSERT:
self.insert("\n")
def action_handle_enter(self) -> None:
outer = self._outer()
if outer is None:
return
if outer.mode == VimMode.INSERT:
self.action_newline()
elif outer.mode == VimMode.NORMAL:
self.action_submit_chat()
elif outer.mode == VimMode.COMMAND:
outer._end_command(execute=True)
def action_to_normal(self) -> None:
outer = self._outer()
if outer is None:
return
if outer.mode in (VimMode.INSERT, VimMode.VISUAL, VimMode.VISUAL_LINE):
from textual.widgets.text_area import Selection
self.selection = Selection.cursor(self.cursor_location)
outer.set_mode(VimMode.NORMAL)
elif outer.mode == VimMode.COMMAND:
outer._end_command(execute=False)
def on_key(self, event: Key) -> None:
outer = self._outer()
if outer is None:
return
mode = outer.mode
k = event.key
ch = event.character or ""
if mode == VimMode.INSERT and self._is_submit_key(event):
event.prevent_default()
event.stop()
self.action_submit_chat()
return
if mode == VimMode.INSERT and self._is_enter_key(event):
event.prevent_default()
event.stop()
self.action_newline()
return
if mode == VimMode.NORMAL:
event.prevent_default()
event.stop()
if outer._find_char_pending and ch and len(ch) == 1:
self._do_find_char(ch, forward=(outer._find_char_pending == "f"))
outer._last_find = (ch, outer._find_char_pending == "f")
outer._find_char_pending = None
return
if outer._replace_pending and ch and len(ch) == 1:
self._do_replace_char(ch)
outer._replace_pending = False
outer._last_action = ("r", ch)
return
if ch and ch.isdigit() and not (ch == "0" and not outer._count_buf):
outer._count_buf += ch
return
if outer._op_buf or ch in ("g", "c", "d", "y"):
if self._handle_normal_sequence(outer, ch):
return
if ch == " ":
outer._op_buf = ""
try:
self.app.query_one(WhichKeyPanel).show()
except Exception:
pass
elif ch == "i":
outer._op_buf = ""
outer.set_mode(VimMode.INSERT)
elif ch == "I":
outer._op_buf = ""
self.action_cursor_line_start()
outer.set_mode(VimMode.INSERT)
elif ch == "A":
outer._op_buf = ""
self.action_cursor_line_end()
outer.set_mode(VimMode.INSERT)
elif ch == "a":
outer._op_buf = ""
outer.set_mode(VimMode.INSERT)
self.action_cursor_right()
elif ch == "o":
outer._op_buf = ""
self._open_line_below()
outer.set_mode(VimMode.INSERT)
elif ch == "O":
outer._op_buf = ""
self._open_line_above()
outer.set_mode(VimMode.INSERT)
elif ch == "v":
outer._op_buf = ""
self._visual_anchor = self.cursor_location
outer.set_mode(VimMode.VISUAL)
elif ch == "V":
outer._op_buf = ""
self._visual_anchor = self.cursor_location
outer.set_mode(VimMode.VISUAL_LINE)
elif ch == ":":
outer._op_buf = ""
outer._start_command()
elif self._is_enter_key(event):
outer._op_buf = ""
self.action_submit_chat()
elif ch == "h" or k == "left":
outer._op_buf = ""
self.action_cursor_left()
elif ch == "l" or k == "right":
outer._op_buf = ""
self.action_cursor_right()
elif ch == "j" or k == "down":
outer._op_buf = ""
self.action_cursor_down()
elif ch == "k" or k == "up":
outer._op_buf = ""
self.action_cursor_up()
elif ch == "0":
outer._op_buf = ""
self.action_cursor_line_start()
elif ch == "$":
outer._op_buf = ""
self.action_cursor_line_end()
elif ch == "G":
outer._op_buf = ""
self._move_doc_end()
elif ch == "w":
outer._op_buf = ""
self._move_word_forward()
elif ch == "b":
outer._op_buf = ""
self._move_word_backward()
elif ch == "e":
outer._op_buf = ""
self._move_word_end()
elif ch == "x":
outer._op_buf = ""
self.read_only = False
self.action_delete_right()
self.read_only = True
outer._last_action = ("x",)
elif ch == "D":
outer._op_buf = ""
self._delete_to_line_end()
elif ch == "C":
outer._op_buf = ""
self._delete_to_line_end()
outer.set_mode(VimMode.INSERT)
elif ch == "p":
outer._op_buf = ""
self._paste_after()
elif ch == "P":
outer._op_buf = ""
self._paste_before()
elif ch == "u":
outer._op_buf = ""
self.read_only = False
self.action_undo()
self.read_only = True
elif k == "ctrl+r":
outer._op_buf = ""
self.read_only = False
self.action_redo()
self.read_only = True
elif ch == "f":
outer._op_buf = ""
outer._find_char_pending = "f"
elif ch == "F":
outer._op_buf = ""
outer._find_char_pending = "F"
elif ch == ";":
outer._op_buf = ""
if outer._last_find:
self._do_find_char(outer._last_find[0], forward=outer._last_find[1])
elif ch == ",":
outer._op_buf = ""
if outer._last_find:
self._do_find_char(outer._last_find[0], forward=not outer._last_find[1])
elif ch == "r":
outer._op_buf = ""
outer._replace_pending = True
elif ch == ".":
outer._op_buf = ""
if outer._last_action:
self._replay_last_action(outer)
else:
outer._op_buf = ""
outer._count_buf = ""
elif mode in (VimMode.VISUAL, VimMode.VISUAL_LINE):
event.prevent_default()
event.stop()
anchor = getattr(self, "_visual_anchor", self.cursor_location)
if k == "escape":
from textual.widgets.text_area import Selection
self.selection = Selection.cursor(self.cursor_location)
outer.set_mode(VimMode.NORMAL)
elif ch == "v":
from textual.widgets.text_area import Selection
if mode == VimMode.VISUAL:
self.selection = Selection.cursor(self.cursor_location)
outer.set_mode(VimMode.NORMAL)
else:
outer.set_mode(VimMode.VISUAL)
self._update_visual_selection(anchor, VimMode.VISUAL)
elif ch == "V":
if mode == VimMode.VISUAL_LINE:
from textual.widgets.text_area import Selection
self.selection = Selection.cursor(self.cursor_location)
outer.set_mode(VimMode.NORMAL)
else:
outer.set_mode(VimMode.VISUAL_LINE)
self._update_visual_selection(anchor, VimMode.VISUAL_LINE)
elif ch in "hjkl" or k in ("left", "right", "up", "down"):
if ch == "h" or k == "left":
self.action_cursor_left()
elif ch == "l" or k == "right":
self.action_cursor_right()
elif ch == "j" or k == "down":
self.action_cursor_down()
elif ch == "k" or k == "up":
self.action_cursor_up()
self._update_visual_selection(anchor, mode)
elif ch == "0":
self.action_cursor_line_start()
self._update_visual_selection(anchor, mode)
elif ch == "$":
self.action_cursor_line_end()
self._update_visual_selection(anchor, mode)
elif ch == "G":
self._move_doc_end()
self._update_visual_selection(anchor, mode)
elif ch == "w":
self._move_word_forward()
self._update_visual_selection(anchor, mode)
elif ch == "b":
self._move_word_backward()
self._update_visual_selection(anchor, mode)
elif ch == "y":
self._yank_selection(mode)
from textual.widgets.text_area import Selection
self.selection = Selection.cursor(self.cursor_location)
outer.set_mode(VimMode.NORMAL)
elif ch == "d":
self._delete_selection()
outer.set_mode(VimMode.NORMAL)
elif ch == "c":
self._delete_selection()
outer.set_mode(VimMode.INSERT)
elif mode == VimMode.COMMAND:
if k == "escape":
return
event.prevent_default()
event.stop()
if self._is_enter_key(event) or k in ("shift+enter", "shift_enter"):
outer._end_command(execute=True)
elif k in ("up", "ctrl+p"):
if outer._cmd_history:
outer._cmd_hist_idx = min(outer._cmd_hist_idx + 1, len(outer._cmd_history) - 1)
outer._cmd_buf = outer._cmd_history[-(outer._cmd_hist_idx + 1)]
outer._refresh_cmd()
elif k in ("down", "ctrl+n"):
if outer._cmd_hist_idx > 0:
outer._cmd_hist_idx -= 1
outer._cmd_buf = outer._cmd_history[-(outer._cmd_hist_idx + 1)]
else:
outer._cmd_hist_idx = -1
outer._cmd_buf = ""
outer._refresh_cmd()
elif k == "backspace":
if outer._cmd_buf:
outer._cmd_buf = outer._cmd_buf[:-1]
outer._refresh_cmd()
if not outer._cmd_buf:
outer._end_command(execute=False)
elif k == "tab":
outer._apply_completion()
if outer._completions:
outer._comp_idx = (outer._comp_idx + 1) % len(outer._completions)
outer._update_command_bar()
elif k == "shift+tab":
if outer._completions:
outer._comp_idx = (outer._comp_idx - 1) % len(outer._completions)
outer._apply_completion()
outer._update_command_bar()
elif ch and ch.isprintable():
outer._cmd_buf += ch
outer._refresh_cmd()
def _is_submit_key(self, event: Key) -> bool:
if event.key in ("ctrl+j", "ctrl+enter", "shift+enter", "shift_enter"):
return True
if event.key == "enter" and getattr(event, "shift", False):
return True
return False
def _is_enter_key(self, event: Key) -> bool:
return event.key in ("enter", "return", "ctrl+m")
def _handle_normal_sequence(self, outer: "VimEditor", ch: str) -> bool:
if ch not in ("g", "c", "d", "i", "w", "y"):
return False
outer._op_buf += ch
seq = outer._op_buf
if seq == "gg":
self.move_cursor((0, 0), center=True)
outer._op_buf = ""
return True
if seq == "dd":
self._delete_line()
outer._last_action = ("dd",)
outer._op_buf = ""
return True
if seq == "yy":
self._yank_line()
outer._op_buf = ""
return True
if seq == "cc":
self._delete_line_content()
outer.set_mode(VimMode.INSERT)
outer._op_buf = ""
return True
if seq == "ciw":
if self._delete_inner_word():
outer.set_mode(VimMode.INSERT)
outer._op_buf = ""
return True
if seq == "diw":
self._delete_inner_word()
outer._op_buf = ""
return True
if seq == "dw":
self._delete_word_forward()
outer._last_action = ("dw",)
outer._op_buf = ""
return True
if seq == "cw":
self._delete_word_forward()
outer._last_action = ("cw",)
outer.set_mode(VimMode.INSERT)
outer._op_buf = ""
return True
if seq == "yw":
self._yank_word_forward()
outer._op_buf = ""
return True
if seq in ("g", "c", "d", "y", "ci", "di"):
return True
outer._op_buf = ""
return False
def _delete_inner_word(self) -> bool:
bounds = self._inner_word_bounds()
if bounds is None:
return False
start, end = bounds
self.read_only = False
try:
self.delete(start, end)
self.move_cursor(start)
finally:
self.read_only = True
return True
def _inner_word_bounds(self) -> tuple[tuple[int, int], tuple[int, int]] | None:
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if row >= len(lines):
return None
line = lines[row]
if not line:
return None
def is_word_char(idx: int) -> bool:
return line[idx].isalnum() or line[idx] == "_"
if col >= len(line):
col = max(len(line) - 1, 0)
idx = col
if not is_word_char(idx):
right = idx
while right < len(line) and not is_word_char(right):
right += 1
if right < len(line):
idx = right
else:
left = idx
while left >= 0 and not is_word_char(left):
left -= 1
if left < 0:
return None
idx = left
start = idx
while start > 0 and is_word_char(start - 1):
start -= 1
end = idx + 1
while end < len(line) and is_word_char(end):
end += 1
return (row, start), (row, end)
def _move_doc_end(self) -> None:
lines = self.text.splitlines() or [""]
row = max(len(lines) - 1, 0)
self.move_cursor((row, len(lines[row])), center=True)
def _move_word_forward(self) -> None:
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if row >= len(lines):
return
line = lines[row]
while col < len(line) and (line[col].isalnum() or line[col] == "_"):
col += 1
while col < len(line) and not (line[col].isalnum() or line[col] == "_"):
col += 1
if col >= len(line) and row + 1 < len(lines):
row += 1
col = 0
self.move_cursor((row, col))
def _move_word_backward(self) -> None:
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if col == 0:
if row > 0:
row -= 1
col = len(lines[row])
else:
return
line = lines[row]
col -= 1
while col > 0 and not (line[col].isalnum() or line[col] == "_"):
col -= 1
while col > 0 and (line[col - 1].isalnum() or line[col - 1] == "_"):
col -= 1
self.move_cursor((row, col))
def _move_word_end(self) -> None:
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if row >= len(lines):
return
line = lines[row]
if col + 1 < len(line):
col += 1
while col < len(line) and not (line[col].isalnum() or line[col] == "_"):
col += 1
while col + 1 < len(line) and (line[col + 1].isalnum() or line[col + 1] == "_"):
col += 1
self.move_cursor((row, min(col, max(len(line) - 1, 0))))
def _do_find_char(self, ch: str, forward: bool) -> None:
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if row >= len(lines):
return
line = lines[row]
idx = line.find(ch, col + 1) if forward else line.rfind(ch, 0, col)
if idx != -1:
self.move_cursor((row, idx))
def _do_replace_char(self, ch: str) -> None:
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if row >= len(lines) or col >= len(lines[row]):
return
self.read_only = False
try:
self.delete((row, col), (row, col + 1))
self.insert(ch, location=(row, col))
self.move_cursor((row, col))
finally:
self.read_only = True
def _delete_word_forward(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if row >= len(lines):
return
line = lines[row]
end = col
while end < len(line) and (line[end].isalnum() or line[end] == "_"):
end += 1
if end == col:
while end < len(line) and not (line[end].isalnum() or line[end] == "_"):
end += 1
_vim_clipboard = line[col:end]
_vim_clipboard_linewise = False
self.read_only = False
try:
self.delete((row, col), (row, end))
finally:
self.read_only = True
def _yank_word_forward(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
row, col = self.cursor_location
lines = self.text.splitlines() or [""]
if row >= len(lines):
return
line = lines[row]
end = col
while end < len(line) and (line[end].isalnum() or line[end] == "_"):
end += 1
_vim_clipboard = line[col:end]
_vim_clipboard_linewise = False
def _replay_last_action(self, outer: "VimEditor") -> None:
act = outer._last_action
if not act:
return
if act[0] == "dd":
self._delete_line()
elif act[0] == "x":
self.read_only = False
self.action_delete_right()
self.read_only = True
elif act[0] == "r":
self._do_replace_char(act[1])
elif act[0] == "dw":
self._delete_word_forward()
elif act[0] == "cw":
self._delete_word_forward()
outer.set_mode(VimMode.INSERT)
def _open_line_below(self) -> None:
self.action_cursor_line_end()
self.read_only = False
try:
self.insert("\n")
finally:
self.read_only = True
def _open_line_above(self) -> None:
row, _ = self.cursor_location
self.read_only = False
try:
if row == 0:
self.insert("\n", location=(0, 0))
self.move_cursor((0, 0))
else:
self.insert("\n", location=(row, 0))
self.move_cursor((row, 0))
finally:
self.read_only = True
def _delete_line(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
row, _ = self.cursor_location
lines = self.text.splitlines(keepends=True)
if row >= len(lines):
return
_vim_clipboard = lines[row].rstrip("\n\r")
_vim_clipboard_linewise = True
self.read_only = False
try:
line_start = (row, 0)
if row + 1 < len(lines):
line_end = (row + 1, 0)
else:
raw = lines[row].rstrip("\n\r")
line_end = (row, len(raw))
self.delete(line_start, line_end)
finally:
self.read_only = True
def _yank_line(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
row, _ = self.cursor_location
lines = self.text.splitlines()
if row >= len(lines):
return
_vim_clipboard = lines[row]
_vim_clipboard_linewise = True
def _delete_line_content(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
row, _ = self.cursor_location
lines = self.text.splitlines()
if row >= len(lines):
return
_vim_clipboard = lines[row]
_vim_clipboard_linewise = False
self.read_only = False
try:
self.delete((row, 0), (row, len(lines[row])))
finally:
self.read_only = True
def _delete_to_line_end(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
row, col = self.cursor_location
lines = self.text.splitlines()
if row >= len(lines):
return
line = lines[row]
_vim_clipboard = line[col:]
_vim_clipboard_linewise = False
self.read_only = False
try:
self.delete((row, col), (row, len(line)))
finally:
self.read_only = True
def _paste_after(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
if not _vim_clipboard:
return
row, col = self.cursor_location
self.read_only = False
try:
if _vim_clipboard_linewise:
lines = self.text.splitlines(keepends=True)
pos = (row + 1, 0) if row < len(lines) else (row, col)
self.insert(_vim_clipboard + "\n", location=pos)
self.move_cursor((row + 1, 0))
else:
all_lines = self.text.splitlines() or [""]
line = all_lines[row] if row < len(all_lines) else ""
insert_col = min(col + 1, len(line))
self.insert(_vim_clipboard, location=(row, insert_col))
finally:
self.read_only = True
def _paste_before(self) -> None:
global _vim_clipboard, _vim_clipboard_linewise
if not _vim_clipboard:
return
row, col = self.cursor_location
self.read_only = False
try:
if _vim_clipboard_linewise:
self.insert(_vim_clipboard + "\n", location=(row, 0))
self.move_cursor((row, 0))
else:
self.insert(_vim_clipboard, location=(row, col))
finally:
self.read_only = True
def _update_visual_selection(self, anchor: tuple[int, int], mode: VimMode) -> None:
from textual.widgets.text_area import Selection
current = self.cursor_location
if mode == VimMode.VISUAL_LINE:
start_row = min(anchor[0], current[0])
end_row = max(anchor[0], current[0])
lines = self.text.splitlines() or [""]
end_col = len(lines[end_row]) if end_row < len(lines) else 0
self.selection = Selection((start_row, 0), (end_row, end_col))
else:
if anchor <= current:
end_col = current[1] + 1
all_lines = self.text.splitlines() or [""]
line = all_lines[current[0]] if current[0] < len(all_lines) else ""
end_col = min(end_col, len(line))
self.selection = Selection(anchor, (current[0], end_col))
else:
end_col = anchor[1] + 1
all_lines = self.text.splitlines() or [""]
line = all_lines[anchor[0]] if anchor[0] < len(all_lines) else ""
end_col = min(end_col, len(line))
self.selection = Selection(current, (anchor[0], end_col))
def _yank_selection(self, mode: VimMode) -> None:
global _vim_clipboard, _vim_clipboard_linewise
sel = self.selection
start, end = sel.start, sel.end
if start > end:
start, end = end, start
lines = self.text.splitlines() or [""]
if start[0] == end[0]:
line = lines[start[0]] if start[0] < len(lines) else ""
_vim_clipboard = line[start[1]:end[1]]
_vim_clipboard_linewise = False
else:
parts = []
for r in range(start[0], end[0] + 1):
if r >= len(lines):
break
line = lines[r]
if r == start[0]:
parts.append(line[start[1]:])
elif r == end[0]:
parts.append(line[:end[1]])
else:
parts.append(line)
_vim_clipboard = "\n".join(parts)
_vim_clipboard_linewise = mode == VimMode.VISUAL_LINE
try:
copy_text_to_clipboard(self.app, _vim_clipboard)
except Exception:
pass
def _delete_selection(self) -> None:
self._yank_selection(VimMode.VISUAL)
sel = self.selection
start, end = sel.start, sel.end
if start > end:
start, end = end, start
if start == end:
return
self.read_only = False
try:
self.delete(start, end)
finally:
self.read_only = True

40
src/vimkit/clipboard.py Normal file
View File

@@ -0,0 +1,40 @@
"""Clipboard helpers for VimKit."""
from __future__ import annotations
import shutil
import subprocess
from textual.app import App
def copy_text_to_clipboard(app: App, text: str) -> bool:
"""Copy text through Textual OSC52 and local clipboard fallbacks."""
try:
app.copy_to_clipboard(text)
except Exception:
pass
copied_somewhere = getattr(app, "_clipboard", None) == text
commands = (
("wl-copy",),
("xclip", "-selection", "clipboard"),
("xsel", "--clipboard", "--input"),
("pbcopy",),
)
for cmd in commands:
if shutil.which(cmd[0]) is None:
continue
try:
proc = subprocess.run(
list(cmd),
input=text,
text=True,
capture_output=True,
timeout=2,
)
if proc.returncode == 0:
return True
except Exception:
continue
return copied_somewhere

View File

@@ -0,0 +1,187 @@
"""Command execution helpers for VimEditor."""
from __future__ import annotations
import shlex
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from vimkit.editor import VimEditor
def resolve_editor_path(editor: "VimEditor", path: str) -> Path:
candidate = Path(path).expanduser()
return candidate if candidate.is_absolute() else editor._cwd / candidate
def run_command(editor: "VimEditor", cmd: str) -> None:
if cmd.startswith("!"):
run_passthrough_command(editor, cmd[1:].strip())
return
if cmd.startswith("e "):
target = resolve_editor_path(editor, cmd[2:].strip())
if target.exists():
try:
editor.load_text(target.read_text(encoding="utf-8"))
editor.set_buffer_path(target)
editor._cwd = target.parent
editor.app.notify(f"Loaded {target.name}", timeout=2)
except Exception as exc:
editor.app.notify(str(exc), severity="error", timeout=3)
else:
editor.load_text("")
editor.set_buffer_path(target)
editor.app.notify(f"New buffer → {target}", severity="information", timeout=3)
return
if cmd == "w":
if editor.current_path is None:
editor.app.notify(":w requires a filename or existing buffer path", severity="warning", timeout=4)
else:
editor._write_to_path(editor.current_path)
return
if cmd.startswith("w "):
path_arg = cmd[2:].strip()
if not path_arg:
editor.app.notify(":w requires a filename", severity="warning", timeout=4)
else:
editor._write_to_path(resolve_editor_path(editor, path_arg))
return
if cmd.startswith("cd "):
path = cmd[3:].strip()
named = editor.host.named_directory(path) if editor.host is not None else None
if named is not None:
editor._cwd = named
editor.app.notify(f"cwd → {editor._cwd}", timeout=2)
else:
target = resolve_editor_path(editor, path) if path else Path.home()
try:
editor._cwd = target.resolve()
editor.app.notify(f"cwd → {editor._cwd}", timeout=2)
except Exception as exc:
editor.app.notify(str(exc), severity="error", timeout=3)
return
if cmd == "cd":
editor._cwd = Path.home()
editor.app.notify("cwd → home (hint: :cd projects | :cd notebooks)", timeout=4)
return
if cmd in ("q", "q!"):
editor.post_message(editor.QuitRequested(editor, force=(cmd == "q!")))
return
if cmd.startswith("wq "):
path_arg = cmd[3:].strip()
if not path_arg:
editor.app.notify(":wq requires a filename", severity="warning", timeout=4)
return
try:
editor._write_to_path(resolve_editor_path(editor, path_arg))
editor.post_message(editor.QuitRequested(editor, force=False))
except Exception as exc:
editor.app.notify(f"Write failed: {exc}", severity="error", timeout=6)
return
if cmd == "wq":
if editor.current_path is not None:
try:
editor._write_to_path(editor.current_path)
except Exception as exc:
editor.app.notify(f"Write failed: {exc}", severity="error", timeout=6)
return
editor.post_message(editor.QuitRequested(editor, force=False))
return
for extension in editor.extensions:
if extension.handles(editor, cmd):
return
if cmd == "help":
try:
from vimkit.panels import WhichKeyPanel
editor.app.query_one(WhichKeyPanel).show()
except Exception:
pass
return
editor.app.notify(f"Unknown command: :{cmd}", severity="warning", timeout=4)
def run_passthrough_command(editor: "VimEditor", raw: str) -> None:
if not raw:
editor.app.notify(":! requires a command", severity="warning", timeout=3)
return
try:
parts = shlex.split(raw)
except ValueError as exc:
editor.app.notify(f"Parse failed: {exc}", severity="error", timeout=4)
return
if not parts:
editor.app.notify(":! requires a command", severity="warning", timeout=3)
return
cmd, *args = parts
if cmd == "cd":
target_arg = args[0] if args else ""
run_command(editor, f"cd {target_arg}".rstrip())
return
if cmd == "mkdir":
parents = False
paths: list[str] = []
for arg in args:
if arg == "-p":
parents = True
elif arg.startswith("-"):
editor.app.notify(f"Unsupported mkdir option: {arg}", severity="warning", timeout=4)
return
else:
paths.append(arg)
if not paths:
editor.app.notify(":!mkdir requires a path", severity="warning", timeout=3)
return
created: list[str] = []
for path_arg in paths:
target = resolve_editor_path(editor, path_arg)
target.mkdir(parents=parents, exist_ok=parents)
created.append(str(target))
editor.app.notify(f"mkdir → {', '.join(created[:3])}", severity="information", timeout=4)
return
if cmd == "touch":
if not args:
editor.app.notify(":!touch requires a path", severity="warning", timeout=3)
return
touched: list[str] = []
for path_arg in args:
target = resolve_editor_path(editor, path_arg)
target.parent.mkdir(parents=True, exist_ok=True)
target.touch(exist_ok=True)
touched.append(str(target))
editor.app.notify(f"touch → {', '.join(touched[:3])}", severity="information", timeout=4)
return
if cmd == "ls":
target = resolve_editor_path(editor, args[0]) if args else editor._cwd
if not target.exists():
editor.app.notify(f"No such path: {target}", severity="error", timeout=4)
return
base = target if target.is_dir() else target.parent
entries = sorted(base.iterdir(), key=lambda path: (not path.is_dir(), path.name.lower()))
lines = [f"{path.name}/" if path.is_dir() else path.name for path in entries[:200]]
body = "\n".join(lines) if lines else "(empty)"
from vimkit.modals import TextModal
editor.app.push_screen(TextModal(f":!ls {base}", body))
return
editor.app.notify(
"Supported passthrough commands: :!cd, :!mkdir, :!touch, :!ls",
severity="warning",
timeout=5,
)

51
src/vimkit/commands.py Normal file
View File

@@ -0,0 +1,51 @@
"""Reusable command and motion metadata for VimEditor surfaces."""
from __future__ import annotations
COMMANDS: dict[str, str] = {
"e": "load file into prompt",
"w": "save prompt to file",
"wq": "write then quit, requires filename",
"q": "quit FleetCommand",
"q!": "quit FleetCommand",
"cd": "change command working directory",
"new": "start a new chat session",
"model": "open orchestrator model picker",
"chat": "show chat view",
"agents": "show agents view",
"panel": "toggle logs/system panel",
"clear": "clear current chat",
"manifest": "open memory manifest screen",
"notes": "open Obsidian notes browser",
"note": "fuzzy-find and open an Obsidian note",
"projectnote": "open the dedicated Obsidian note for this project",
"OrderTemplate": "create a seeded navigator task plan in .fleet/plans",
"engineer": "open engineer monitor screen",
"health": "run project health checks",
"theme": "pick UI colour theme",
"help": "show command and motion reference",
}
NORMAL_MOTIONS: dict[str, str] = {
"h/j/k/l": "cursor left/down/up/right",
"w/b/e": "word forward/backward/end",
"0/$": "line start/end",
"gg/G": "doc start/end",
"i/a/A": "insert at/after/line-end",
"I/o/O": "insert line-start/below/above",
"dd/yy/cc": "delete/yank/change line",
"dw/cw/yw": "delete/change/yank word",
"ciw/diw": "change/delete inner word",
"D/C": "delete/change to line-end",
"p/P": "paste after/before",
"f<c>/F<c>": "find char forward/backward",
"r<c>": "replace char under cursor",
"3j/5w": "move N lines/words (count prefix)",
";/,": "repeat last find fwd/bwd",
".": "repeat last change",
"u/ctrl+r": "undo/redo",
"v/V": "visual char/line mode",
"Space": "open WhichKey reference panel",
":": "open command bar",
}

67
src/vimkit/completion.py Normal file
View File

@@ -0,0 +1,67 @@
"""Command completion helpers for VimEditor."""
from __future__ import annotations
from pathlib import Path
from vimkit.commands import COMMANDS
def list_path_matches(cwd: Path, partial: str) -> list[str]:
try:
if "/" in partial:
dir_part, name_part = partial.rsplit("/", 1)
base = (cwd / dir_part).expanduser().resolve()
prefix = dir_part + "/"
lower = name_part.lower()
else:
base = cwd
prefix = ""
lower = partial.lower()
if not base.is_dir():
return []
return sorted(
prefix + path.name + ("/" if path.is_dir() else "")
for path in base.iterdir()
if lower in path.name.lower()
)[:20]
except Exception:
return []
def compute_command_completions(cmd_buf: str, cwd: Path, commands: dict[str, str] | None = None) -> list[str]:
commands = commands or COMMANDS
stripped = cmd_buf.lstrip()
if not stripped or " " not in stripped:
lower = stripped.lower()
return [
cmd + (" " if cmd in ("e", "w", "wq", "cd") else "")
for cmd in commands
if lower in cmd.lower()
]
if stripped.startswith("e ") or stripped.startswith("w ") or stripped.startswith("wq "):
partial = cmd_buf[2:]
if stripped.startswith("wq "):
partial = cmd_buf[3:]
return list_path_matches(cwd, partial)
if stripped.startswith("cd "):
partial = cmd_buf[3:]
named = [name for name in ("projects", "notebooks") if partial == "" or name.startswith(partial.lower())]
file_matches = list_path_matches(cwd, partial) if partial else []
return named + [match for match in file_matches if match not in named]
return []
def apply_completion_text(cmd_buf: str, completions: list[str], index: int) -> str:
if not completions:
return cmd_buf
stripped = cmd_buf.lstrip()
selected = completions[index % len(completions)]
if " " not in stripped:
return selected
for prefix in ("e ", "w ", "wq ", "cd "):
if stripped.startswith(prefix):
return prefix + selected
return cmd_buf

486
src/vimkit/editor.py Normal file
View File

@@ -0,0 +1,486 @@
"""Core vim-modal editor widget for VimKit."""
from __future__ import annotations
from pathlib import Path
from textual.binding import Binding
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Label
from vimkit.area import VimTextArea
from vimkit.command_executor import run_command, run_passthrough_command
from vimkit.completion import apply_completion_text, compute_command_completions
from vimkit.commands import COMMANDS
from vimkit.extensions import VimExtension
from vimkit.host import VimHost
from vimkit.messages import BufferPathChanged, ModeChanged, QuitRequested, Submitted
from vimkit.modes import VimMode
from vimkit.panels import CommandBar
from vimkit.state import VimSessionState
from vimkit.theme import register_cyberdream
class VimEditor(Widget):
"""Unified vim modal editor — used for chat input, notes, and anywhere else."""
BINDINGS = [
Binding("enter", "handle_enter", "Enter", show=False, priority=True),
Binding("ctrl+m", "handle_enter", "Enter", show=False, priority=True),
]
Submitted = Submitted
ModeChanged = ModeChanged
QuitRequested = QuitRequested
BufferPathChanged = BufferPathChanged
_Area = VimTextArea
mode: reactive[VimMode] = reactive(VimMode.INSERT)
DEFAULT_CSS = """
VimEditor {
width: 1fr;
height: auto;
min-height: 1;
max-height: 5;
background: $surface;
}
VimEditor TextArea {
background: transparent;
border: none;
height: auto;
min-height: 1;
padding: 0 1;
}
VimEditor TextArea:focus {
border: none;
}
VimEditor #comp-bar {
display: none;
}
VimEditor #cmd-line {
display: none;
}
VimEditor.busy {
opacity: 0.6;
}
"""
def __init__(
self,
notes_mode: bool = False,
extensions: list[VimExtension] | None = None,
host: VimHost | None = None,
**kwargs,
) -> None:
super().__init__(**kwargs)
self._state = VimSessionState(notes_mode=notes_mode, extensions=list(extensions or []))
self._saved_text = ""
self._host = host
@property
def host(self) -> VimHost | None:
return self._host
@property
def _cmd_buf(self) -> str:
return self._state.cmd_buf
@_cmd_buf.setter
def _cmd_buf(self, value: str) -> None:
self._state.cmd_buf = value
@property
def _op_buf(self) -> str:
return self._state.op_buf
@_op_buf.setter
def _op_buf(self, value: str) -> None:
self._state.op_buf = value
@property
def _rows(self) -> int:
return self._state.rows
@_rows.setter
def _rows(self, value: int) -> None:
self._state.rows = value
@property
def _cwd(self) -> Path:
return self._state.cwd
@_cwd.setter
def _cwd(self, value: Path) -> None:
self._state.cwd = value
@property
def _completions(self) -> list[str]:
return self._state.completions
@_completions.setter
def _completions(self, value: list[str]) -> None:
self._state.completions = value
@property
def _comp_idx(self) -> int:
return self._state.comp_idx
@_comp_idx.setter
def _comp_idx(self, value: int) -> None:
self._state.comp_idx = value
@property
def _notes_mode(self) -> bool:
return self._state.notes_mode
@property
def _count_buf(self) -> str:
return self._state.count_buf
@_count_buf.setter
def _count_buf(self, value: str) -> None:
self._state.count_buf = value
@property
def _find_char_pending(self) -> str | None:
return self._state.find_char_pending
@_find_char_pending.setter
def _find_char_pending(self, value: str | None) -> None:
self._state.find_char_pending = value
@property
def _last_find(self) -> tuple[str, bool] | None:
return self._state.last_find
@_last_find.setter
def _last_find(self, value: tuple[str, bool] | None) -> None:
self._state.last_find = value
@property
def _replace_pending(self) -> bool:
return self._state.replace_pending
@_replace_pending.setter
def _replace_pending(self, value: bool) -> None:
self._state.replace_pending = value
@property
def _last_action(self) -> tuple | None:
return self._state.last_action
@_last_action.setter
def _last_action(self, value: tuple | None) -> None:
self._state.last_action = value
@property
def _cmd_history(self) -> list[str]:
return self._state.cmd_history
@property
def _cmd_hist_idx(self) -> int:
return self._state.cmd_hist_idx
@_cmd_hist_idx.setter
def _cmd_hist_idx(self, value: int) -> None:
self._state.cmd_hist_idx = value
@property
def _buffer_path(self) -> Path | None:
return self._state.buffer_path
@_buffer_path.setter
def _buffer_path(self, value: Path | None) -> None:
self._state.buffer_path = value
def compose(self):
if self._notes_mode:
yield VimTextArea(
"",
language="markdown",
theme="vscode_dark",
show_line_numbers=True,
tab_behavior="indent",
soft_wrap=True,
highlight_cursor_line=True,
)
else:
yield VimTextArea(
"",
language="markdown",
theme="vscode_dark",
show_line_numbers=False,
tab_behavior="focus",
soft_wrap=True,
highlight_cursor_line=False,
)
yield Label("", id="comp-bar", markup=False)
yield Label("", id="cmd-line", markup=False)
def watch_mode(self, mode: VimMode) -> None:
try:
self.query_one(VimTextArea).read_only = (mode not in (VimMode.INSERT,))
except Exception:
pass
self.post_message(VimEditor.ModeChanged(self, mode))
def set_mode(self, mode: VimMode) -> None:
self.mode = mode
def action_handle_enter(self) -> None:
try:
area = self.query_one(VimTextArea)
except Exception:
return
if self.mode == VimMode.INSERT:
area.action_newline()
elif self.mode == VimMode.NORMAL:
area.action_submit_chat()
elif self.mode == VimMode.COMMAND:
self._end_command(execute=True)
def _start_command(self) -> None:
self._cmd_buf = ""
self.mode = VimMode.COMMAND
command_bar = self._command_bar()
if command_bar is not None:
try:
command_bar.show_command("")
except Exception:
pass
def _refresh_cmd(self) -> None:
self._recompute_completions()
self._update_command_bar()
def _recompute_completions(self) -> None:
self._completions = compute_command_completions(self._cmd_buf, self._cwd, self.command_descriptions)
self._comp_idx = 0
def _update_command_bar(self) -> None:
command_bar = self._command_bar()
if command_bar is None:
return
try:
command_bar.show_command(self._cmd_buf, self._completions, self._comp_idx)
except Exception:
pass
def _apply_completion(self) -> None:
if not self._completions:
return
self._cmd_buf = apply_completion_text(self._cmd_buf, self._completions, self._comp_idx)
if self._cmd_buf.endswith("/"):
self._recompute_completions()
command_bar = self._command_bar()
if command_bar is not None:
try:
command_bar.update_command(self._cmd_buf)
except Exception:
pass
self._update_command_bar()
def _end_command(self, execute: bool) -> None:
cmd = self._cmd_buf.strip()
if execute:
self._state.remember_command(cmd)
self._state.reset_command_mode()
command_bar = self._command_bar()
if command_bar is not None:
try:
command_bar.hide_command()
except Exception:
pass
self.mode = VimMode.NORMAL
if execute and cmd:
self._run_cmd(cmd)
def _run_cmd(self, cmd: str) -> None:
run_command(self, cmd)
@property
def extensions(self) -> list[VimExtension]:
return self._state.extensions
@property
def command_descriptions(self) -> dict[str, str]:
merged = dict(COMMANDS)
for extension in self.extensions:
merged.update(extension.commands)
return merged
def _command_bar(self) -> CommandBar | None:
parent = self.parent
while parent is not None:
try:
return parent.query_one(CommandBar)
except Exception:
parent = parent.parent
try:
return self.app.query_one(CommandBar)
except Exception:
return None
def on_mount(self) -> None:
self._apply_rows()
self._apply_cyberdream_ta()
def _apply_cyberdream_ta(self) -> None:
register_cyberdream(self.app)
ta_theme = getattr(register_cyberdream, "_ta_theme", None)
if ta_theme is None:
return
try:
area = self.query_one(VimTextArea)
area.register_theme(ta_theme)
area.theme = "cyberdream"
except Exception:
pass
def _apply_rows(self) -> None:
if self._notes_mode:
self.styles.min_height = 3
self.styles.height = "1fr"
try:
self.query_one(VimTextArea).styles.height = "1fr"
except Exception:
pass
else:
self.styles.min_height = self._rows
self.styles.max_height = self._rows
try:
self.query_one(VimTextArea).styles.height = self._rows
except Exception:
pass
@property
def rows(self) -> int:
return self._rows
def set_rows(self, rows: int) -> None:
self._rows = max(1, min(int(rows), 20))
self._apply_rows()
def grow(self) -> None:
self.set_rows(self._rows + 1)
def shrink(self) -> None:
self.set_rows(self._rows - 1)
def toggle_pin(self) -> bool:
pinned = not self._state.pinned
self._state.pinned = pinned
if pinned:
self.styles.min_height = 1
self.styles.max_height = 1
try:
self.query_one(VimTextArea).styles.height = 1
except Exception:
pass
else:
self._apply_rows()
return pinned
@property
def text(self) -> str:
try:
return self.query_one(VimTextArea).text
except Exception:
return ""
@property
def cursor_location(self) -> tuple[int, int]:
try:
return self.query_one(VimTextArea).cursor_location
except Exception:
return (0, 0)
@property
def is_modified(self) -> bool:
return self.text != self._saved_text
@property
def cwd(self) -> Path:
return self._cwd
@property
def current_path(self) -> Path | None:
return self._buffer_path
def set_buffer_path(self, path: Path | None) -> None:
self._buffer_path = path.resolve() if isinstance(path, Path) and path.exists() else path
try:
self.post_message(self.BufferPathChanged(self, self._buffer_path))
except Exception:
pass
def load_text(self, s: str) -> None:
try:
area = self.query_one(VimTextArea)
was_read_only = area.read_only
area.read_only = False
try:
area.load_text(s)
area.move_cursor((0, 0), center=True)
finally:
area.read_only = was_read_only
except Exception:
pass
self._saved_text = s
self.set_buffer_path(None)
def insert_text(self, s: str) -> None:
try:
area = self.query_one(VimTextArea)
was_read_only = area.read_only
area.read_only = False
try:
area.insert(s, location=area.cursor_location)
finally:
area.read_only = was_read_only
except Exception:
pass
def _write_to_path(self, path: Path) -> None:
content = self.text
target = path.expanduser()
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(content, encoding="utf-8")
self._saved_text = content
self.set_buffer_path(target.resolve())
self._cwd = target.parent.resolve()
self.app.notify(f"Saved {len(content)} chars → {target}", severity="information", timeout=4)
def _run_passthrough_command(self, raw: str) -> None:
run_passthrough_command(self, raw)
@property
def disabled(self) -> bool:
return "busy" in self.classes
@disabled.setter
def disabled(self, value: bool) -> None:
try:
self.query_one(VimTextArea).disabled = value
except Exception:
pass
if value:
self.add_class("busy")
else:
self.remove_class("busy")
def focus(self, scroll_visible: bool = True) -> "VimEditor":
try:
self.query_one(VimTextArea).focus(scroll_visible)
except Exception:
pass
return self
def blur(self) -> "VimEditor":
try:
self.query_one(VimTextArea).blur()
except Exception:
pass
return self

25
src/vimkit/extensions.py Normal file
View File

@@ -0,0 +1,25 @@
"""Command extension primitives for VimEditor."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
from vimkit.editor import VimEditor
CommandHandler = Callable[["VimEditor", str], bool]
@dataclass(slots=True)
class VimExtension:
"""Named bundle of commands that can be attached to a VimEditor instance."""
name: str
commands: dict[str, str] = field(default_factory=dict)
handler: CommandHandler | None = None
def handles(self, editor: "VimEditor", cmd: str) -> bool:
if self.handler is None:
return False
return self.handler(editor, cmd)

37
src/vimkit/host.py Normal file
View File

@@ -0,0 +1,37 @@
"""Host adapter protocol for vimkit integrations."""
from __future__ import annotations
from pathlib import Path
from typing import Protocol
class VimHost(Protocol):
"""Optional host API that lets applications provide app-specific actions."""
def named_directory(self, name: str) -> Path | None: ...
def action_new_chat(self) -> None: ...
def action_pick_model(self) -> None: ...
def action_show_chat(self) -> None: ...
def action_toggle_log(self) -> None: ...
def action_clear_chat(self) -> None: ...
def action_open_manifest(self) -> None: ...
def action_open_notes(self) -> None: ...
def action_pick_note(self) -> None: ...
def action_open_project_note(self) -> None: ...
def action_open_order_template(self) -> None: ...
def action_open_engineer(self) -> None: ...
def action_open_health(self) -> None: ...
def action_pick_theme(self) -> None: ...

50
src/vimkit/messages.py Normal file
View File

@@ -0,0 +1,50 @@
"""Public message types emitted by VimEditor widgets."""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
from textual.message import Message
from vimkit.modes import VimMode
if TYPE_CHECKING:
from vimkit.editor import VimEditor
class VimWidgetMessage(Message):
"""Base message that exposes the originating VimEditor control."""
def __init__(self, sender: "VimEditor") -> None:
super().__init__()
self._sender = sender
@property
def control(self) -> "VimEditor":
return self._sender
class Submitted(VimWidgetMessage):
def __init__(self, sender: "VimEditor", value: str) -> None:
super().__init__(sender)
self.value = value
class ModeChanged(VimWidgetMessage):
def __init__(self, sender: "VimEditor", mode: VimMode) -> None:
super().__init__(sender)
self.mode = mode
class QuitRequested(VimWidgetMessage):
"""Emitted when :q or :q! is invoked. Parent decides whether to quit or dismiss."""
def __init__(self, sender: "VimEditor", force: bool = False) -> None:
super().__init__(sender)
self.force = force
class BufferPathChanged(VimWidgetMessage):
def __init__(self, sender: "VimEditor", path: Path | None) -> None:
super().__init__(sender)
self.path = path

61
src/vimkit/modals.py Normal file
View File

@@ -0,0 +1,61 @@
"""Generic modal surfaces owned by the reusable VimKit package."""
from __future__ import annotations
from textual import on
from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Vertical
from textual.screen import ModalScreen
from textual.widgets import Button, RichLog
class TextModal(ModalScreen[None]):
"""Simple read-only text viewer for builtin Vim commands like :!ls."""
CSS = """
TextModal {
align: center middle;
}
TextModal #tm-dialog {
width: 100;
height: auto;
max-height: 30;
background: $surface;
border: thick $primary;
padding: 1 2;
}
TextModal RichLog {
height: 1fr;
background: transparent;
border: solid $panel;
padding: 0 1;
}
TextModal Button {
width: 10;
margin-top: 1;
align-horizontal: right;
}
"""
BINDINGS = [Binding("escape", "close", "Close"), Binding("q", "close", "Close", show=False)]
def __init__(self, title: str, body: str) -> None:
super().__init__()
self._title = title
self._body = body
def compose(self) -> ComposeResult:
with Vertical(id="tm-dialog"):
yield RichLog(id="tm-log", highlight=False, markup=False, wrap=False)
yield Button("Close", id="tm-close")
def on_mount(self) -> None:
self.sub_title = self._title
self.query_one("#tm-log", RichLog).write(self._body or "(empty)")
@on(Button.Pressed, "#tm-close")
def _close_pressed(self, _: Button.Pressed) -> None:
self.dismiss(None)
def action_close(self) -> None:
self.dismiss(None)

12
src/vimkit/modes.py Normal file
View File

@@ -0,0 +1,12 @@
"""Core mode definitions for the reusable VimEditor subsystem."""
from __future__ import annotations
from enum import Enum
class VimMode(Enum):
INSERT = "I"
NORMAL = "N"
COMMAND = "C"
VISUAL = "V"
VISUAL_LINE = "VL"

242
src/vimkit/panels.py Normal file
View File

@@ -0,0 +1,242 @@
"""Reusable presentational widgets for VimEditor surfaces."""
from __future__ import annotations
from textual.containers import Vertical
from textual.events import Key
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Input, Label, Static
from vimkit.commands import COMMANDS, NORMAL_MOTIONS
from vimkit.prompt_render import WAVE_COLORS, wave
class FleetPrompt(Widget):
"""Colour-wave animated ⬡ fleet prompt indicator."""
DEFAULT_CSS = """
FleetPrompt {
width: auto;
height: 1;
padding: 0 1;
background: transparent;
content-align: left middle;
}
FleetPrompt > Static {
background: transparent;
content-align: left middle;
height: 1;
}
"""
frame: reactive[int] = reactive(0)
def compose(self):
yield Static(wave(0), markup=True)
def on_mount(self) -> None:
self.set_interval(0.12, self._tick)
def _tick(self) -> None:
self.frame = (self.frame + 1) % len(WAVE_COLORS)
def watch_frame(self, frame: int) -> None:
self.query_one(Static).update(wave(frame))
class WhichKeyPanel(Widget):
"""Floating help/discovery panel showing commands and normal-mode motions."""
DEFAULT_CSS = """
WhichKeyPanel {
height: auto; max-height: 16; display: none;
dock: bottom; background: $surface-darken-1;
border: solid $primary; margin: 0 1 1 1;
}
WhichKeyPanel #wk-title {
height: 1; background: $surface-darken-2; color: $primary;
text-style: bold; content-align: center middle;
}
WhichKeyPanel #wk-filter {
height: 1; background: $surface-darken-2; border: none; padding: 0 1;
border-top: none !important; border-bottom: none !important;
}
WhichKeyPanel #wk-grid {
layout: grid; grid-size: 2; height: auto; overflow-y: auto;
}
WhichKeyPanel .wk-item { padding: 0 1; height: 1; }
WhichKeyPanel .wk-item.-selected { background: $boost; }
"""
def __init__(self, **kwargs: object) -> None:
super().__init__(**kwargs)
self._prev_focus: Widget | None = None
self._all_items: list[tuple[str, str]] = []
self._selected_idx: int = 0
def compose(self):
yield Label(" VimKit — Commands & Motions (Esc to close)", id="wk-title")
yield Input(placeholder="filter…", id="wk-filter")
yield Vertical(id="wk-grid")
def show(self, section: str = "all") -> None:
self._prev_focus = self.app.focused
self._all_items = []
command_map = dict(COMMANDS)
try:
from vimkit.editor import VimEditor
vim = self.app.query_one(VimEditor)
command_map = vim.command_descriptions
except Exception:
pass
if section in ("all", "commands"):
for key, desc in command_map.items():
self._all_items.append((f":{key}", desc))
if section in ("all", "motions"):
for key, desc in NORMAL_MOTIONS.items():
self._all_items.append((key, desc))
self._selected_idx = 0
self.display = True
self._rebuild_grid(self._all_items)
try:
self.query_one("#wk-filter", Input).value = ""
self.query_one("#wk-filter", Input).focus()
except Exception:
pass
def dismiss_panel(self) -> None:
self.display = False
if self._prev_focus is not None:
try:
self._prev_focus.focus()
except Exception:
pass
self._prev_focus = None
def _active_vim(self):
node = self._prev_focus or self.app.focused
while node is not None:
try:
from vimkit.editor import VimEditor
if isinstance(node, VimEditor):
return node
except Exception:
return None
node = getattr(node, "parent", None)
return None
def _rebuild_grid(self, items: list[tuple[str, str]]) -> None:
grid = self.query_one("#wk-grid", Vertical)
grid.remove_children()
for i, (key, desc) in enumerate(items):
lbl = Label(f"[bold]{key}[/] {desc}", classes="wk-item")
if i == self._selected_idx:
lbl.add_class("-selected")
grid.mount(lbl)
def _filtered_items(self) -> list[tuple[str, str]]:
try:
q = self.query_one("#wk-filter", Input).value.lower()
except Exception:
q = ""
if not q:
return self._all_items
return [(k, d) for k, d in self._all_items if q in k.lower() or q in d.lower()]
def on_input_changed(self, _: Input.Changed) -> None:
self._selected_idx = 0
self._rebuild_grid(self._filtered_items())
def on_key(self, event: Key) -> None:
k = event.key
if k in ("escape", "ctrl+c"):
self.dismiss_panel()
event.stop()
elif k in ("up", "ctrl+p", "shift+tab"):
items = self._filtered_items()
if items:
self._selected_idx = max(0, self._selected_idx - 1)
self._rebuild_grid(items)
event.stop()
event.prevent_default()
elif k in ("down", "ctrl+n", "tab"):
items = self._filtered_items()
if items:
self._selected_idx = min(len(items) - 1, self._selected_idx + 1)
self._rebuild_grid(items)
event.stop()
event.prevent_default()
elif k == "enter":
items = self._filtered_items()
if items and 0 <= self._selected_idx < len(items):
chosen_key = items[self._selected_idx][0]
if chosen_key.startswith(":"):
cmd = chosen_key[1:]
self.dismiss_panel()
try:
vim = self._active_vim()
if vim is not None:
vim._run_cmd(cmd)
except Exception:
pass
else:
self.dismiss_panel()
event.stop()
class CommandBar(Widget):
"""Vim-style command line anchored above the status bar."""
DEFAULT_CSS = """
CommandBar {
height: 2;
display: none;
background: $surface-darken-1;
}
CommandBar #cmd-completions {
height: 1;
color: $text-muted;
background: $surface-darken-1;
padding: 0 1;
}
CommandBar #cmd-input {
height: 1;
color: $accent;
background: $surface-darken-2;
padding: 0 1;
}
"""
def compose(self):
yield Label("", id="cmd-completions", markup=False)
yield Label(":", id="cmd-input", markup=False)
def show_command(self, text: str = "", completions: list[str] | None = None, index: int = 0) -> None:
self.display = True
self.update_command(text)
self.update_completions(completions or [], index)
def hide_command(self) -> None:
self.display = False
self.update_command("")
self.update_completions([], 0)
def update_command(self, text: str) -> None:
try:
self.query_one("#cmd-input", Label).update(f":{text}")
except Exception:
pass
def update_completions(self, completions: list[str], index: int = 0) -> None:
try:
lbl = self.query_one("#cmd-completions", Label)
if not completions:
lbl.update("")
return
idx = index % len(completions)
parts = [f"[{item}]" if i == idx else item for i, item in enumerate(completions[:10])]
lbl.update(" ".join(parts))
except Exception:
pass

View File

@@ -0,0 +1,12 @@
"""Rendering helpers for portable VimEditor prompt widgets."""
from __future__ import annotations
WAVE_COLORS = ["#ff6b6b", "#ffa06a", "#ffd93d", "#6bcb77", "#4d96ff", "#c77dff"]
def wave(frame: int) -> str:
glyphs = "⬡ fleet "
return "".join(
f"[bold {WAVE_COLORS[(i + frame) % len(WAVE_COLORS)]}]{ch}[/]"
for i, ch in enumerate(glyphs)
)

44
src/vimkit/state.py Normal file
View File

@@ -0,0 +1,44 @@
"""Mutable session state for VimEditor widgets."""
from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from vimkit.extensions import VimExtension
@dataclass
class VimSessionState:
"""Editor-local state separate from widget composition and rendering."""
notes_mode: bool = False
rows: int = 5
cwd: Path = field(default_factory=Path.cwd)
cmd_buf: str = ""
op_buf: str = ""
completions: list[str] = field(default_factory=list)
comp_idx: int = 0
count_buf: str = ""
find_char_pending: str | None = None
last_find: tuple[str, bool] | None = None
replace_pending: bool = False
last_action: tuple | None = None
cmd_history: list[str] = field(default_factory=list)
cmd_hist_idx: int = -1
buffer_path: Path | None = None
pinned: bool = False
extensions: list[VimExtension] = field(default_factory=list)
def remember_command(self, cmd: str, *, max_history: int = 50) -> None:
if not cmd:
return
if not self.cmd_history or self.cmd_history[-1] != cmd:
self.cmd_history.append(cmd)
if len(self.cmd_history) > max_history:
self.cmd_history.pop(0)
def reset_command_mode(self) -> None:
self.cmd_hist_idx = -1
self.cmd_buf = ""
self.completions.clear()
self.comp_idx = 0

98
src/vimkit/theme.py Normal file
View File

@@ -0,0 +1,98 @@
"""Theme registration helpers for VimKit."""
from __future__ import annotations
from textual.app import App
def register_cyberdream(app: App) -> None:
"""Register the default VimKit app theme and TextArea syntax theme."""
from rich.style import Style as _S
from textual._text_area_theme import TextAreaTheme as _TAT
from textual.theme import Theme as _Theme
cyber_app = _Theme(
name="cyberdream",
primary="#5ea1ff",
secondary="#bd5eff",
warning="#ffbd5e",
error="#ff6e5e",
success="#5eff6c",
accent="#5ef1ff",
foreground="#ffffff",
background="#16181a",
surface="#1e2124",
panel="#3c4048",
boost="#3c4048",
dark=True,
variables={
"primary-darken-1": "#4d8fe0",
"primary-darken-2": "#3b7abf",
"primary-darken-3": "#2a659f",
"primary-lighten-1": "#7ab5ff",
"primary-lighten-2": "#9ac8ff",
"surface-darken-1": "#16181a",
"surface-darken-2": "#121416",
"text-muted": "#7b8496",
},
)
app.register_theme(cyber_app)
cyber_ta = _TAT(
name="cyberdream",
base_style=_S(color="#ffffff", bgcolor="#16181a"),
gutter_style=_S(color="#7b8496", bgcolor="#1e2124"),
cursor_style=_S(color="#16181a", bgcolor="#5ea1ff"),
cursor_line_style=_S(bgcolor="#1e2124"),
cursor_line_gutter_style=_S(color="#ffffff", bgcolor="#1e2124"),
bracket_matching_style=_S(bgcolor="#3c4048", bold=True),
selection_style=_S(bgcolor="#3c4048"),
syntax_styles={
"string": _S(color="#5eff6c"),
"string.documentation": _S(color="#5eff6c"),
"comment": _S(color="#7b8496", italic=True),
"keyword": _S(color="#ff5ef1", bold=True),
"keyword.function": _S(color="#ff5ef1", bold=True),
"keyword.return": _S(color="#ff5ef1"),
"keyword.operator": _S(color="#5ef1ff"),
"operator": _S(color="#5ef1ff"),
"repeat": _S(color="#ff5ef1"),
"exception": _S(color="#ff6e5e"),
"include": _S(color="#bd5eff"),
"conditional": _S(color="#ff5ef1"),
"number": _S(color="#ffbd5e"),
"float": _S(color="#ffbd5e"),
"boolean": _S(color="#ffbd5e"),
"constant.builtin": _S(color="#ffbd5e"),
"class": _S(color="#f1ff5e", bold=True),
"type": _S(color="#f1ff5e"),
"type.class": _S(color="#f1ff5e", bold=True),
"type.builtin": _S(color="#5ef1ff"),
"function": _S(color="#5ea1ff"),
"function.call": _S(color="#5ea1ff"),
"method": _S(color="#5ea1ff"),
"method.call": _S(color="#5ea1ff"),
"variable.builtin": _S(color="#ff5ea0"),
"tag": _S(color="#5ea1ff"),
"yaml.field": _S(color="#5ef1ff"),
"json.label": _S(color="#5ef1ff"),
"json.null": _S(color="#ffbd5e"),
"toml.type": _S(color="#ff5ef1"),
"toml.datetime": _S(color="#ffbd5e"),
"css.property": _S(color="#5ea1ff"),
"regex.punctuation.bracket": _S(color="#5ef1ff"),
"regex.operator": _S(color="#ff5ea0"),
"heading": _S(color="#5ea1ff", bold=True),
"heading.marker": _S(color="#bd5eff"),
"bold": _S(bold=True),
"italic": _S(italic=True),
"strikethrough": _S(strike=True),
"link.label": _S(color="#5ea1ff"),
"link.uri": _S(color="#5ef1ff"),
"list.marker": _S(color="#bd5eff"),
"inline_code": _S(color="#5eff6c", bgcolor="#1e2124"),
"punctuation.bracket": _S(color="#7b8496"),
"punctuation.delimiter": _S(color="#7b8496"),
"punctuation.special": _S(color="#ff5ea0"),
},
)
register_cyberdream._ta_theme = cyber_ta

133
src/vimkit/workbench.py Normal file
View File

@@ -0,0 +1,133 @@
"""Composite vimkit widgets that add editor-local chrome around VimEditor."""
from __future__ import annotations
from textual import on
from textual.app import ComposeResult
from textual.containers import Vertical
from textual.widget import Widget
from textual.widgets import Static
from vimkit.editor import VimEditor
from vimkit.extensions import VimExtension
from vimkit.host import VimHost
from vimkit.messages import BufferPathChanged, ModeChanged
from vimkit.panels import CommandBar
class VimTopBar(Widget):
"""Editor-local header showing file, mode, cwd, and dirty state."""
DEFAULT_CSS = """
VimTopBar {
height: 1;
background: $surface-darken-2;
color: $text-muted;
padding: 0 1;
}
"""
def compose(self) -> ComposeResult:
yield Static("", id="vim-topbar-label")
def update_editor(self, editor: VimEditor) -> None:
path = editor.current_path.name if editor.current_path is not None else "[No Name]"
mode = editor.mode.value
dirty = " [+]" if editor.is_modified else ""
cwd = editor.cwd.name or str(editor.cwd)
self.query_one("#vim-topbar-label", Static).update(f"{mode} {path}{dirty} · {cwd}")
class VimStatusBar(Widget):
"""Editor-local status line showing cursor, command hints, and file state."""
DEFAULT_CSS = """
VimStatusBar {
height: 1;
background: $surface-darken-1;
color: $text-muted;
padding: 0 1;
}
"""
def compose(self) -> ComposeResult:
yield Static("", id="vim-statusbar-label")
def update_editor(self, editor: VimEditor) -> None:
row, col = editor.cursor_location
mode = editor.mode.name.lower()
file_label = editor.current_path.name if editor.current_path is not None else "scratch"
dirty = "modified" if editor.is_modified else "saved"
self.query_one("#vim-statusbar-label", Static).update(
f"{file_label} · {mode} · {dirty} · ln {row + 1}, col {col + 1}"
)
class VimWorkbench(Widget):
"""Composite editor surface with optional vimkit-owned chrome."""
DEFAULT_CSS = """
VimWorkbench {
width: 1fr;
height: auto;
background: $surface;
layout: vertical;
}
VimWorkbench.-full {
height: 1fr;
}
VimWorkbench.-compact {
min-height: 4;
}
"""
def __init__(
self,
*,
editor_id: str,
notes_mode: bool = False,
extensions: list[VimExtension] | None = None,
host: VimHost | None = None,
chrome: str = "compact",
**kwargs,
) -> None:
super().__init__(**kwargs)
self._editor_id = editor_id
self._notes_mode = notes_mode
self._extensions = list(extensions or [])
self._host = host
self._chrome = chrome
def compose(self) -> ComposeResult:
if self._chrome == "full":
yield VimTopBar(id="vim-topbar")
yield VimEditor(
notes_mode=self._notes_mode,
id=self._editor_id,
extensions=self._extensions,
host=self._host,
)
if self._chrome in ("full", "compact"):
yield CommandBar(id="vim-command-bar")
yield VimStatusBar(id="vim-statusbar")
def on_mount(self) -> None:
self.set_class(self._chrome == "full", "-full")
self.set_class(self._chrome == "compact", "-compact")
self.set_interval(0.15, self._refresh_chrome)
self._refresh_chrome()
@property
def editor(self) -> VimEditor:
return self.query_one(f"#{self._editor_id}", VimEditor)
def _refresh_chrome(self) -> None:
editor = self.editor
if self._chrome == "full":
self.query_one("#vim-topbar", VimTopBar).update_editor(editor)
if self._chrome in ("full", "compact"):
self.query_one("#vim-statusbar", VimStatusBar).update_editor(editor)
@on(ModeChanged)
@on(BufferPathChanged)
def _editor_changed(self, _: object) -> None:
self._refresh_chrome()

773
uv.lock generated Normal file
View File

@@ -0,0 +1,773 @@
version = 1
revision = 3
requires-python = ">=3.14"
[[package]]
name = "certifi"
version = "2026.6.17"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
]
[[package]]
name = "cffi"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" },
{ url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" },
{ url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" },
{ url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" },
{ url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" },
{ url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" },
{ url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" },
{ url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" },
{ url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" },
{ url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" },
{ url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" },
{ url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" },
{ url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" },
{ url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" },
{ url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" },
{ url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" },
{ url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" },
{ url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" },
{ url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" },
{ url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.9"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" },
{ url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" },
{ url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" },
{ url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" },
{ url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" },
{ url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" },
{ url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" },
{ url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" },
{ url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" },
{ url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" },
{ url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" },
{ url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" },
{ url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" },
{ url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" },
{ url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" },
{ url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" },
{ url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" },
{ url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" },
{ url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" },
{ url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" },
{ url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" },
{ url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" },
{ url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" },
{ url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" },
{ url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" },
{ url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" },
{ url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" },
]
[[package]]
name = "cryptography"
version = "49.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" },
{ url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" },
{ url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" },
{ url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" },
{ url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" },
{ url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" },
{ url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" },
{ url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" },
{ url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" },
{ url = "https://files.pythonhosted.org/packages/86/12/c48a424f38db03027be9f7ed5c7dc5de9933dbee992865f98b13727a009d/cryptography-49.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d", size = 4678835, upload-time = "2026-06-12T20:02:48.743Z" },
{ url = "https://files.pythonhosted.org/packages/68/28/8a3ad4653662c93fc44dc4e5d8fd374c25c42e07b34bbfbadf49cf57a5a8/cryptography-49.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa", size = 4697239, upload-time = "2026-06-12T20:02:56.03Z" },
{ url = "https://files.pythonhosted.org/packages/a8/b2/2193fc74f81aee4f9b62733133b73b5176718932ed8f2e4b03fa040480a6/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb", size = 4685593, upload-time = "2026-06-12T20:02:50.666Z" },
{ url = "https://files.pythonhosted.org/packages/58/39/2d51306721330c486495853eda1c567880ff036de15a14c4b74f399934af/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561", size = 4731145, upload-time = "2026-06-12T20:02:16.832Z" },
{ url = "https://files.pythonhosted.org/packages/17/50/983e838c7fd0d87fd8c969bcdd328edaf5f756e38df5281637424c155873/cryptography-49.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122", size = 4321719, upload-time = "2026-06-12T20:02:52.611Z" },
{ url = "https://files.pythonhosted.org/packages/a7/f5/8f571d7e27c55bce9f76f026143bcb1e040a4233149ecca0bea5fa5dd5f7/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505", size = 4685209, upload-time = "2026-06-12T20:02:07.282Z" },
{ url = "https://files.pythonhosted.org/packages/11/2d/5e1fb307cb5931881516b464c98774b3f2c36b5d4bb9a2830253cf553cad/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8", size = 4730441, upload-time = "2026-06-12T20:02:01.469Z" },
{ url = "https://files.pythonhosted.org/packages/e4/c0/bff5a02ee731d207d6a1ed51732549d8c53d2bc8da1d10ec6f2844201d68/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3", size = 4815869, upload-time = "2026-06-12T20:01:36.574Z" },
{ url = "https://files.pythonhosted.org/packages/b9/26/814681d14248d95d73d5c3eea0c39a94eb8302df966f670a2c60de90974b/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27", size = 4960948, upload-time = "2026-06-12T20:02:18.688Z" },
{ url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" },
{ url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" },
{ url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" },
{ url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" },
{ url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" },
{ url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" },
{ url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" },
{ url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" },
{ url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" },
]
[[package]]
name = "docutils"
version = "0.23"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/39/a4/5180d9afc57e8fca05601dd652bdff19604c218814037fe90ffc7625a50a/docutils-0.23.tar.gz", hash = "sha256:746f5060322511280a1e50eb76846ed6bf2342984b2ac04dc42caa1a8d78799e", size = 2303823, upload-time = "2026-05-27T17:41:06.934Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/32/91/30151a39f7570f448ed84529390628a651d7f27c87d73c9b887f8189695e/docutils-0.23-py3-none-any.whl", hash = "sha256:25d013af9bf23bc1c7b2b093dff4208166c53a94786c9e447808335ef1185fea", size = 634701, upload-time = "2026-05-27T17:40:58.442Z" },
]
[[package]]
name = "id"
version = "1.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6d/04/c2156091427636080787aac190019dc64096e56a23b7364d3c1764ee3a06/id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069", size = 18088, upload-time = "2026-02-04T16:19:41.26Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/77/de194443bf38daed9452139e960c632b0ef9f9a5dd9ce605fdf18ca9f1b1/id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca", size = 14689, upload-time = "2026-02-04T16:19:40.051Z" },
]
[[package]]
name = "idna"
version = "3.18"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
]
[[package]]
name = "jaraco-classes"
version = "3.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "more-itertools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" },
]
[[package]]
name = "jaraco-context"
version = "6.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" },
]
[[package]]
name = "jaraco-functools"
version = "4.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "more-itertools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272, upload-time = "2026-05-15T21:34:10.025Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594, upload-time = "2026-05-15T21:34:08.595Z" },
]
[[package]]
name = "jeepney"
version = "0.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" },
]
[[package]]
name = "keyring"
version = "25.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jaraco-classes" },
{ name = "jaraco-context" },
{ name = "jaraco-functools" },
{ name = "jeepney", marker = "sys_platform == 'linux'" },
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
{ name = "secretstorage", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" },
]
[[package]]
name = "linkify-it-py"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "uc-micro-py" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" },
]
[package.optional-dependencies]
linkify = [
{ name = "linkify-it-py" },
]
[[package]]
name = "mdit-py-plugins"
version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
]
sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "more-itertools"
version = "11.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772, upload-time = "2026-05-22T14:14:29.909Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" },
]
[[package]]
name = "nh3"
version = "0.3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5e/1b/ef84624f14954d270f74060a19fc550dd4f06656399447569afb584d8c06/nh3-0.3.6.tar.gz", hash = "sha256:f3736c9dd3d1856f80cd031715b84ca75cda2bbb1ac802c3da26bfce590838d7", size = 24684, upload-time = "2026-06-22T00:47:02.008Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/99/3e/6506aa4f23dc7b7993a2d0a45dca3ce864ec48380adfe15a173e643c63e8/nh3-0.3.6-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2411e8c3cee81a1ddd62c2a5d50585c28aa5566d373ad1db92536b95ddb24ef2", size = 1421679, upload-time = "2026-06-22T00:46:20.248Z" },
{ url = "https://files.pythonhosted.org/packages/e3/e1/e96e7864a7a53bd6b6fab7e9632467382a2a2c1f3fed951918ad131542fb/nh3-0.3.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e196fa70c2ff2eb4de7d3df3108f8f358c1d69dff20d45b11f20a5aa227ffb6d", size = 792570, upload-time = "2026-06-22T00:46:22.179Z" },
{ url = "https://files.pythonhosted.org/packages/59/62/5b6108bedaef2b2637fed04c87bdbcb5967b9961758b41f0e466ef22a022/nh3-0.3.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34d2b0d934156b87ee114f599a3ba9b8b9e17b5d79652ba3a13fa50903de965e", size = 842243, upload-time = "2026-06-22T00:46:23.801Z" },
{ url = "https://files.pythonhosted.org/packages/4b/4a/526f199626bfcb496bc01a268051b44737962005553b158e985ed7e64865/nh3-0.3.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2f14b7ae1fca99c4a66c981aac3974e7fbc1ca30a12673d223ae1df76680917", size = 1001468, upload-time = "2026-06-22T00:46:25.481Z" },
{ url = "https://files.pythonhosted.org/packages/49/09/0d8e3101636d9ad88cdefb2914e764cb8e876ebdbb4286bfc251277d9c67/nh3-0.3.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:889932a97fb4abb6f95fef1914c0d269ebfb60011e67121c1163059b9449dbb4", size = 1082933, upload-time = "2026-06-22T00:46:27.15Z" },
{ url = "https://files.pythonhosted.org/packages/09/a1/ea83abe738a3fbaa203dfdb836ca7cbab0e7e9609faaee4fe1d4652599c0/nh3-0.3.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edb2b4a1a27523e6cc7c417f8d21ce3d005243548b93e56b762b66b0c7f589f9", size = 1043120, upload-time = "2026-06-22T00:46:28.89Z" },
{ url = "https://files.pythonhosted.org/packages/66/69/0654482b8635012fbae67826bd6c381abb05d841ac7388b9b4666300fdad/nh3-0.3.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43bc1ed3fa0716295fabee29ba42b2667e4a51d140b0a68e092170a765474fa6", size = 1023824, upload-time = "2026-06-22T00:46:30.453Z" },
{ url = "https://files.pythonhosted.org/packages/ed/a6/1f7285ffadc8307c4dbeb08d21b920536d5117785056d1079e998c4dfa44/nh3-0.3.6-cp314-cp314t-win32.whl", hash = "sha256:597a8e843bea00b2eb5520658dc24a9bb032e7fc9e7c2c0c4cd29420220c9796", size = 599253, upload-time = "2026-06-22T00:46:32.072Z" },
{ url = "https://files.pythonhosted.org/packages/36/ea/5542f3c45da4c00290d9d67a65e996702e23e613c4b627de3e09cb9fe357/nh3-0.3.6-cp314-cp314t-win_amd64.whl", hash = "sha256:4713502748f564fee0633b37b3403783ce0a3af3a3d148ad91025a5bdadb7bc6", size = 612553, upload-time = "2026-06-22T00:46:33.53Z" },
{ url = "https://files.pythonhosted.org/packages/66/35/26bd47e6af5915a628281dccdac354ddf4e32f7397047894270acd8c9870/nh3-0.3.6-cp314-cp314t-win_arm64.whl", hash = "sha256:69bbb92865a693d909db3a700d3c01537533844d0948c1e9323561ce06ecda41", size = 595151, upload-time = "2026-06-22T00:46:34.878Z" },
{ url = "https://files.pythonhosted.org/packages/f3/ab/a7653bce9a3b204be6a6931767a9e23595807bb84790ce6685e4d7e5bd08/nh3-0.3.6-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a43ebd7543555c3ac1bc353023d0794e75cb76f6f18f19c32e95441496c0cc25", size = 1443564, upload-time = "2026-06-22T00:46:36.66Z" },
{ url = "https://files.pythonhosted.org/packages/41/21/e1084ab18eb589506335c7c7576f2d4643e9a0c0e33983ef0e549a256b96/nh3-0.3.6-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1b160831c9cdb06a6c79c2f9cdb11386602938f9af260d1c457a85add4f6f69", size = 838002, upload-time = "2026-06-22T00:46:38.101Z" },
{ url = "https://files.pythonhosted.org/packages/b0/94/f48d08e6f72a406300fa11d8acd929fea1a80d4bf750fa292cb10785f126/nh3-0.3.6-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d14bf7982e7a77c0c775634c29c07ce08b38a046df73e1c1f139b3e82f18a38e", size = 823045, upload-time = "2026-06-22T00:46:39.495Z" },
{ url = "https://files.pythonhosted.org/packages/25/bb/431615ba1d1d3eb63cde0f974f2114edf863a8a3f6049a12fed23fc241d3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:44673b27010051ab5a5e438a86ec31bbda61d4a77d7e900af6b7be3037c1abae", size = 1093171, upload-time = "2026-06-22T00:46:41.21Z" },
{ url = "https://files.pythonhosted.org/packages/0e/24/a0d80182a18919665fefd19c1c06f1d1df1c9a6455d0252de40c034a0bc3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6b7beece07525dc6e6b0fc2f104442de2ba328360ad00e50cbe2e1fd620447d", size = 1049217, upload-time = "2026-06-22T00:46:42.804Z" },
{ url = "https://files.pythonhosted.org/packages/0a/13/6f1e302ca674ac74362e150848ad56a1be5145391204f74facdb8e94df12/nh3-0.3.6-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:455469a29951edc92bc48b47ac2281c3f2609e6c4f6a047056449f8c2c23facf", size = 917372, upload-time = "2026-06-22T00:46:44.495Z" },
{ url = "https://files.pythonhosted.org/packages/5b/67/314f6151bad77a93d751978a344033e1fc890822f05f0416079338e34231/nh3-0.3.6-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:905f877dc66dd7aea4a76e54bcb26acb5ff8216f720c0017ccf63e0e6035698e", size = 806699, upload-time = "2026-06-22T00:46:45.99Z" },
{ url = "https://files.pythonhosted.org/packages/3c/a6/bfaa00046e58603507dcfc266c4778e3ab7adf68a5dedd73b6274b8d9314/nh3-0.3.6-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:25c733bee928530556b1db0ea46c52cf5aa686146e38e60a6fc7cb801ef91cec", size = 835165, upload-time = "2026-06-22T00:46:47.617Z" },
{ url = "https://files.pythonhosted.org/packages/30/a8/fb2c38845efb703a9173bffdfc745fc64d2b0e55cfc73a3647d2f028250c/nh3-0.3.6-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f90d9a0cfdbee218994fdaaeeb5a0fde62d08f35e4eef0378ec1e2200172fd0", size = 858282, upload-time = "2026-06-22T00:46:49.276Z" },
{ url = "https://files.pythonhosted.org/packages/68/17/06e72a18ee9b572914447338237ca7eb164c0df901f141bc10d1282247a2/nh3-0.3.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:82ca5bf427ad1b216b65ede1a2e2d87dc49bec417ceba0f297213107d3cd9d78", size = 1014328, upload-time = "2026-06-22T00:46:51.026Z" },
{ url = "https://files.pythonhosted.org/packages/11/f9/3966c61455668c08853bf5e33b4bed93c421f3194ce4de896dc248d6f6ce/nh3-0.3.6-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5ed5fe84aee7f39db95c214a7421bf0499fbf500fec6d86a4e29bfc37971438", size = 1098207, upload-time = "2026-06-22T00:46:52.674Z" },
{ url = "https://files.pythonhosted.org/packages/19/d3/479cb4ae440424825735d60525b53e3c77fd60fd6e6afc0e984f00eb0178/nh3-0.3.6-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:082675ff87b9385ec430ffe6d5847ba7456cc39b73720cd4add472f9f4cffd56", size = 1056961, upload-time = "2026-06-22T00:46:54.335Z" },
{ url = "https://files.pythonhosted.org/packages/17/0c/6cdb5ee1e127be50dc8391e54bddc1f64e87bf4bfad0c55633320e2e02db/nh3-0.3.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36d06341bd501240d320f5942481ed5e6846136b666e1ba4faf802b78ebc875f", size = 1033829, upload-time = "2026-06-22T00:46:56.258Z" },
{ url = "https://files.pythonhosted.org/packages/e9/55/9de666ad975d6ccd77d799ea0add55ee2347aa81286ce21b2a97c070746b/nh3-0.3.6-cp38-abi3-win32.whl", hash = "sha256:5276ef17bdba9ad8040575c74072008b13aae429436e9d0429e718bb5f90f4da", size = 609081, upload-time = "2026-06-22T00:46:57.665Z" },
{ url = "https://files.pythonhosted.org/packages/82/fa/2b5d684e3edf1e81bfd02d298c78c3e3da77ca1d8a2be3183a79544a7548/nh3-0.3.6-cp38-abi3-win_amd64.whl", hash = "sha256:f338ac7d594c067679f1e99b4f5ec3906842979560f9d8f15d6bdfa39a353b10", size = 624461, upload-time = "2026-06-22T00:46:59.163Z" },
{ url = "https://files.pythonhosted.org/packages/7b/e5/7cafee2f0413ca4cb0ef3bd111e94d408a48810008b283ad8aee00dd1809/nh3-0.3.6-cp38-abi3-win_arm64.whl", hash = "sha256:69f365963f63a1e9bff53bdbb3c542c7c2efed3e163c9d5d83a772a2ac468c21", size = 603060, upload-time = "2026-06-22T00:47:00.596Z" },
]
[[package]]
name = "packaging"
version = "26.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
]
[[package]]
name = "platformdirs"
version = "4.10.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" },
]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]
[[package]]
name = "pygments"
version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
[[package]]
name = "pywin32-ctypes"
version = "0.2.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
]
[[package]]
name = "readme-renderer"
version = "45.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "docutils" },
{ name = "nh3" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/02/51/d3a6ea424652c60f05600d8c2e01a55c913755e7cdad64afabbd1aa16f44/readme_renderer-45.0.tar.gz", hash = "sha256:030a8fac74904f8fba11ad1bb6964e3f76e896dc7e5e71f16af190c9056696d1", size = 36172, upload-time = "2026-06-09T21:05:17.37Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/97/1b/295bf2fa3e740131778065e5ffa2c481f0e7210182d408e9a2c244ff5b0c/readme_renderer-45.0-py3-none-any.whl", hash = "sha256:3385ed220117104a2bceb4a9dac8c5fdf6d1f96890d7ea2a9c7174fd5c84091f", size = 14134, upload-time = "2026-06-09T21:05:15.85Z" },
]
[[package]]
name = "requests"
version = "2.34.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
]
[[package]]
name = "requests-toolbelt"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "requests" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" },
]
[[package]]
name = "rfc3986"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" },
]
[[package]]
name = "rich"
version = "15.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
]
[[package]]
name = "secretstorage"
version = "3.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "jeepney" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" },
]
[[package]]
name = "textual"
version = "8.2.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py", extra = ["linkify"] },
{ name = "mdit-py-plugins" },
{ name = "platformdirs" },
{ name = "pygments" },
{ name = "rich" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/00/21/39a76b01bd5eea82a04baaca7580e105d8c59450df03998345bb2cfb307b/textual-8.2.8.tar.gz", hash = "sha256:3f106a9fbc73e39dd266c9712432087de78a6d644084c7c241d6a25c3169115b", size = 1860502, upload-time = "2026-06-30T06:51:24.495Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/be/35261223d9416a0751cdff1c7b4a6f881387218a12d439fe22fefebc8c04/textual-8.2.8-py3-none-any.whl", hash = "sha256:267375fd402dc8d981457212efa71f0e3365fd17bba144ba9bb3ed7563cb374a", size = 731418, upload-time = "2026-06-30T06:51:26.364Z" },
]
[package.optional-dependencies]
syntax = [
{ name = "tree-sitter" },
{ name = "tree-sitter-bash" },
{ name = "tree-sitter-css" },
{ name = "tree-sitter-go" },
{ name = "tree-sitter-html" },
{ name = "tree-sitter-java" },
{ name = "tree-sitter-javascript" },
{ name = "tree-sitter-json" },
{ name = "tree-sitter-markdown" },
{ name = "tree-sitter-python" },
{ name = "tree-sitter-regex" },
{ name = "tree-sitter-rust" },
{ name = "tree-sitter-sql" },
{ name = "tree-sitter-toml" },
{ name = "tree-sitter-xml" },
{ name = "tree-sitter-yaml" },
]
[[package]]
name = "tree-sitter"
version = "0.26.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f7/03/5600b84aff2e6c4fe80cfebb4063fe2f50299521befe5f6092ab8c082f4a/tree_sitter-0.26.0.tar.gz", hash = "sha256:b40c219edccc4564530c96f8f1556f6202b37cda964d1cbd7bd2b7e68b40a245", size = 191423, upload-time = "2026-06-30T12:14:27.933Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/7a/4d84e6f6ae2c3e757490dd84de251712c31e293dfe31f28da1ec019cefa2/tree_sitter-0.26.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:5a3c93a352b7e6f70f73e121bbfa2d0117ba7478bd51114ed35c91b0b78814fa", size = 148901, upload-time = "2026-06-30T12:14:19.452Z" },
{ url = "https://files.pythonhosted.org/packages/b0/d9/efe62ec65dc9d096e834d27b8c058127e2146e42ff3380b822a233f016a6/tree_sitter-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5fc2f41bf246ff2f70a9cc3690be35ec7580a4923151873d898c8bcb1a4503d3", size = 140805, upload-time = "2026-06-30T12:14:20.478Z" },
{ url = "https://files.pythonhosted.org/packages/c4/2c/c82326b7b97e3c485c18679883b16f89e5e913c639d3b219d3da70c9e67e/tree_sitter-0.26.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8ea92a255c91671a7ec4625aba3ab7bb5220c423630ffbf83c45d7312abe084", size = 640586, upload-time = "2026-06-30T12:14:21.527Z" },
{ url = "https://files.pythonhosted.org/packages/e2/7a/f56e7d8282859452611024c7cbc623bfba5b24b8cb9b8f8bc88c5219fe9a/tree_sitter-0.26.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f665510f0fcf4636fb9696f1f7853bed7a3bd764b7bb0cb8494e619c14ed5a0c", size = 668300, upload-time = "2026-06-30T12:14:22.728Z" },
{ url = "https://files.pythonhosted.org/packages/91/51/240ee81b9d5e9ca0a6cb1528e8605ffa70ab58c89ce126631be96d3e4bae/tree_sitter-0.26.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:253df7ab82cc0a9d311cd65f06e9f99fb3eac55996ae9fc94da22f123a861b90", size = 649627, upload-time = "2026-06-30T12:14:23.819Z" },
{ url = "https://files.pythonhosted.org/packages/6a/54/760035cefedf9eb44f0f84c4ac22f1322e73155853e272576ee876336312/tree_sitter-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ff80d4833d330a73184a3ac5132abe93c575d2dea31975c6f15c0d21fef238aa", size = 664885, upload-time = "2026-06-30T12:14:25.064Z" },
{ url = "https://files.pythonhosted.org/packages/c9/1b/0b36fe2a984ecedc4ce6aefd5d56447a6626a8e9b595c4e48658510ce8f8/tree_sitter-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:a4033fecc8f606c7f2e8b8014d0057b74668a7f0152763606f7bc25c5f9ec64c", size = 132688, upload-time = "2026-06-30T12:14:26.106Z" },
{ url = "https://files.pythonhosted.org/packages/4d/74/ebc041a13fbf40144afdb0d4b447e48e0b4012ca866c63de8b48f801f0c1/tree_sitter-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:823251c4b6725a7c03ed497a339135ede7ae4bdde75bb8be7ef5e305aeb4ff52", size = 120287, upload-time = "2026-06-30T12:14:26.991Z" },
]
[[package]]
name = "tree-sitter-bash"
version = "0.25.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/0e/f0108be910f1eef6499eabce517e79fe3b12057280ed398da67ce2426cba/tree_sitter_bash-0.25.1.tar.gz", hash = "sha256:bfc0bdaa77bc1e86e3c6652e5a6e140c40c0a16b84185c2b63ad7cd809b88f14", size = 419703, upload-time = "2025-12-02T17:01:08.849Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/30/8e/37e7364d9c9c58da89e05c510671d8c45818afd7b31c6939ab72f8dc6c04/tree_sitter_bash-0.25.1-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:0e6235f59e366d220dde7d830196bed597d01e853e44d8ccd1a82c5dd2500acf", size = 194160, upload-time = "2025-12-02T17:00:59.047Z" },
{ url = "https://files.pythonhosted.org/packages/23/bb/2d2cfbb1f89aaeb1ec892624f069d92d058d06bb66f16b9ec9fb5873ab60/tree_sitter_bash-0.25.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:f4a34a6504c7c5b2a9b8c5c4065531dea19ca2c35026e706cf2eeeebe2c92512", size = 202659, upload-time = "2025-12-02T17:01:00.275Z" },
{ url = "https://files.pythonhosted.org/packages/25/f0/1bb25519be27460255d3899db677313cfa1e6306988fbf456a3d7e211bbb/tree_sitter_bash-0.25.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e76c4cfb20b076552406782b7f8c2a3946835993df0a44df006de54b7030c7dc", size = 230596, upload-time = "2025-12-02T17:01:01.759Z" },
{ url = "https://files.pythonhosted.org/packages/d7/22/9f70bc3d3b942ab9fc0f89c1dc9e087519a3a94f64ae6b7377aae3a7a0f0/tree_sitter_bash-0.25.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f484c4bb8796cde7a87ca351e6116f09653edac0eb3c6d238566359dd28b117", size = 231981, upload-time = "2025-12-02T17:01:02.859Z" },
{ url = "https://files.pythonhosted.org/packages/7a/c3/f1540e42cd41b323c6821e45e52e1aed6ed386209aad52db996f05703963/tree_sitter_bash-0.25.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5e76af6df46d958c7f5b6d5884c9743218e3902a00ccb493ec92728b1084430b", size = 228364, upload-time = "2025-12-02T17:01:03.997Z" },
{ url = "https://files.pythonhosted.org/packages/f7/a0/c3050a6277dfcac8c480f514dc4fe49f3f65f0eac68b4702cbaca2584e85/tree_sitter_bash-0.25.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a3332d71c7b7d5f78259b19d02d0ea111fcb82b72712ee4a93aaa5b226d3f0a8", size = 230074, upload-time = "2025-12-02T17:01:05.05Z" },
{ url = "https://files.pythonhosted.org/packages/71/0f/203fe6b27211387f4b9ba8c4a321567ca4ded2624dae6ccdbd2b6e940e17/tree_sitter_bash-0.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:52a6802d9218f86278aa3e8b459c3abdad67eed0fde1f9f13aca5b6c634217a6", size = 195574, upload-time = "2025-12-02T17:01:06.412Z" },
{ url = "https://files.pythonhosted.org/packages/47/75/4ca1a9fabd8fb5aea78cea70f7837ce4dbf2afae115f62051e5fa99cba1c/tree_sitter_bash-0.25.1-cp310-abi3-win_arm64.whl", hash = "sha256:59115057ec2bae319e8082ff29559861045002964c3431ccb0fc92aa4bc9bccb", size = 191196, upload-time = "2025-12-02T17:01:07.486Z" },
]
[[package]]
name = "tree-sitter-css"
version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/38/37/7d60171240d4c5ba330f05b725dfb5e5fd5b7cbe0aa98ef9e77f77f868f5/tree_sitter_css-0.25.0.tar.gz", hash = "sha256:2fc996bf05b04e06061e88ee4c60837783dc4e62a695205acbc262ee30454138", size = 43232, upload-time = "2025-09-28T11:37:13.387Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/25/a9/69e556f15ca774638bd79005369213dfbd41995bf032ce81cf3ffe086b8a/tree_sitter_css-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ddce6f84eeb0bb2877b4587b07bffb0753040c44d811ed9ab2af978c313beda8", size = 29933, upload-time = "2025-09-28T11:37:07.703Z" },
{ url = "https://files.pythonhosted.org/packages/4d/28/ebcbcbba812d3e407f2f393747330eb8843e0c69d159024e33460b622aab/tree_sitter_css-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:5a2a9c875037ef5f9da57697fb8075086476d42a49d25a88dcca60dfc09bd092", size = 31097, upload-time = "2025-09-28T11:37:08.46Z" },
{ url = "https://files.pythonhosted.org/packages/86/a2/6f9658c723f3a857367c198bd4f50d854aa9468783b418407492c9634a44/tree_sitter_css-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4f5e1135bfd01bce24e2fc7bca1381f52bdd6c6282ee28f7aa77185340bcd135", size = 41713, upload-time = "2025-09-28T11:37:09.101Z" },
{ url = "https://files.pythonhosted.org/packages/85/bb/f74eea6839cb1ff6b5851c6ed33b18e65309eb347bbbe027c93e70e6c691/tree_sitter_css-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b6d0084536828c733a66524a43c9df89f335971d5b1b973e9d1c42ba9dd426b", size = 42312, upload-time = "2025-09-28T11:37:09.757Z" },
{ url = "https://files.pythonhosted.org/packages/ca/fd/031ef1a5938441c98342faf70bb30998683b2130d4b55c282d76b2083f4a/tree_sitter_css-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8a83825daf538656cb88f4f7a0dd9963e3f204e83e7f8d92131f17e5bd712a77", size = 41585, upload-time = "2025-09-28T11:37:10.447Z" },
{ url = "https://files.pythonhosted.org/packages/96/74/9f269bb3644a0511c1c263135e32d38a7f2af39cbba24d59a1633a5ebbc1/tree_sitter_css-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b486c097d250a598fba5f1f46f62697c7f4428252c8bdaad696a907ee913421d", size = 41490, upload-time = "2025-09-28T11:37:11.134Z" },
{ url = "https://files.pythonhosted.org/packages/04/9f/d4f1d3164b692b97266274dad6437586e0614f75080b7795fc7bfa5bf8ff/tree_sitter_css-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:fe319e4ad1b8327afbd9758b3ae22b09226d6c28dc9b022bcadabdaf6ea3716c", size = 32416, upload-time = "2025-09-28T11:37:11.808Z" },
{ url = "https://files.pythonhosted.org/packages/39/5c/fa62d70cb324788bcced741b5e19864ccf4c51ca31766a9f56a6b46a5cf6/tree_sitter_css-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:4fc2c82645cd593f1c695b4d6b678d71e633212ca030f26dedee4f92434bfe21", size = 31057, upload-time = "2025-09-28T11:37:12.734Z" },
]
[[package]]
name = "tree-sitter-go"
version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/01/05/727308adbbc79bcb1c92fc0ea10556a735f9d0f0a5435a18f59d40f7fd77/tree_sitter_go-0.25.0.tar.gz", hash = "sha256:a7466e9b8d94dda94cae8d91629f26edb2d26166fd454d4831c3bf6dfa2e8d68", size = 93890, upload-time = "2025-08-29T06:20:25.044Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/aa/0984707acc2b9bb461fe4a41e7e0fc5b2b1e245c32820f0c83b3c602957c/tree_sitter_go-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b852993063a3429a443e7bd0aa376dd7dd329d595819fabf56ac4cf9d7257b54", size = 47117, upload-time = "2025-08-29T06:20:14.286Z" },
{ url = "https://files.pythonhosted.org/packages/32/16/dd4cb124b35e99239ab3624225da07d4cb8da4d8564ed81d03fcb3a6ba9f/tree_sitter_go-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:503b81a2b4c31e302869a1de3a352ad0912ccab3df9ac9950197b0a9ceeabd8f", size = 48674, upload-time = "2025-08-29T06:20:17.557Z" },
{ url = "https://files.pythonhosted.org/packages/86/fb/b30d63a08044115d8b8bd196c6c2ab4325fb8db5757249a4ef0563966e2e/tree_sitter_go-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04b3b3cb4aff18e74e28d49b716c6f24cb71ddfdd66768987e26e4d0fa812f74", size = 66418, upload-time = "2025-08-29T06:20:18.345Z" },
{ url = "https://files.pythonhosted.org/packages/26/21/d3d88a30ad007419b2c97b3baeeef7431407faf9f686195b6f1cad0aedf9/tree_sitter_go-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:148255aca2f54b90d48c48a9dbb4c7faad6cad310a980b2c5a5a9822057ed145", size = 72006, upload-time = "2025-08-29T06:20:19.14Z" },
{ url = "https://files.pythonhosted.org/packages/cd/d0/0dd6442353ced8a88bbda9e546f4ea29e381b59b5a40b122e5abb586bb6c/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4d338116cdf8a6c6ff990d2441929b41323ef17c710407abe0993c13417d6aad", size = 70603, upload-time = "2025-08-29T06:20:21.544Z" },
{ url = "https://files.pythonhosted.org/packages/01/e2/ee5e09f63504fc286539535d374d2eaa0e7d489b80f8f744bb3962aff22a/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5608e089d2a29fa8d2b327abeb2ad1cdb8e223c440a6b0ceab0d3fa80bdeebae", size = 66088, upload-time = "2025-08-29T06:20:22.336Z" },
{ url = "https://files.pythonhosted.org/packages/6e/b6/d9142583374720e79aca9ccb394b3795149a54c012e1dfd80738df2d984e/tree_sitter_go-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:30d4ada57a223dfc2c32d942f44d284d40f3d1215ddcf108f96807fd36d53022", size = 48152, upload-time = "2025-08-29T06:20:23.089Z" },
{ url = "https://files.pythonhosted.org/packages/9e/00/9a2638e7339236f5b01622952a4d71c1474dd3783d1982a89555fc1f03b1/tree_sitter_go-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:d5d62362059bf79997340773d47cc7e7e002883b527a05cca829c46e40b70ded", size = 46752, upload-time = "2025-08-29T06:20:24.235Z" },
]
[[package]]
name = "tree-sitter-html"
version = "0.23.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/04/06/ad1c53c79da15bef85939aa022d72301e12a9773e9bb9a5e6a6f65b7753a/tree_sitter_html-0.23.2.tar.gz", hash = "sha256:bc9922defe23144d9146bc1509fcd00d361bf6b3303f9effee6532c6a0296961", size = 13977, upload-time = "2024-11-11T05:58:07.403Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/27/b846852b567601c4df765bcb4636085a3260e9f03ae21e0ef2e7c7f957fc/tree_sitter_html-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e1641d5edf5568a246c6c47b947ed524b5bf944664e6473b21d4ae568e28ee9", size = 14787, upload-time = "2024-11-11T05:57:58.684Z" },
{ url = "https://files.pythonhosted.org/packages/bd/17/827c315deb156bb8cac541da800c4bd62878f50a28b7498fbb722bddd225/tree_sitter_html-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:3d0a83dd6cd1c7d4bcf6287b5145c92140f0194f8516f329ae8b9e952fbfa8ff", size = 15232, upload-time = "2024-11-11T05:58:00.139Z" },
{ url = "https://files.pythonhosted.org/packages/91/cb/2028fe446d0e18edf3737d91edcb6430f2c97f2296b8cd760702dfa13d90/tree_sitter_html-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b3775732fffc0abd275a419ef018fd4c1ad4044b2a2e422f3378d93c30eded", size = 39109, upload-time = "2024-11-11T05:58:00.986Z" },
{ url = "https://files.pythonhosted.org/packages/19/bc/b24f5e66be51447cf7e9bcce3d9440a6b4f17021da85779a51566646a7c7/tree_sitter_html-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bdaa7ac5030d416aea0c512d4810ef847bbbd62d61e3d213f370b64ce147293", size = 39630, upload-time = "2024-11-11T05:58:02.424Z" },
{ url = "https://files.pythonhosted.org/packages/d2/d5/31b46cb362ad9679af21ff8b75d846fb7522ecf949beea4fddc86e97815d/tree_sitter_html-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d2e9631b66041a4fd792d7f79a0c4128adb3bfc71f3dcb7e1a3eab5dbee77d67", size = 37440, upload-time = "2024-11-11T05:58:03.819Z" },
{ url = "https://files.pythonhosted.org/packages/28/30/03910b7c037105f33166439f0518dd0aa4f1b7ef8c9d7367c6e9cc6b5681/tree_sitter_html-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:85095f49f9e57f0ac9087a3e830783352c8447fdda55b1c1139aa47e5eaa0e21", size = 17765, upload-time = "2024-11-11T05:58:05.163Z" },
{ url = "https://files.pythonhosted.org/packages/20/32/63761055b03c69202a0e67b6e9a5cb3578da23aeefb62ee3e7ec2c1b0ff2/tree_sitter_html-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:0f65ed9e877144d0f04ade5644e5b0e88bf98a9e60bce65235c99905623e2f1a", size = 15576, upload-time = "2024-11-11T05:58:06.577Z" },
]
[[package]]
name = "tree-sitter-java"
version = "0.23.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fa/dc/eb9c8f96304e5d8ae1663126d89967a622a80937ad2909903569ccb7ec8f/tree_sitter_java-0.23.5.tar.gz", hash = "sha256:f5cd57b8f1270a7f0438878750d02ccc79421d45cca65ff284f1527e9ef02e38", size = 138121, upload-time = "2024-12-21T18:24:26.936Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/67/21/b3399780b440e1567a11d384d0ebb1aea9b642d0d98becf30fa55c0e3a3b/tree_sitter_java-0.23.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:355ce0308672d6f7013ec913dee4a0613666f4cda9044a7824240d17f38209df", size = 58926, upload-time = "2024-12-21T18:24:12.53Z" },
{ url = "https://files.pythonhosted.org/packages/57/ef/6406b444e2a93bc72a04e802f4107e9ecf04b8de4a5528830726d210599c/tree_sitter_java-0.23.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:24acd59c4720dedad80d548fe4237e43ef2b7a4e94c8549b0ca6e4c4d7bf6e69", size = 62288, upload-time = "2024-12-21T18:24:14.634Z" },
{ url = "https://files.pythonhosted.org/packages/4e/6c/74b1c150d4f69c291ab0b78d5dd1b59712559bbe7e7daf6d8466d483463f/tree_sitter_java-0.23.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9401e7271f0b333df39fc8a8336a0caf1b891d9a2b89ddee99fae66b794fc5b7", size = 85533, upload-time = "2024-12-21T18:24:16.695Z" },
{ url = "https://files.pythonhosted.org/packages/29/09/e0d08f5c212062fd046db35c1015a2621c2631bc8b4aae5740d7adb276ad/tree_sitter_java-0.23.5-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:370b204b9500b847f6d0c5ad584045831cee69e9a3e4d878535d39e4a7e4c4f1", size = 84033, upload-time = "2024-12-21T18:24:18.758Z" },
{ url = "https://files.pythonhosted.org/packages/43/56/7d06b23ddd09bde816a131aa504ee11a1bbe87c6b62ab9b2ed23849a3382/tree_sitter_java-0.23.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:aae84449e330363b55b14a2af0585e4e0dae75eb64ea509b7e5b0e1de536846a", size = 82564, upload-time = "2024-12-21T18:24:20.493Z" },
{ url = "https://files.pythonhosted.org/packages/da/d6/0528c7e1e88a18221dbd8ccee3825bf274b1fa300f745fd74eb343878043/tree_sitter_java-0.23.5-cp39-abi3-win_amd64.whl", hash = "sha256:1ee45e790f8d31d416bc84a09dac2e2c6bc343e89b8a2e1d550513498eedfde7", size = 60650, upload-time = "2024-12-21T18:24:22.902Z" },
{ url = "https://files.pythonhosted.org/packages/72/57/5bab54d23179350356515526fff3cc0f3ac23bfbc1a1d518a15978d4880e/tree_sitter_java-0.23.5-cp39-abi3-win_arm64.whl", hash = "sha256:402efe136104c5603b429dc26c7e75ae14faaca54cfd319ecc41c8f2534750f4", size = 59059, upload-time = "2024-12-21T18:24:24.934Z" },
]
[[package]]
name = "tree-sitter-javascript"
version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/59/e0/e63103c72a9d3dfd89a31e02e660263ad84b7438e5f44ee82e443e65bbde/tree_sitter_javascript-0.25.0.tar.gz", hash = "sha256:329b5414874f0588a98f1c291f1b28138286617aa907746ffe55adfdcf963f38", size = 132338, upload-time = "2025-09-01T07:13:44.792Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/df/5106ac250cd03661ebc3cc75da6b3d9f6800a3606393a0122eca58038104/tree_sitter_javascript-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b70f887fb269d6e58c349d683f59fa647140c410cfe2bee44a883b20ec92e3dc", size = 64052, upload-time = "2025-09-01T07:13:36.865Z" },
{ url = "https://files.pythonhosted.org/packages/b1/8f/6b4b2bc90d8ab3955856ce852cc9d1e82c81d7ab9646385f0e75ffd5b5d3/tree_sitter_javascript-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:8264a996b8845cfce06965152a013b5d9cbb7d199bc3503e12b5682e62bb1de1", size = 66440, upload-time = "2025-09-01T07:13:37.962Z" },
{ url = "https://files.pythonhosted.org/packages/5f/c4/7da74ecdcd8a398f88bd003a87c65403b5fe0e958cdd43fbd5fd4a398fcf/tree_sitter_javascript-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9dc04ba91fc8583344e57c1f1ed5b2c97ecaaf47480011b92fbeab8dda96db75", size = 99728, upload-time = "2025-09-01T07:13:38.755Z" },
{ url = "https://files.pythonhosted.org/packages/96/c8/97da3af4796495e46421e9344738addb3602fa6426ea695be3fcbadbee37/tree_sitter_javascript-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:199d09985190852e0912da2b8d26c932159be314bc04952cf917ed0e4c633e6b", size = 106072, upload-time = "2025-09-01T07:13:39.798Z" },
{ url = "https://files.pythonhosted.org/packages/13/be/c964e8130be08cc9bd6627d845f0e4460945b158429d39510953bbcb8fcc/tree_sitter_javascript-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dfcf789064c58dc13c0a4edb550acacfc6f0f280577f1e7a00de3e89fc7f8ddc", size = 104388, upload-time = "2025-09-01T07:13:40.866Z" },
{ url = "https://files.pythonhosted.org/packages/ee/89/9b773dee0f8961d1bb8d7baf0a204ab587618df19897c1ef260916f318ec/tree_sitter_javascript-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b852d3aee8a36186dbcc32c798b11b4869f9b5041743b63b65c2ef793db7a54", size = 98377, upload-time = "2025-09-01T07:13:41.838Z" },
{ url = "https://files.pythonhosted.org/packages/3b/dc/d90cb1790f8cec9b4878d278ad9faf7c8f893189ce0f855304fd704fc274/tree_sitter_javascript-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:e5ed840f5bd4a3f0272e441d19429b26eedc257abe5574c8546da6b556865e3c", size = 62975, upload-time = "2025-09-01T07:13:42.828Z" },
{ url = "https://files.pythonhosted.org/packages/2e/1f/f9eba1038b7d4394410f3c0a6ec2122b590cd7acb03f196e52fa57ebbe72/tree_sitter_javascript-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:622a69d677aa7f6ee2931d8c77c981a33f0ebb6d275aa9d43d3397c879a9bb0b", size = 61668, upload-time = "2025-09-01T07:13:43.803Z" },
]
[[package]]
name = "tree-sitter-json"
version = "0.24.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/29/e92df6dca3a6b2ab1c179978be398059817e1173fbacd47e832aaff3446b/tree_sitter_json-0.24.8.tar.gz", hash = "sha256:ca8486e52e2d261819311d35cf98656123d59008c3b7dcf91e61d2c0c6f3120e", size = 8155, upload-time = "2024-11-11T06:05:00.667Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/41/84866232980fb3cf0cff46f5af2dbb9bfa3324b32614c6a9af3d08926b72/tree_sitter_json-0.24.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:59ac06c6db1877d0e2076bce54a5fddcdd2fc38ca778905662e80fa9ffcea2ab", size = 8718, upload-time = "2024-11-11T06:04:49.779Z" },
{ url = "https://files.pythonhosted.org/packages/5c/31/102c15948d97b135611d6a995c97a3933c0e9745f25737723977f58e142c/tree_sitter_json-0.24.8-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:62b4c45b561db31436a81a3f037f71ec29049f4fc9bf5269b6ec3ebaaa35a1cd", size = 9163, upload-time = "2024-11-11T06:04:51.275Z" },
{ url = "https://files.pythonhosted.org/packages/28/64/aa44ea2f3d2e76ec086ce83902eb26b2ed0a92d3fd5e2714c9cb007e90d1/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8627f7d375fda9fc193ebee368c453f374f65c2f25c58b6fea4e6b49a7fccbc", size = 17726, upload-time = "2024-11-11T06:04:52.732Z" },
{ url = "https://files.pythonhosted.org/packages/77/08/10001992526670e0d6f24c571b179f0ece90e5e014a4b98a3ce076884f32/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85cca779872f7278f3a74eb38533d34b9c4de4fd548615e3361fa64fe350ad0a", size = 17236, upload-time = "2024-11-11T06:04:54.189Z" },
{ url = "https://files.pythonhosted.org/packages/92/64/908e9e0bd84fe3c81c564115d3bbe0e49b0e152784bbaf153d749d00bbe6/tree_sitter_json-0.24.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:deeb45850dcc52990fbb52c80196492a099e3fa3512d928a390a91cf061068cc", size = 16071, upload-time = "2024-11-11T06:04:55.628Z" },
{ url = "https://files.pythonhosted.org/packages/53/df/31daab1eedb445bef208a04fc35428de3afe2b37075fec84d7737e1c69de/tree_sitter_json-0.24.8-cp39-abi3-win_amd64.whl", hash = "sha256:e4849a03cd7197267b2688a4506a90a13568a8e0e8588080bd0212fcb38974e3", size = 11457, upload-time = "2024-11-11T06:04:57.698Z" },
{ url = "https://files.pythonhosted.org/packages/6c/3d/902d2f3125b6b90cebf404b63ca775bc6d82071ccc76c0d10fabfeb2febe/tree_sitter_json-0.24.8-cp39-abi3-win_arm64.whl", hash = "sha256:591e0096c882d12668b88f30d3ca6f85b9db3406910eaaab6afb6b17d65367dd", size = 10174, upload-time = "2024-11-11T06:04:59.309Z" },
]
[[package]]
name = "tree-sitter-markdown"
version = "0.5.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9a/87/8f705d8f99337c8a691bcc8c22d89ddd323eb2b860a78ae2e894b9f7ade1/tree_sitter_markdown-0.5.1.tar.gz", hash = "sha256:6c69d7270a7e09be8988ced44584c09a6a4f541cea0dc394dd1c1a5ac3b5601d", size = 250138, upload-time = "2025-09-16T17:12:11.732Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/73/b5f88217a526f61080ddd71d554cff6a01ea23fffa584ad9de41ee8d1fe5/tree_sitter_markdown-0.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:f00ce3f48f127377983859fcb93caf0693cbc7970f8c41f1e2bd21e4d56bdfd8", size = 139706, upload-time = "2025-09-16T17:12:03.738Z" },
{ url = "https://files.pythonhosted.org/packages/6d/9b/65eb5e6a8d7791174644854437d35849d9b4e4ed034d54d2c78810eaf1a6/tree_sitter_markdown-0.5.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1ec4cc5d7b0d188bad22247501ab13663bb1bf1a60c2c020a22877fabce8daa9", size = 147540, upload-time = "2025-09-16T17:12:04.955Z" },
{ url = "https://files.pythonhosted.org/packages/24/d5/4152d00829c8643243f65b67a5485248661824f15e1868e14e54f03c2069/tree_sitter_markdown-0.5.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727242a70c46222092eba86c102301646f21ba32aee221f4b1f70e2020755e81", size = 187851, upload-time = "2025-09-16T17:12:05.813Z" },
{ url = "https://files.pythonhosted.org/packages/a8/c1/994001c5a51d09e9da7236e01a855d3d49437a47fa8669f1d5e9ed60e64f/tree_sitter_markdown-0.5.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0b2fde19e692bb90e300d9788887528c624b659c794de6337f8193396de4399", size = 187563, upload-time = "2025-09-16T17:12:06.929Z" },
{ url = "https://files.pythonhosted.org/packages/cb/d1/1f2ba1ae11568639f133c45c7a697e4e9277d6cc26a66c0caee62c11d1c2/tree_sitter_markdown-0.5.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:13da82db04cec7910b6afd4a67d02da9ef402df8d56fc6ed85e00584af1730ee", size = 185478, upload-time = "2025-09-16T17:12:08.126Z" },
{ url = "https://files.pythonhosted.org/packages/8c/c8/8218482d56b78755cdc20816a28754145cb1767e1e7e0ddde5988547ab86/tree_sitter_markdown-0.5.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8a8a04a5d942c177cc590ec40074fcf3658f3a7c0a3388a8575990003665d8c", size = 184922, upload-time = "2025-09-16T17:12:08.937Z" },
{ url = "https://files.pythonhosted.org/packages/b6/ca/423600960b91c3aba6f2202ad4c430b5401e652d51a73a59769375c2b4ea/tree_sitter_markdown-0.5.1-cp39-abi3-win_amd64.whl", hash = "sha256:b1b0e4cbcf5a7b85005f1e9266fc2ed9b649b41a6048f3b1abae3612368d97a6", size = 142519, upload-time = "2025-09-16T17:12:10.027Z" },
{ url = "https://files.pythonhosted.org/packages/93/f5/327dd7fa42ae39796a8853685c40a8ac968585260094c581047270cbc851/tree_sitter_markdown-0.5.1-cp39-abi3-win_arm64.whl", hash = "sha256:2296ef53a757d8f5b848616706d0518e04d487bc7748bd05755d4a3a65711542", size = 137166, upload-time = "2025-09-16T17:12:10.858Z" },
]
[[package]]
name = "tree-sitter-python"
version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b8/8b/c992ff0e768cb6768d5c96234579bf8842b3a633db641455d86dd30d5dac/tree_sitter_python-0.25.0.tar.gz", hash = "sha256:b13e090f725f5b9c86aa455a268553c65cadf325471ad5b65cd29cac8a1a68ac", size = 159845, upload-time = "2025-09-11T06:47:58.159Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/64/a4e503c78a4eb3ac46d8e72a29c1b1237fa85238d8e972b063e0751f5a94/tree_sitter_python-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:14a79a47ddef72f987d5a2c122d148a812169d7484ff5c75a3db9609d419f361", size = 73790, upload-time = "2025-09-11T06:47:47.652Z" },
{ url = "https://files.pythonhosted.org/packages/e6/1d/60d8c2a0cc63d6ec4ba4e99ce61b802d2e39ef9db799bdf2a8f932a6cd4b/tree_sitter_python-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:480c21dbd995b7fe44813e741d71fed10ba695e7caab627fb034e3828469d762", size = 76691, upload-time = "2025-09-11T06:47:49.038Z" },
{ url = "https://files.pythonhosted.org/packages/aa/cb/d9b0b67d037922d60cbe0359e0c86457c2da721bc714381a63e2c8e35eba/tree_sitter_python-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:86f118e5eecad616ecdb81d171a36dde9bef5a0b21ed71ea9c3e390813c3baf5", size = 108133, upload-time = "2025-09-11T06:47:50.499Z" },
{ url = "https://files.pythonhosted.org/packages/40/bd/bf4787f57e6b2860f3f1c8c62f045b39fb32d6bac4b53d7a9e66de968440/tree_sitter_python-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be71650ca2b93b6e9649e5d65c6811aad87a7614c8c1003246b303f6b150f61b", size = 110603, upload-time = "2025-09-11T06:47:51.985Z" },
{ url = "https://files.pythonhosted.org/packages/5d/25/feff09f5c2f32484fbce15db8b49455c7572346ce61a699a41972dea7318/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6d5b5799628cc0f24691ab2a172a8e676f668fe90dc60468bee14084a35c16d", size = 108998, upload-time = "2025-09-11T06:47:53.046Z" },
{ url = "https://files.pythonhosted.org/packages/75/69/4946da3d6c0df316ccb938316ce007fb565d08f89d02d854f2d308f0309f/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:71959832fc5d9642e52c11f2f7d79ae520b461e63334927e93ca46cd61cd9683", size = 107268, upload-time = "2025-09-11T06:47:54.388Z" },
{ url = "https://files.pythonhosted.org/packages/ed/a2/996fc2dfa1076dc460d3e2f3c75974ea4b8f02f6bc925383aaae519920e8/tree_sitter_python-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:9bcde33f18792de54ee579b00e1b4fe186b7926825444766f849bf7181793a76", size = 76073, upload-time = "2025-09-11T06:47:55.773Z" },
{ url = "https://files.pythonhosted.org/packages/07/19/4b5569d9b1ebebb5907d11554a96ef3fa09364a30fcfabeff587495b512f/tree_sitter_python-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:0fbf6a3774ad7e89ee891851204c2e2c47e12b63a5edbe2e9156997731c128bb", size = 74169, upload-time = "2025-09-11T06:47:56.747Z" },
]
[[package]]
name = "tree-sitter-regex"
version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/86/92/1767b833518d731b97c07cf616ea15495dcc0af584aa0381657be4ec446d/tree_sitter_regex-0.25.0.tar.gz", hash = "sha256:5d29111b3f27d4afb31496476d392d1f562fe0bfe954e8968f1d8683424fc331", size = 22156, upload-time = "2025-09-13T05:00:18.699Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2b/b4/12e9ba02bab4ce13d1875f6585c3f2a5816233104d1507ea118950a4f7eb/tree_sitter_regex-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3fa11bbd76b29ac8ca2dbf85ad082f9b18ae6352251d805eb2d4191e1706a9d5", size = 13267, upload-time = "2025-09-13T05:00:10.847Z" },
{ url = "https://files.pythonhosted.org/packages/71/06/6b4f995f61952572a94bcfce12d43fc580226551fab9dd0aac4e94465f38/tree_sitter_regex-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:df5713649b89c5758649398053c306c41565f22a6f267cb5ec25596504bcf012", size = 13646, upload-time = "2025-09-13T05:00:12.149Z" },
{ url = "https://files.pythonhosted.org/packages/43/61/d94d889ee415805e5d64fc5163e7e2996975bb2c40d13f547efae3e7e37d/tree_sitter_regex-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cdd92400fd9d8229e584c55e12410251561f0d47eea49db17805e2f64a8b2490", size = 24691, upload-time = "2025-09-13T05:00:13.037Z" },
{ url = "https://files.pythonhosted.org/packages/00/a8/09dd698a9ac2b3d3139a936742b41ec1263f0b86d32ad68f4695871c8860/tree_sitter_regex-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cceab1c14deeec9c5899babcb2b7942f0607b4355e66eab4083514f644f1bd52", size = 26741, upload-time = "2025-09-13T05:00:14.182Z" },
{ url = "https://files.pythonhosted.org/packages/d7/bf/985e226c9a9f5ae895ff1a2cbc69531589a7d74acac49b2710ec89d53d80/tree_sitter_regex-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:253436be178150ca4a0603720e0c246e08b5bdd2dc6df313667d97e6c0fce846", size = 25758, upload-time = "2025-09-13T05:00:14.994Z" },
{ url = "https://files.pythonhosted.org/packages/b8/89/c6a6817e94a7deb61770a21e590a46791778ceed053ba4afbfb095488a23/tree_sitter_regex-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:883eacc46fd7eaffc328efd5865f1fe8825711892d3a89fccc2c414b061e806d", size = 24575, upload-time = "2025-09-13T05:00:16.081Z" },
{ url = "https://files.pythonhosted.org/packages/d0/5e/04e87eb155875f27355703ac7ab703090e30ad9aac6e003ef5c40820ee98/tree_sitter_regex-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:f0f2ebf9a6bb5d0d0da2a8ac51d7e5a985b87cdb24d86db5ddc6a58baf115d5d", size = 15684, upload-time = "2025-09-13T05:00:16.865Z" },
{ url = "https://files.pythonhosted.org/packages/fb/d3/1f37c79dc18cc3c7521fdb51b614d29a36628d2afdc2cac2680967e703a6/tree_sitter_regex-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:d5a36150daa452f8aec1c2d6d1f2d26255dc05d1490f9618b14c12a6a648cda4", size = 14525, upload-time = "2025-09-13T05:00:17.673Z" },
]
[[package]]
name = "tree-sitter-rust"
version = "0.24.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b7/87/75cbd22b927267d310f76cca1ab3c1d9d41035dfa3eb9cc95f96ee199440/tree_sitter_rust-0.24.2.tar.gz", hash = "sha256:54fb02a5911e345308b405174465112479f56dc39e3f1e7744d7568595f00db9", size = 339341, upload-time = "2026-03-27T21:08:55.629Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/24/2b2d33af5e27c84a4fde4e8cd2594bb4ab1e1cf48756a9f40dadc84956cc/tree_sitter_rust-0.24.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3620cfd12340efa43082d45df76349ff511893a9c361da2f8d6d51e307020a59", size = 129507, upload-time = "2026-03-27T21:08:47.585Z" },
{ url = "https://files.pythonhosted.org/packages/78/2a/cf39f881a545360b5a86bb1accba1f4acc713daab01fb9edd35b6e84f473/tree_sitter_rust-0.24.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:01a46622735498493f29f3e628a90de95c96a07bfbeb88996243eb986b1cee36", size = 136812, upload-time = "2026-03-27T21:08:48.761Z" },
{ url = "https://files.pythonhosted.org/packages/ca/45/a051bbd3045a61182dde25b93ae9a33d2677c935b16952283e12eaf46051/tree_sitter_rust-0.24.2-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e033c5a93b57c88e0a835880de39fc802909ff69f57aaff6000211c196ea5190", size = 164706, upload-time = "2026-03-27T21:08:49.605Z" },
{ url = "https://files.pythonhosted.org/packages/b5/f6/a5a146df5c0a5daea3ffcd5d7245775fe7f084357770d5a313dd6245ae78/tree_sitter_rust-0.24.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d76d1208c3638b871236090759dfc13d478921320653a6c9da5336e7c58f65a", size = 170310, upload-time = "2026-03-27T21:08:50.424Z" },
{ url = "https://files.pythonhosted.org/packages/95/a8/f85b1ca75e01361ca5f92d226593ca4857cea49551b9f6c8fa6fc08ea917/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87930163a462408c49ab62c667e74029bc26b4cc7123dd1bdc7352215786c64a", size = 168668, upload-time = "2026-03-27T21:08:51.404Z" },
{ url = "https://files.pythonhosted.org/packages/a2/e1/3519f866a4679ca36acd9f5a06a779ecb8a92b18887c5546458d521df557/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da2b86099028fd42c6cd32878b7b16b01f8aac0f7b0e98742b7fa6bc3cf09b89", size = 162403, upload-time = "2026-03-27T21:08:52.588Z" },
{ url = "https://files.pythonhosted.org/packages/34/71/7ef609894dbfe5699eb16f7471f9b8af1d958d8ba3e29c238d7607e8cb47/tree_sitter_rust-0.24.2-cp39-abi3-win_amd64.whl", hash = "sha256:4529c125d928882ddfb879fdc6bc0704913261ecc078b6fa7902559e0daf200d", size = 129422, upload-time = "2026-03-27T21:08:54.031Z" },
{ url = "https://files.pythonhosted.org/packages/b9/d8/050a781172745bc345f98abb7c56e72022ea0790f8e793de981c83c2ef15/tree_sitter_rust-0.24.2-cp39-abi3-win_arm64.whl", hash = "sha256:66ba90f61bd54f4c4f5d30434957daf64507c16b0313df76becb37d63f70a227", size = 128245, upload-time = "2026-03-27T21:08:54.803Z" },
]
[[package]]
name = "tree-sitter-sql"
version = "0.3.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e8/5c/3d10387f779f36835486167253682f61d5f4fd8336b7001da1ac7d78f31c/tree_sitter_sql-0.3.11.tar.gz", hash = "sha256:700b93be2174c3c83d174ec3e10b682f72a4fb451f0076c7ce5012f1d5a76cbc", size = 834454, upload-time = "2025-10-01T13:44:15.913Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/32/68/bb80073915dfe1b38935451bc0d65528666c126b2d5878e7140ef9bf9f8a/tree_sitter_sql-0.3.11-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cf1b0c401756940bf47544ad7c4cc97373fc0dac118f821820953e7015a115e3", size = 322035, upload-time = "2025-10-01T13:44:07.497Z" },
{ url = "https://files.pythonhosted.org/packages/05/45/b2bd5f9919ea15c4ae90a156999101ebd4caa4036babe54efaf9d3e77d55/tree_sitter_sql-0.3.11-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a33cd6880ab2debef036f80365c32becb740ec79946805598488732b6c515fff", size = 341635, upload-time = "2025-10-01T13:44:08.961Z" },
{ url = "https://files.pythonhosted.org/packages/8e/96/7cee5661aa897e5d1a67499944ea5cf8a148953c1dc07a3059a50db8cb56/tree_sitter_sql-0.3.11-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:344e99b59c8c8d72f7154041e9d054400f4a3fccc16c2c96ac106dde0e7f8d0c", size = 381217, upload-time = "2025-10-01T13:44:10.211Z" },
{ url = "https://files.pythonhosted.org/packages/1d/c1/eec7c09a9c94436ea4c56d096feba815e42b209b3d41a17532f99ecf0c67/tree_sitter_sql-0.3.11-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5128b12f71ac0f5ebcc607f67a62cdc56a187c1a5ba7553feeb9c5f6f9bc3c72", size = 380606, upload-time = "2025-10-01T13:44:11.135Z" },
{ url = "https://files.pythonhosted.org/packages/94/1d/06e9598799bd119e56f6e431d42c2f3a5c6dee858a5b6ad7633cc4d670aa/tree_sitter_sql-0.3.11-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:03cc164fcf7b1f711e7d939aeb4d1f62c76f4162e081c70b860b4fcd91806a38", size = 380862, upload-time = "2025-10-01T13:44:12.072Z" },
{ url = "https://files.pythonhosted.org/packages/52/e9/a7afd7f68ce165c040ce50e67bb05553784a8e17f37e057405d693fc869d/tree_sitter_sql-0.3.11-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0e22ea8de690dd9960d8c0c36c4cd25417b084e1e29c91ac0235fbdb3abb4664", size = 379447, upload-time = "2025-10-01T13:44:13.062Z" },
{ url = "https://files.pythonhosted.org/packages/eb/b3/57ff42dadd33c06fabe6c725de50e1625e1060f1571cc21a9260febadc1f/tree_sitter_sql-0.3.11-cp310-abi3-win_amd64.whl", hash = "sha256:c57b877702d218c0856592d33320c02b2dc8411d8820b3bf7b81be86c54fa0bb", size = 343550, upload-time = "2025-10-01T13:44:13.988Z" },
{ url = "https://files.pythonhosted.org/packages/77/60/f10b8551f435d57a4748820ee30e66df2682820b2972375c2b89d2e5fb10/tree_sitter_sql-0.3.11-cp310-abi3-win_arm64.whl", hash = "sha256:8a1e42f0a2c9b01b23074708ecf5b8d21b9a0440e3dff279d8cf466cdf1a877e", size = 333547, upload-time = "2025-10-01T13:44:14.893Z" },
]
[[package]]
name = "tree-sitter-toml"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/59/b9/03ee757ac375e77186ea112c14fcf31e0ca70b27b6388d93dcceef61f029/tree_sitter_toml-0.7.0.tar.gz", hash = "sha256:29e257612fa8f0c1fcbc4e7e08ddc561169f1725265302e64d81086354144a70", size = 16803, upload-time = "2024-12-03T05:03:46.711Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ad/4d/1e00a5cd8dba09e340b25aa60a3eaeae584ff5bc5d93b0777169d6741ee5/tree_sitter_toml-0.7.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b9ae5c3e7c5b6bb05299dd73452ceafa7fa0687d5af3012332afa7757653b676", size = 14755, upload-time = "2024-12-03T05:03:39.973Z" },
{ url = "https://files.pythonhosted.org/packages/92/20/ac8a20805339105fe0bbb6beaa99dbbd1159647760ddd786142364e0b7f2/tree_sitter_toml-0.7.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:18be09538e9775cddc0290392c4e2739de2201260af361473ca60b5c21f7bd22", size = 15201, upload-time = "2024-12-03T05:03:40.871Z" },
{ url = "https://files.pythonhosted.org/packages/36/cf/7bae8e20310e7cc763ae407599e6130b819f91ad5197e210a56f697f15d8/tree_sitter_toml-0.7.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a045e0acfcf91b7065066f7e51ea038ed7385c1e35e7e8fae18f252d3f8adb8c", size = 30855, upload-time = "2024-12-03T05:03:41.83Z" },
{ url = "https://files.pythonhosted.org/packages/7d/49/51f2fa25a3ff4d45af1be8cbf7a3d733fb6a390b2763cfa00892fffe90bf/tree_sitter_toml-0.7.0-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a2f8cf9d73f07b6628093b35e5c5fbac039247e32cb075eaa5289a5914e73af", size = 29741, upload-time = "2024-12-03T05:03:42.577Z" },
{ url = "https://files.pythonhosted.org/packages/4d/30/dd94ed1ab0bc3198e16ed2140a6f4d2474c1cd561d8c6847ab269af73654/tree_sitter_toml-0.7.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:860ffa4513b2dc3083d8e412bd815a350b0a9490624b37e7c8f6ed5c6f9ce63c", size = 30498, upload-time = "2024-12-03T05:03:43.447Z" },
{ url = "https://files.pythonhosted.org/packages/a2/dd/0681d43aa09dd161565858bcfdd4402c8d10259f142de734448f5ce17418/tree_sitter_toml-0.7.0-cp39-abi3-win_amd64.whl", hash = "sha256:2760a04f06937b01b1562a2135cd7e8207e399e73ef75bbebc77e37b1ad3b15d", size = 16756, upload-time = "2024-12-03T05:03:44.227Z" },
{ url = "https://files.pythonhosted.org/packages/17/e4/cce587001e620f1972e70aeabc1b38893a85681be9ec5a64e4be9ce17410/tree_sitter_toml-0.7.0-cp39-abi3-win_arm64.whl", hash = "sha256:fd00fd8a51c65aa19c40539431cb1773d87c30af5757b4041fa6c229058420b4", size = 15651, upload-time = "2024-12-03T05:03:45.261Z" },
]
[[package]]
name = "tree-sitter-xml"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/41/ba/77a92dbb4dfb374fb99863a07f938de7509ceeaa74139933ac2bd306eeb1/tree_sitter_xml-0.7.0.tar.gz", hash = "sha256:ab0ff396f20230ad8483d968151ce0c35abe193eb023b20fbd8b8ce4cf9e9f61", size = 54635, upload-time = "2024-11-13T17:27:01.655Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/1d/6b8974c493973c0c9df2bbf220a1f0a96fa785da81a5a13461faafd1441c/tree_sitter_xml-0.7.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cc3e516d4c1e0860fb22172c172148debb825ba638971bc48bad15b22e5b0bae", size = 35404, upload-time = "2024-11-13T17:26:51.989Z" },
{ url = "https://files.pythonhosted.org/packages/75/f5/31013d04c4e3b9a55e90168cc222a601c84235ba4953a5a06b5cdf8353c4/tree_sitter_xml-0.7.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0674fdf4cc386e4d323cb287d3b072663de0f20a9e9af5d5e09821aae56a9e5c", size = 35488, upload-time = "2024-11-13T17:26:53.526Z" },
{ url = "https://files.pythonhosted.org/packages/c8/e6/e7493217f950a7c5969e3f3f057664142fa948abefd2dba5acea25719d55/tree_sitter_xml-0.7.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c0fe5f2d6cc09974c8375c8ea9b24909f493b5bf04aacdc4c694b5d2ae6b040", size = 74199, upload-time = "2024-11-13T17:26:55.069Z" },
{ url = "https://files.pythonhosted.org/packages/94/27/1dd6815592489de51fa7b5fffc1160cd385ade7fa06f07b998742ac18020/tree_sitter_xml-0.7.0-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd3209516a4d84dff90bc91d2ad2ce246de8504cede4358849687fa8e71536e7", size = 76244, upload-time = "2024-11-13T17:26:56.655Z" },
{ url = "https://files.pythonhosted.org/packages/20/10/2e4e84c50b2175cb53d255ef154aa893cb82cc9d035d7a1a73be9d2d2db4/tree_sitter_xml-0.7.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:87578e15fa55f44ecd9f331233b6f8a2cbde3546b354c830ecb862a632379455", size = 75112, upload-time = "2024-11-13T17:26:57.729Z" },
{ url = "https://files.pythonhosted.org/packages/ae/91/77c348568bccb179eca21062c923f6f54026900b09fe0cf1aae89d78a0c8/tree_sitter_xml-0.7.0-cp39-abi3-win_amd64.whl", hash = "sha256:9ba2dafc6ce9feaf4ccc617d3aeea57f8e0ca05edad34953e788001ebff79133", size = 36558, upload-time = "2024-11-13T17:26:58.702Z" },
{ url = "https://files.pythonhosted.org/packages/be/cc/6b4de230770d7be87b2a415583121ac565ce1ff7d9a1ad7fec11f8e613fc/tree_sitter_xml-0.7.0-cp39-abi3-win_arm64.whl", hash = "sha256:fc759f710a8fd7a01c23e2d7cb013679199045bea3dc0e5151650a11322aaf40", size = 34610, upload-time = "2024-11-13T17:27:00.187Z" },
]
[[package]]
name = "tree-sitter-yaml"
version = "0.7.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882, upload-time = "2025-10-07T14:40:36.071Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898, upload-time = "2025-10-07T14:40:29.486Z" },
{ url = "https://files.pythonhosted.org/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691, upload-time = "2025-10-07T14:40:30.369Z" },
{ url = "https://files.pythonhosted.org/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430, upload-time = "2025-10-07T14:40:31.16Z" },
{ url = "https://files.pythonhosted.org/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428, upload-time = "2025-10-07T14:40:31.954Z" },
{ url = "https://files.pythonhosted.org/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580, upload-time = "2025-10-07T14:40:33.037Z" },
{ url = "https://files.pythonhosted.org/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455, upload-time = "2025-10-07T14:40:33.778Z" },
{ url = "https://files.pythonhosted.org/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514, upload-time = "2025-10-07T14:40:34.562Z" },
{ url = "https://files.pythonhosted.org/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065, upload-time = "2025-10-07T14:40:35.337Z" },
]
[[package]]
name = "twine"
version = "6.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "id" },
{ name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" },
{ name = "packaging" },
{ name = "readme-renderer" },
{ name = "requests" },
{ name = "requests-toolbelt" },
{ name = "rfc3986" },
{ name = "rich" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" },
]
[[package]]
name = "typing-extensions"
version = "4.16.0"
source = { registry = "https://pypi.org/simple" }
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" }
wheels = [
{ 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" },
]
[[package]]
name = "uc-micro-py"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" },
]
[[package]]
name = "urllib3"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
]
[[package]]
name = "vimkit"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "textual", extra = ["syntax"] },
]
[package.dev-dependencies]
dev = [
{ name = "twine" },
]
[package.metadata]
requires-dist = [{ name = "textual", extras = ["syntax"], specifier = ">=8.2.8" }]
[package.metadata.requires-dev]
dev = [{ name = "twine", specifier = ">=6.1.0" }]