hammad-python 0.0.30__py3-none-any.whl → 0.0.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ham/__init__.py +10 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.31.dist-info}/METADATA +6 -32
- hammad_python-0.0.31.dist-info/RECORD +6 -0
- hammad/__init__.py +0 -84
- hammad/_internal.py +0 -256
- hammad/_main.py +0 -226
- hammad/cache/__init__.py +0 -40
- hammad/cache/base_cache.py +0 -181
- hammad/cache/cache.py +0 -169
- hammad/cache/decorators.py +0 -261
- hammad/cache/file_cache.py +0 -80
- hammad/cache/ttl_cache.py +0 -74
- hammad/cli/__init__.py +0 -33
- hammad/cli/animations.py +0 -573
- hammad/cli/plugins.py +0 -867
- hammad/cli/styles/__init__.py +0 -55
- hammad/cli/styles/settings.py +0 -139
- hammad/cli/styles/types.py +0 -358
- hammad/cli/styles/utils.py +0 -634
- hammad/data/__init__.py +0 -90
- hammad/data/collections/__init__.py +0 -49
- hammad/data/collections/collection.py +0 -326
- hammad/data/collections/indexes/__init__.py +0 -37
- hammad/data/collections/indexes/qdrant/__init__.py +0 -1
- hammad/data/collections/indexes/qdrant/index.py +0 -723
- hammad/data/collections/indexes/qdrant/settings.py +0 -94
- hammad/data/collections/indexes/qdrant/utils.py +0 -210
- hammad/data/collections/indexes/tantivy/__init__.py +0 -1
- hammad/data/collections/indexes/tantivy/index.py +0 -426
- hammad/data/collections/indexes/tantivy/settings.py +0 -40
- hammad/data/collections/indexes/tantivy/utils.py +0 -176
- hammad/data/configurations/__init__.py +0 -35
- hammad/data/configurations/configuration.py +0 -564
- hammad/data/models/__init__.py +0 -50
- hammad/data/models/extensions/__init__.py +0 -4
- hammad/data/models/extensions/pydantic/__init__.py +0 -42
- hammad/data/models/extensions/pydantic/converters.py +0 -759
- hammad/data/models/fields.py +0 -546
- hammad/data/models/model.py +0 -1078
- hammad/data/models/utils.py +0 -280
- hammad/data/sql/__init__.py +0 -24
- hammad/data/sql/database.py +0 -576
- hammad/data/sql/types.py +0 -127
- hammad/data/types/__init__.py +0 -75
- hammad/data/types/file.py +0 -431
- hammad/data/types/multimodal/__init__.py +0 -36
- hammad/data/types/multimodal/audio.py +0 -200
- hammad/data/types/multimodal/image.py +0 -182
- hammad/data/types/text.py +0 -1308
- hammad/formatting/__init__.py +0 -33
- hammad/formatting/json/__init__.py +0 -27
- hammad/formatting/json/converters.py +0 -158
- hammad/formatting/text/__init__.py +0 -63
- hammad/formatting/text/converters.py +0 -723
- hammad/formatting/text/markdown.py +0 -131
- hammad/formatting/yaml/__init__.py +0 -26
- hammad/formatting/yaml/converters.py +0 -5
- hammad/genai/__init__.py +0 -217
- hammad/genai/a2a/__init__.py +0 -32
- hammad/genai/a2a/workers.py +0 -552
- hammad/genai/agents/__init__.py +0 -59
- hammad/genai/agents/agent.py +0 -1973
- hammad/genai/agents/run.py +0 -1024
- hammad/genai/agents/types/__init__.py +0 -42
- hammad/genai/agents/types/agent_context.py +0 -13
- hammad/genai/agents/types/agent_event.py +0 -128
- hammad/genai/agents/types/agent_hooks.py +0 -220
- hammad/genai/agents/types/agent_messages.py +0 -31
- hammad/genai/agents/types/agent_response.py +0 -125
- hammad/genai/agents/types/agent_stream.py +0 -327
- hammad/genai/graphs/__init__.py +0 -125
- hammad/genai/graphs/_utils.py +0 -190
- hammad/genai/graphs/base.py +0 -1828
- hammad/genai/graphs/plugins.py +0 -316
- hammad/genai/graphs/types.py +0 -638
- hammad/genai/models/__init__.py +0 -1
- hammad/genai/models/embeddings/__init__.py +0 -43
- hammad/genai/models/embeddings/model.py +0 -226
- hammad/genai/models/embeddings/run.py +0 -163
- hammad/genai/models/embeddings/types/__init__.py +0 -37
- hammad/genai/models/embeddings/types/embedding_model_name.py +0 -75
- hammad/genai/models/embeddings/types/embedding_model_response.py +0 -76
- hammad/genai/models/embeddings/types/embedding_model_run_params.py +0 -66
- hammad/genai/models/embeddings/types/embedding_model_settings.py +0 -47
- hammad/genai/models/language/__init__.py +0 -57
- hammad/genai/models/language/model.py +0 -1098
- hammad/genai/models/language/run.py +0 -878
- hammad/genai/models/language/types/__init__.py +0 -40
- hammad/genai/models/language/types/language_model_instructor_mode.py +0 -47
- hammad/genai/models/language/types/language_model_messages.py +0 -28
- hammad/genai/models/language/types/language_model_name.py +0 -239
- hammad/genai/models/language/types/language_model_request.py +0 -127
- hammad/genai/models/language/types/language_model_response.py +0 -217
- hammad/genai/models/language/types/language_model_response_chunk.py +0 -56
- hammad/genai/models/language/types/language_model_settings.py +0 -89
- hammad/genai/models/language/types/language_model_stream.py +0 -600
- hammad/genai/models/language/utils/__init__.py +0 -28
- hammad/genai/models/language/utils/requests.py +0 -421
- hammad/genai/models/language/utils/structured_outputs.py +0 -135
- hammad/genai/models/model_provider.py +0 -4
- hammad/genai/models/multimodal.py +0 -47
- hammad/genai/models/reranking.py +0 -26
- hammad/genai/types/__init__.py +0 -1
- hammad/genai/types/base.py +0 -215
- hammad/genai/types/history.py +0 -290
- hammad/genai/types/tools.py +0 -507
- hammad/logging/__init__.py +0 -35
- hammad/logging/decorators.py +0 -834
- hammad/logging/logger.py +0 -1018
- hammad/mcp/__init__.py +0 -53
- hammad/mcp/client/__init__.py +0 -35
- hammad/mcp/client/client.py +0 -624
- hammad/mcp/client/client_service.py +0 -400
- hammad/mcp/client/settings.py +0 -178
- hammad/mcp/servers/__init__.py +0 -26
- hammad/mcp/servers/launcher.py +0 -1161
- hammad/runtime/__init__.py +0 -32
- hammad/runtime/decorators.py +0 -142
- hammad/runtime/run.py +0 -299
- hammad/service/__init__.py +0 -49
- hammad/service/create.py +0 -527
- hammad/service/decorators.py +0 -283
- hammad/types.py +0 -288
- hammad/typing/__init__.py +0 -435
- hammad/web/__init__.py +0 -43
- hammad/web/http/__init__.py +0 -1
- hammad/web/http/client.py +0 -944
- hammad/web/models.py +0 -275
- hammad/web/openapi/__init__.py +0 -1
- hammad/web/openapi/client.py +0 -740
- hammad/web/search/__init__.py +0 -1
- hammad/web/search/client.py +0 -1023
- hammad/web/utils.py +0 -472
- hammad_python-0.0.30.dist-info/RECORD +0 -135
- {hammad → ham}/py.typed +0 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.31.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.31.dist-info}/licenses/LICENSE +0 -0
hammad/cache/ttl_cache.py
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
"""hammad.cache.ttl_cache"""
|
2
|
-
|
3
|
-
from dataclasses import dataclass
|
4
|
-
from typing import Any, Literal, OrderedDict, Tuple
|
5
|
-
import time
|
6
|
-
|
7
|
-
from .base_cache import BaseCache
|
8
|
-
|
9
|
-
__all__ = ("TTLCache",)
|
10
|
-
|
11
|
-
|
12
|
-
@dataclass
|
13
|
-
class TTLCache(BaseCache):
|
14
|
-
"""
|
15
|
-
Thread-safe TTL cache implementation with LRU eviction.
|
16
|
-
|
17
|
-
Uses OrderedDict for efficient LRU tracking and automatic cleanup
|
18
|
-
of expired entries on access.
|
19
|
-
"""
|
20
|
-
|
21
|
-
maxsize: int = 1000
|
22
|
-
ttl: int = 3600
|
23
|
-
type: Literal["ttl"] = "ttl"
|
24
|
-
|
25
|
-
def __post_init__(self):
|
26
|
-
"""Initialize TTL cache after dataclass initialization."""
|
27
|
-
super().__post_init__()
|
28
|
-
self._cache: OrderedDict[str, Tuple[Any, float]] = OrderedDict()
|
29
|
-
|
30
|
-
def __contains__(self, key: str) -> bool:
|
31
|
-
"""Check if key exists and is not expired."""
|
32
|
-
if key in self._cache:
|
33
|
-
_value, timestamp = self._cache[key]
|
34
|
-
if time.time() - timestamp <= self.ttl:
|
35
|
-
self._cache.move_to_end(key)
|
36
|
-
return True
|
37
|
-
else:
|
38
|
-
# Expired, remove it
|
39
|
-
del self._cache[key]
|
40
|
-
return False
|
41
|
-
|
42
|
-
def __getitem__(self, key: str) -> Any:
|
43
|
-
"""Get value for key if not expired."""
|
44
|
-
if key in self:
|
45
|
-
return self._cache[key][0]
|
46
|
-
raise KeyError(key)
|
47
|
-
|
48
|
-
def __setitem__(self, key: str, value: Any) -> None:
|
49
|
-
"""Set value with current timestamp."""
|
50
|
-
if len(self._cache) >= self.maxsize and key not in self._cache:
|
51
|
-
self._cleanup_expired()
|
52
|
-
|
53
|
-
if len(self._cache) >= self.maxsize:
|
54
|
-
self._cache.popitem(last=False)
|
55
|
-
|
56
|
-
self._cache[key] = (value, time.time())
|
57
|
-
self._cache.move_to_end(key)
|
58
|
-
|
59
|
-
def _cleanup_expired(self) -> None:
|
60
|
-
"""Remove all expired entries."""
|
61
|
-
current_time = time.time()
|
62
|
-
|
63
|
-
expired_keys = [
|
64
|
-
k
|
65
|
-
for k, (_, ts) in list(self._cache.items())
|
66
|
-
if current_time - ts > self.ttl
|
67
|
-
]
|
68
|
-
for k in expired_keys:
|
69
|
-
if k in self._cache:
|
70
|
-
del self._cache[k]
|
71
|
-
|
72
|
-
def clear(self) -> None:
|
73
|
-
"""Clear all cached items."""
|
74
|
-
self._cache.clear()
|
hammad/cli/__init__.py
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
"""hammad.cli
|
2
|
-
|
3
|
-
Contains resources for styling rendered CLI content as well
|
4
|
-
as extensions / utilities for creating CLI interfaces."""
|
5
|
-
|
6
|
-
from typing import TYPE_CHECKING
|
7
|
-
from .._internal import create_getattr_importer
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from .plugins import print, input, animate
|
11
|
-
from .styles.settings import (
|
12
|
-
CLIStyleRenderableSettings,
|
13
|
-
CLIStyleBackgroundSettings,
|
14
|
-
CLIStyleLiveSettings,
|
15
|
-
)
|
16
|
-
|
17
|
-
|
18
|
-
__all__ = (
|
19
|
-
"print",
|
20
|
-
"input",
|
21
|
-
"animate",
|
22
|
-
"CLIStyleRenderableSettings",
|
23
|
-
"CLIStyleBackgroundSettings",
|
24
|
-
"CLIStyleLiveSettings",
|
25
|
-
)
|
26
|
-
|
27
|
-
|
28
|
-
__getattr__ = create_getattr_importer(__all__)
|
29
|
-
|
30
|
-
|
31
|
-
def __dir__() -> list[str]:
|
32
|
-
"""Get the attributes of the plugins module."""
|
33
|
-
return list(__all__)
|
hammad/cli/animations.py
DELETED
@@ -1,573 +0,0 @@
|
|
1
|
-
"""hammad.cli.animations"""
|
2
|
-
|
3
|
-
import time
|
4
|
-
import math
|
5
|
-
import random
|
6
|
-
import threading
|
7
|
-
from dataclasses import dataclass, field
|
8
|
-
from typing import Literal, Optional, List, overload, TYPE_CHECKING
|
9
|
-
|
10
|
-
from rich import get_console
|
11
|
-
from rich.console import Console, ConsoleOptions, RenderResult, RenderableType
|
12
|
-
from rich.live import Live
|
13
|
-
from rich.text import Text
|
14
|
-
from rich.panel import Panel
|
15
|
-
|
16
|
-
from .styles.types import (
|
17
|
-
CLIStyleColorName,
|
18
|
-
)
|
19
|
-
|
20
|
-
|
21
|
-
__all__ = (
|
22
|
-
"CLIAnimation",
|
23
|
-
"CLIAnimationState",
|
24
|
-
"CLIFlashingAnimation",
|
25
|
-
"CLIPulsingAnimation",
|
26
|
-
"CLIShakingAnimation",
|
27
|
-
"CLITypingAnimation",
|
28
|
-
"CLISpinningAnimation",
|
29
|
-
"CLIRainbowAnimation",
|
30
|
-
"animate_flashing",
|
31
|
-
"animate_pulsing",
|
32
|
-
"animate_shaking",
|
33
|
-
"animate_spinning",
|
34
|
-
"animate_rainbow",
|
35
|
-
"animate_typing",
|
36
|
-
)
|
37
|
-
|
38
|
-
|
39
|
-
@dataclass
|
40
|
-
class CLIAnimationState:
|
41
|
-
"""Internal class used to track the current state of an
|
42
|
-
animation."""
|
43
|
-
|
44
|
-
start_time: float = field(default_factory=time.time)
|
45
|
-
frame: int = 0
|
46
|
-
last_update: float | None = field(default_factory=time.time)
|
47
|
-
|
48
|
-
|
49
|
-
@dataclass
|
50
|
-
class CLIAnimation:
|
51
|
-
"""Base class for all animations within the `hammad` package,
|
52
|
-
this is used to integrate with rich's `__rich_console__` protocol."""
|
53
|
-
|
54
|
-
def __init__(
|
55
|
-
self,
|
56
|
-
# The object that this animation is being applied to.
|
57
|
-
renderable,
|
58
|
-
duration: Optional[float] = None,
|
59
|
-
) -> None:
|
60
|
-
self.renderable = renderable
|
61
|
-
"""The object that this animation is being applied to."""
|
62
|
-
self.duration = duration or 2.0
|
63
|
-
"""The duration of the animation in seconds (defaults to 2.0 seconds)."""
|
64
|
-
# Set last_update to None to ensure the animation is classified as
|
65
|
-
# the first update on init.
|
66
|
-
self.state = CLIAnimationState(last_update=None)
|
67
|
-
"""The current state of the animation."""
|
68
|
-
|
69
|
-
self.rich_console = get_console()
|
70
|
-
"""The rich console responsible for rendering the animation."""
|
71
|
-
self._animation_thread: threading.Thread | None = None
|
72
|
-
"""The thread responsible for running the animation."""
|
73
|
-
self._stop_animation = False
|
74
|
-
"""Flag used to stop the animation."""
|
75
|
-
|
76
|
-
def __rich_console__(
|
77
|
-
self,
|
78
|
-
console,
|
79
|
-
options,
|
80
|
-
):
|
81
|
-
"""Rich will call this automatically when rendering."""
|
82
|
-
if not self.is_complete:
|
83
|
-
console.force_terminal = True
|
84
|
-
if console.is_terminal:
|
85
|
-
# force referesh
|
86
|
-
console._is_alt_screen = False
|
87
|
-
|
88
|
-
current_time = time.time()
|
89
|
-
self.state.frame += 1
|
90
|
-
self.state.last_update = current_time
|
91
|
-
|
92
|
-
yield from self.apply(console, options)
|
93
|
-
|
94
|
-
def apply(self, console, options):
|
95
|
-
"""Used by subclasses to apply the animation."""
|
96
|
-
yield self.renderable
|
97
|
-
|
98
|
-
@property
|
99
|
-
def time_elapsed(self) -> float:
|
100
|
-
"""Time elapsed since the animation started."""
|
101
|
-
return time.time() - self.state.start_time
|
102
|
-
|
103
|
-
@property
|
104
|
-
def is_complete(self) -> bool:
|
105
|
-
"""Check if the animation is complete."""
|
106
|
-
if self.duration is None:
|
107
|
-
return False
|
108
|
-
return self.time_elapsed >= self.duration
|
109
|
-
|
110
|
-
def animate(
|
111
|
-
self,
|
112
|
-
duration: Optional[float] = None,
|
113
|
-
refresh_rate: int = 20,
|
114
|
-
transient: bool = True,
|
115
|
-
auto_refresh: bool = True,
|
116
|
-
console: Optional["Console"] = None,
|
117
|
-
screen: bool = False,
|
118
|
-
vertical_overflow: str = "ellipsis",
|
119
|
-
) -> None:
|
120
|
-
"""Animate this effect for the specified duration using Live."""
|
121
|
-
animate_duration = duration or self.duration or 3.0
|
122
|
-
|
123
|
-
# Use provided console or create new one
|
124
|
-
live_console = console or get_console()
|
125
|
-
|
126
|
-
with Live(
|
127
|
-
self,
|
128
|
-
console=live_console,
|
129
|
-
refresh_per_second=refresh_rate,
|
130
|
-
transient=transient,
|
131
|
-
auto_refresh=auto_refresh,
|
132
|
-
screen=screen,
|
133
|
-
vertical_overflow=vertical_overflow,
|
134
|
-
) as live:
|
135
|
-
start = time.time()
|
136
|
-
while time.time() - start < animate_duration:
|
137
|
-
time.sleep(0.05)
|
138
|
-
|
139
|
-
|
140
|
-
class CLIFlashingAnimation(CLIAnimation):
|
141
|
-
"""Makes any renderable flash/blink."""
|
142
|
-
|
143
|
-
def __init__(
|
144
|
-
self,
|
145
|
-
renderable,
|
146
|
-
speed: float = 0.5,
|
147
|
-
colors: Optional[List[CLIStyleColorName]] = None,
|
148
|
-
on_color: CLIStyleColorName = "white",
|
149
|
-
off_color: CLIStyleColorName = "dim white",
|
150
|
-
duration: Optional[float] = None,
|
151
|
-
):
|
152
|
-
super().__init__(renderable, duration)
|
153
|
-
self.speed = speed
|
154
|
-
# If colors is provided, use it; otherwise use on_color/off_color
|
155
|
-
if colors is not None:
|
156
|
-
self.colors = colors
|
157
|
-
else:
|
158
|
-
self.colors = [on_color, off_color]
|
159
|
-
|
160
|
-
def apply(self, console, options):
|
161
|
-
# Calculate which color to use based on time
|
162
|
-
color_index = int(self.time_elapsed / self.speed) % len(self.colors)
|
163
|
-
color = self.colors[color_index]
|
164
|
-
|
165
|
-
# Apply color to the renderable
|
166
|
-
if isinstance(self.renderable, str):
|
167
|
-
yield Text(self.renderable, style=color)
|
168
|
-
else:
|
169
|
-
# Wrap any renderable in the flash color
|
170
|
-
yield Text.from_markup(f"[{color}]{self.renderable}[/{color}]")
|
171
|
-
|
172
|
-
|
173
|
-
class CLIPulsingAnimation(CLIAnimation):
|
174
|
-
"""Makes any renderable pulse/breathe."""
|
175
|
-
|
176
|
-
def __init__(
|
177
|
-
self,
|
178
|
-
renderable: "RenderableType",
|
179
|
-
speed: float = 2.0,
|
180
|
-
min_opacity: float = 0.3,
|
181
|
-
max_opacity: float = 1.0,
|
182
|
-
color: "CLIStyleColorName" = "white",
|
183
|
-
duration: Optional[float] = None,
|
184
|
-
):
|
185
|
-
super().__init__(renderable, duration)
|
186
|
-
self.speed = speed
|
187
|
-
self.min_opacity = min_opacity
|
188
|
-
self.max_opacity = max_opacity
|
189
|
-
self.color = color
|
190
|
-
|
191
|
-
def apply(self, console: "Console", options: "ConsoleOptions") -> "RenderResult":
|
192
|
-
# Calculate opacity using sine wave
|
193
|
-
opacity = self.min_opacity + (self.max_opacity - self.min_opacity) * (
|
194
|
-
0.5 + 0.5 * math.sin(self.time_elapsed * self.speed)
|
195
|
-
)
|
196
|
-
|
197
|
-
# Convert opacity to RGB values for fading effect
|
198
|
-
rgb_value = int(opacity * 255)
|
199
|
-
fade_color = f"rgb({rgb_value},{rgb_value},{rgb_value})"
|
200
|
-
|
201
|
-
if isinstance(self.renderable, str):
|
202
|
-
yield Text(self.renderable, style=fade_color)
|
203
|
-
else:
|
204
|
-
# For Panel and other renderables, we need to use opacity styling
|
205
|
-
if isinstance(self.renderable, Panel):
|
206
|
-
# Create a new panel with modified style
|
207
|
-
new_panel = Panel(
|
208
|
-
self.renderable.renderable,
|
209
|
-
title=self.renderable.title,
|
210
|
-
title_align=self.renderable.title_align,
|
211
|
-
subtitle=self.renderable.subtitle,
|
212
|
-
subtitle_align=self.renderable.subtitle_align,
|
213
|
-
box=self.renderable.box,
|
214
|
-
style=fade_color,
|
215
|
-
border_style=fade_color,
|
216
|
-
expand=self.renderable.expand,
|
217
|
-
padding=self.renderable.padding,
|
218
|
-
width=self.renderable.width,
|
219
|
-
height=self.renderable.height,
|
220
|
-
)
|
221
|
-
yield new_panel
|
222
|
-
else:
|
223
|
-
# For other renderables, wrap in a panel with the fade effect
|
224
|
-
yield Panel(self.renderable, style=fade_color, border_style=fade_color)
|
225
|
-
|
226
|
-
|
227
|
-
class CLIShakingAnimation(CLIAnimation):
|
228
|
-
"""Makes text shake/jitter."""
|
229
|
-
|
230
|
-
def __init__(
|
231
|
-
self,
|
232
|
-
renderable: "RenderableType",
|
233
|
-
intensity: int = 1,
|
234
|
-
speed: float = 0.1,
|
235
|
-
duration: Optional[float] = None,
|
236
|
-
):
|
237
|
-
super().__init__(renderable, duration)
|
238
|
-
self.intensity = intensity
|
239
|
-
self.speed = speed
|
240
|
-
self.last_shake = 0
|
241
|
-
|
242
|
-
def apply(self, console: "Console", options: "ConsoleOptions") -> "RenderResult":
|
243
|
-
if self.time_elapsed - self.last_shake > self.speed:
|
244
|
-
self.last_shake = self.time_elapsed
|
245
|
-
|
246
|
-
# Add random spaces for shake effect
|
247
|
-
shake = " " * random.randint(0, self.intensity)
|
248
|
-
|
249
|
-
if isinstance(self.renderable, str):
|
250
|
-
yield Text(shake + self.renderable)
|
251
|
-
else:
|
252
|
-
yield Text(shake) + self.renderable
|
253
|
-
else:
|
254
|
-
# Keep previous position
|
255
|
-
yield self.renderable
|
256
|
-
|
257
|
-
|
258
|
-
class CLITypingAnimation(CLIAnimation):
|
259
|
-
"""Typewriter effect."""
|
260
|
-
|
261
|
-
def __init__(
|
262
|
-
self,
|
263
|
-
text: str,
|
264
|
-
speed: float = 0.05,
|
265
|
-
typing_speed: Optional[float] = None,
|
266
|
-
cursor: str = "█",
|
267
|
-
show_cursor: bool = True,
|
268
|
-
duration: Optional[float] = None,
|
269
|
-
):
|
270
|
-
super().__init__(text, duration)
|
271
|
-
self.text = text
|
272
|
-
# Use typing_speed if provided, otherwise use speed
|
273
|
-
self.speed = typing_speed if typing_speed is not None else speed
|
274
|
-
self.cursor = cursor
|
275
|
-
self.show_cursor = show_cursor
|
276
|
-
|
277
|
-
def apply(self, console: "Console", options: "ConsoleOptions") -> "RenderResult":
|
278
|
-
# Calculate how many characters to show
|
279
|
-
chars_to_show = int(self.time_elapsed / self.speed)
|
280
|
-
chars_to_show = min(chars_to_show, len(self.text))
|
281
|
-
|
282
|
-
if chars_to_show < len(self.text):
|
283
|
-
# Still typing - show cursor if enabled
|
284
|
-
text_content = self.text[:chars_to_show]
|
285
|
-
if self.show_cursor:
|
286
|
-
text_content += self.cursor
|
287
|
-
yield Text(text_content)
|
288
|
-
else:
|
289
|
-
# Finished typing - show complete text without cursor
|
290
|
-
yield Text(self.text)
|
291
|
-
|
292
|
-
|
293
|
-
class CLISpinningAnimation(CLIAnimation):
|
294
|
-
"""Spinner effect for any renderable."""
|
295
|
-
|
296
|
-
def __init__(
|
297
|
-
self,
|
298
|
-
renderable: "RenderableType",
|
299
|
-
frames: Optional[List[str]] = None,
|
300
|
-
speed: float = 0.1,
|
301
|
-
prefix: bool = True,
|
302
|
-
duration: Optional[float] = None,
|
303
|
-
):
|
304
|
-
super().__init__(renderable, duration)
|
305
|
-
self.frames = frames or ["⋅", "•", "●", "◉", "●", "•"]
|
306
|
-
self.speed = speed
|
307
|
-
self.prefix = prefix
|
308
|
-
|
309
|
-
def apply(self, console: "Console", options: "ConsoleOptions") -> "RenderResult":
|
310
|
-
frame_index = int(self.time_elapsed / self.speed) % len(self.frames)
|
311
|
-
spinner = self.frames[frame_index]
|
312
|
-
|
313
|
-
if isinstance(self.renderable, str):
|
314
|
-
if self.prefix:
|
315
|
-
yield Text(f"{spinner} {self.renderable}")
|
316
|
-
else:
|
317
|
-
yield Text(f"{self.renderable} {spinner}")
|
318
|
-
else:
|
319
|
-
if self.prefix:
|
320
|
-
yield Text(f"{spinner} ") + self.renderable
|
321
|
-
else:
|
322
|
-
yield self.renderable + Text(f" {spinner}")
|
323
|
-
|
324
|
-
|
325
|
-
RainbowPreset = Literal["classic", "bright", "pastel", "neon"]
|
326
|
-
|
327
|
-
RAINBOW_PRESETS = {
|
328
|
-
"classic": ["red", "yellow", "green", "cyan", "blue", "magenta"],
|
329
|
-
"bright": [
|
330
|
-
"bright_red",
|
331
|
-
"bright_yellow",
|
332
|
-
"bright_green",
|
333
|
-
"bright_cyan",
|
334
|
-
"bright_blue",
|
335
|
-
"bright_magenta",
|
336
|
-
],
|
337
|
-
"pastel": [
|
338
|
-
"light_pink3",
|
339
|
-
"khaki1",
|
340
|
-
"light_green",
|
341
|
-
"light_cyan1",
|
342
|
-
"light_blue",
|
343
|
-
"plum2",
|
344
|
-
],
|
345
|
-
"neon": ["hot_pink", "yellow1", "green1", "cyan1", "blue1", "magenta1"],
|
346
|
-
}
|
347
|
-
|
348
|
-
|
349
|
-
class CLIRainbowAnimation(CLIAnimation):
|
350
|
-
"""Rainbow color cycling effect."""
|
351
|
-
|
352
|
-
def __init__(
|
353
|
-
self,
|
354
|
-
renderable: "RenderableType",
|
355
|
-
speed: float = 0.5,
|
356
|
-
colors: "RainbowPreset | List[CLIStyleColorName] | None" = None,
|
357
|
-
duration: Optional[float] = None,
|
358
|
-
):
|
359
|
-
super().__init__(renderable, duration)
|
360
|
-
self.speed = speed
|
361
|
-
|
362
|
-
# Handle color selection
|
363
|
-
if colors is None:
|
364
|
-
colors = "classic"
|
365
|
-
|
366
|
-
if isinstance(colors, str) and colors in RAINBOW_PRESETS:
|
367
|
-
self.colors = RAINBOW_PRESETS[colors]
|
368
|
-
elif isinstance(colors, list):
|
369
|
-
self.colors = colors
|
370
|
-
else:
|
371
|
-
self.colors = RAINBOW_PRESETS["classic"]
|
372
|
-
|
373
|
-
def apply(self, console: "Console", options: "ConsoleOptions") -> "RenderResult":
|
374
|
-
if isinstance(self.renderable, str):
|
375
|
-
# Apply rainbow to each character
|
376
|
-
result = Text()
|
377
|
-
for i, char in enumerate(self.renderable):
|
378
|
-
color_offset = int(
|
379
|
-
(self.time_elapsed / self.speed + i) % len(self.colors)
|
380
|
-
)
|
381
|
-
color = self.colors[color_offset]
|
382
|
-
result.append(char, style=color)
|
383
|
-
yield result
|
384
|
-
else:
|
385
|
-
# Cycle through colors for the whole renderable
|
386
|
-
color_index = int(self.time_elapsed / self.speed) % len(self.colors)
|
387
|
-
yield Text.from_markup(
|
388
|
-
f"[{self.colors[color_index]}]{self.renderable}[/{self.colors[color_index]}]"
|
389
|
-
)
|
390
|
-
|
391
|
-
|
392
|
-
def animate_flashing(
|
393
|
-
renderable: "RenderableType",
|
394
|
-
duration: Optional[float] = None,
|
395
|
-
speed: float = 0.5,
|
396
|
-
on_color: CLIStyleColorName = "white",
|
397
|
-
off_color: CLIStyleColorName = "dim white",
|
398
|
-
refresh_rate: int = 20,
|
399
|
-
transient: bool = True,
|
400
|
-
) -> None:
|
401
|
-
"""Create and run a flashing animation on any renderable.
|
402
|
-
|
403
|
-
Args:
|
404
|
-
renderable: The object to animate (text, panel, etc.)
|
405
|
-
duration: Duration of the animation in seconds (defaults to 2.0)
|
406
|
-
speed: Speed of the flashing effect (defaults to 0.5)
|
407
|
-
on_color: Color when flashing "on" (defaults to "white")
|
408
|
-
off_color: Color when flashing "off" (defaults to "dim white")
|
409
|
-
refresh_rate: Refresh rate per second (defaults to 20)
|
410
|
-
transient: Whether to clear animation after completion (defaults to True)
|
411
|
-
|
412
|
-
Examples:
|
413
|
-
>>> animate_flashing("Alert!", duration=3.0, speed=0.3)
|
414
|
-
>>> animate_flashing(Panel("Warning"), on_color="red", off_color="dark_red")
|
415
|
-
"""
|
416
|
-
animation = CLIFlashingAnimation(
|
417
|
-
renderable,
|
418
|
-
duration=duration,
|
419
|
-
speed=speed,
|
420
|
-
on_color=on_color,
|
421
|
-
off_color=off_color,
|
422
|
-
)
|
423
|
-
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
424
|
-
|
425
|
-
|
426
|
-
def animate_pulsing(
|
427
|
-
renderable: "RenderableType",
|
428
|
-
duration: Optional[float] = None,
|
429
|
-
speed: float = 1.0,
|
430
|
-
min_opacity: float = 0.3,
|
431
|
-
max_opacity: float = 1.0,
|
432
|
-
refresh_rate: int = 20,
|
433
|
-
transient: bool = True,
|
434
|
-
) -> None:
|
435
|
-
"""Create and run a pulsing animation on any renderable.
|
436
|
-
|
437
|
-
Args:
|
438
|
-
renderable: The object to animate (text, panel, etc.)
|
439
|
-
duration: Duration of the animation in seconds (defaults to 2.0)
|
440
|
-
speed: Speed of the pulsing effect (defaults to 1.0)
|
441
|
-
min_opacity: Minimum opacity during pulse (defaults to 0.3)
|
442
|
-
max_opacity: Maximum opacity during pulse (defaults to 1.0)
|
443
|
-
refresh_rate: Refresh rate per second (defaults to 20)
|
444
|
-
transient: Whether to clear animation after completion (defaults to True)
|
445
|
-
|
446
|
-
Examples:
|
447
|
-
>>> animate_pulsing("Loading...", duration=5.0, speed=2.0)
|
448
|
-
>>> animate_pulsing(Panel("Status"), min_opacity=0.1, max_opacity=0.9)
|
449
|
-
"""
|
450
|
-
animation = CLIPulsingAnimation(
|
451
|
-
renderable,
|
452
|
-
duration=duration,
|
453
|
-
speed=speed,
|
454
|
-
min_opacity=min_opacity,
|
455
|
-
max_opacity=max_opacity,
|
456
|
-
)
|
457
|
-
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
458
|
-
|
459
|
-
|
460
|
-
def animate_shaking(
|
461
|
-
renderable: "RenderableType",
|
462
|
-
duration: Optional[float] = None,
|
463
|
-
intensity: int = 2,
|
464
|
-
speed: float = 10.0,
|
465
|
-
refresh_rate: int = 20,
|
466
|
-
transient: bool = True,
|
467
|
-
) -> None:
|
468
|
-
"""Create and run a shaking animation on any renderable.
|
469
|
-
|
470
|
-
Args:
|
471
|
-
renderable: The object to animate (text, panel, etc.)
|
472
|
-
duration: Duration of the animation in seconds (defaults to 2.0)
|
473
|
-
intensity: Intensity of the shake effect (defaults to 2)
|
474
|
-
speed: Speed of the shaking motion (defaults to 10.0)
|
475
|
-
refresh_rate: Refresh rate per second (defaults to 20)
|
476
|
-
transient: Whether to clear animation after completion (defaults to True)
|
477
|
-
|
478
|
-
Examples:
|
479
|
-
>>> animate_shaking("Error!", duration=1.5, intensity=3)
|
480
|
-
>>> animate_shaking(Panel("Critical Alert"), speed=15.0)
|
481
|
-
"""
|
482
|
-
animation = CLIShakingAnimation(
|
483
|
-
renderable, duration=duration, intensity=intensity, speed=speed
|
484
|
-
)
|
485
|
-
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
486
|
-
|
487
|
-
|
488
|
-
def animate_spinning(
|
489
|
-
renderable: "RenderableType",
|
490
|
-
duration: Optional[float] = None,
|
491
|
-
frames: Optional[List[str]] = None,
|
492
|
-
speed: float = 0.1,
|
493
|
-
prefix: bool = True,
|
494
|
-
refresh_rate: int = 20,
|
495
|
-
transient: bool = True,
|
496
|
-
) -> None:
|
497
|
-
"""Create and run a spinning animation on any renderable.
|
498
|
-
|
499
|
-
Args:
|
500
|
-
renderable: The object to animate (text, panel, etc.)
|
501
|
-
duration: Duration of the animation in seconds (defaults to 2.0)
|
502
|
-
frames: List of spinner frames (defaults to ["⋅", "•", "●", "◉", "●", "•"])
|
503
|
-
speed: Speed between frame changes (defaults to 0.1)
|
504
|
-
prefix: Whether to show spinner before text (defaults to True)
|
505
|
-
refresh_rate: Refresh rate per second (defaults to 20)
|
506
|
-
transient: Whether to clear animation after completion (defaults to True)
|
507
|
-
|
508
|
-
Examples:
|
509
|
-
>>> animate_spinning("Processing...", duration=10.0, speed=0.2)
|
510
|
-
>>> animate_spinning("Done", frames=["◐", "◓", "◑", "◒"], prefix=False)
|
511
|
-
"""
|
512
|
-
animation = CLISpinningAnimation(
|
513
|
-
renderable, duration=duration, frames=frames, speed=speed, prefix=prefix
|
514
|
-
)
|
515
|
-
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
516
|
-
|
517
|
-
|
518
|
-
def animate_rainbow(
|
519
|
-
renderable: "RenderableType",
|
520
|
-
duration: Optional[float] = None,
|
521
|
-
speed: float = 0.5,
|
522
|
-
refresh_rate: int = 20,
|
523
|
-
transient: bool = True,
|
524
|
-
) -> None:
|
525
|
-
"""Create and run a rainbow animation on any renderable.
|
526
|
-
|
527
|
-
Args:
|
528
|
-
renderable: The object to animate (text, panel, etc.)
|
529
|
-
duration: Duration of the animation in seconds (defaults to 2.0)
|
530
|
-
speed: Speed of the color cycling effect (defaults to 0.5)
|
531
|
-
refresh_rate: Refresh rate per second (defaults to 20)
|
532
|
-
transient: Whether to clear animation after completion (defaults to True)
|
533
|
-
|
534
|
-
Examples:
|
535
|
-
>>> animate_rainbow("Colorful Text!", duration=4.0, speed=1.0)
|
536
|
-
>>> animate_rainbow(Panel("Rainbow Panel"), speed=0.3)
|
537
|
-
"""
|
538
|
-
animation = CLIRainbowAnimation(renderable, duration=duration, speed=speed)
|
539
|
-
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
540
|
-
|
541
|
-
|
542
|
-
def animate_typing(
|
543
|
-
text: str,
|
544
|
-
duration: Optional[float] = None,
|
545
|
-
typing_speed: float = 0.05,
|
546
|
-
cursor: str = "▌",
|
547
|
-
show_cursor: bool = True,
|
548
|
-
refresh_rate: int = 20,
|
549
|
-
transient: bool = True,
|
550
|
-
) -> None:
|
551
|
-
"""Create and run a typewriter animation.
|
552
|
-
|
553
|
-
Args:
|
554
|
-
text: The text to type out
|
555
|
-
duration: Duration of the animation in seconds (defaults to 2.0)
|
556
|
-
typing_speed: Speed between character reveals (defaults to 0.05)
|
557
|
-
cursor: Cursor character to show (defaults to "▌")
|
558
|
-
show_cursor: Whether to show the typing cursor (defaults to True)
|
559
|
-
refresh_rate: Refresh rate per second (defaults to 20)
|
560
|
-
transient: Whether to clear animation after completion (defaults to True)
|
561
|
-
|
562
|
-
Examples:
|
563
|
-
>>> animate_typing("Hello, World!", typing_speed=0.1)
|
564
|
-
>>> animate_typing("Fast typing", duration=1.0, cursor="|", show_cursor=False)
|
565
|
-
"""
|
566
|
-
animation = CLITypingAnimation(
|
567
|
-
text,
|
568
|
-
duration=duration,
|
569
|
-
typing_speed=typing_speed,
|
570
|
-
cursor=cursor,
|
571
|
-
show_cursor=show_cursor,
|
572
|
-
)
|
573
|
-
animation.animate(duration=duration, refresh_rate=refresh_rate)
|