@anastops/cli 1.0.0 → 1.2.0
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.
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +8 -6
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +315 -17
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/ranger.d.ts +7 -2
- package/dist/commands/ranger.d.ts.map +1 -1
- package/dist/commands/ranger.js +99 -21
- package/dist/commands/ranger.js.map +1 -1
- package/dist/commands/uninstall.d.ts +16 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +206 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
- package/ranger-tui/pyproject.toml +51 -0
- package/ranger-tui/ranger_tui/__init__.py +5 -0
- package/ranger-tui/ranger_tui/__main__.py +16 -0
- package/ranger-tui/ranger_tui/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/__pycache__/__main__.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/__pycache__/accessibility.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/__pycache__/app.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/__pycache__/config.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/__pycache__/theme.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/accessibility.py +499 -0
- package/ranger-tui/ranger_tui/actions/__init__.py +13 -0
- package/ranger-tui/ranger_tui/actions/agent_actions.py +74 -0
- package/ranger-tui/ranger_tui/actions/session_actions.py +110 -0
- package/ranger-tui/ranger_tui/actions/task_actions.py +107 -0
- package/ranger-tui/ranger_tui/app.py +93 -0
- package/ranger-tui/ranger_tui/assets/ranger_head.png +0 -0
- package/ranger-tui/ranger_tui/config.py +100 -0
- package/ranger-tui/ranger_tui/data/__init__.py +16 -0
- package/ranger-tui/ranger_tui/data/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/data/__pycache__/client.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/data/__pycache__/models.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/data/client.py +858 -0
- package/ranger-tui/ranger_tui/data/models.py +151 -0
- package/ranger-tui/ranger_tui/screens/__init__.py +16 -0
- package/ranger-tui/ranger_tui/screens/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/screens/__pycache__/dashboard.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/screens/__pycache__/modals.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/screens/__pycache__/session.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/screens/__pycache__/task.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/screens/command_palette.py +357 -0
- package/ranger-tui/ranger_tui/screens/dashboard.py +232 -0
- package/ranger-tui/ranger_tui/screens/help.py +103 -0
- package/ranger-tui/ranger_tui/screens/modals.py +95 -0
- package/ranger-tui/ranger_tui/screens/session.py +289 -0
- package/ranger-tui/ranger_tui/screens/task.py +187 -0
- package/ranger-tui/ranger_tui/styles/ranger.tcss +254 -0
- package/ranger-tui/ranger_tui/theme.py +93 -0
- package/ranger-tui/ranger_tui/widgets/__init__.py +23 -0
- package/ranger-tui/ranger_tui/widgets/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/widgets/__pycache__/accessible.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/widgets/__pycache__/logo.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/widgets/__pycache__/logo_assets.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/widgets/__pycache__/ranger_image.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/widgets/__pycache__/sidebar.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/widgets/__pycache__/topbar.cpython-314.pyc +0 -0
- package/ranger-tui/ranger_tui/widgets/accessible.py +176 -0
- package/ranger-tui/ranger_tui/widgets/agents_table.py +151 -0
- package/ranger-tui/ranger_tui/widgets/header.py +141 -0
- package/ranger-tui/ranger_tui/widgets/logo.py +258 -0
- package/ranger-tui/ranger_tui/widgets/logo_assets.py +62 -0
- package/ranger-tui/ranger_tui/widgets/metrics_panel.py +121 -0
- package/ranger-tui/ranger_tui/widgets/ranger_image.py +91 -0
- package/ranger-tui/ranger_tui/widgets/sessions_table.py +191 -0
- package/ranger-tui/ranger_tui/widgets/sidebar.py +91 -0
- package/ranger-tui/ranger_tui/widgets/tasks_table.py +189 -0
- package/ranger-tui/ranger_tui/widgets/topbar.py +168 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/* Ranger TUI - Theme-aware responsive design */
|
|
2
|
+
|
|
3
|
+
/* ============ Global ============ */
|
|
4
|
+
|
|
5
|
+
Screen {
|
|
6
|
+
background: $background;
|
|
7
|
+
layout: vertical;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* ============ Main Layout ============ */
|
|
11
|
+
|
|
12
|
+
#main {
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 1fr;
|
|
15
|
+
layout: horizontal;
|
|
16
|
+
padding: 0 1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* ============ Tables ============ */
|
|
20
|
+
|
|
21
|
+
DataTable {
|
|
22
|
+
width: 1fr;
|
|
23
|
+
height: 100%;
|
|
24
|
+
background: $surface;
|
|
25
|
+
border: round $secondary-darken-2;
|
|
26
|
+
overflow-x: hidden;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
DataTable > .datatable--header {
|
|
30
|
+
text-style: bold;
|
|
31
|
+
color: $primary;
|
|
32
|
+
background: $panel;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
DataTable > .datatable--cursor {
|
|
36
|
+
background: $primary-darken-3;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
DataTable:focus > .datatable--cursor {
|
|
40
|
+
background: $primary-darken-1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
DataTable > .datatable--even-row {
|
|
44
|
+
background: $surface;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
DataTable > .datatable--odd-row {
|
|
48
|
+
background: $background;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* ============ Tabs ============ */
|
|
52
|
+
|
|
53
|
+
TabbedContent {
|
|
54
|
+
width: 1fr;
|
|
55
|
+
height: 100%;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ContentSwitcher {
|
|
59
|
+
height: 1fr;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Tabs {
|
|
63
|
+
background: $panel;
|
|
64
|
+
dock: top;
|
|
65
|
+
height: 3;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Tab {
|
|
69
|
+
color: $foreground-muted;
|
|
70
|
+
padding: 0 2;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
Tab:hover {
|
|
74
|
+
color: $foreground;
|
|
75
|
+
background: $surface;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Tab.-active {
|
|
79
|
+
color: $primary;
|
|
80
|
+
text-style: bold;
|
|
81
|
+
background: $surface;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
TabPane {
|
|
85
|
+
height: 1fr;
|
|
86
|
+
padding: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ============ RichLog ============ */
|
|
90
|
+
|
|
91
|
+
RichLog {
|
|
92
|
+
width: 100%;
|
|
93
|
+
height: 100%;
|
|
94
|
+
background: $surface;
|
|
95
|
+
border: round $secondary-darken-2;
|
|
96
|
+
padding: 1;
|
|
97
|
+
scrollbar-color: $secondary;
|
|
98
|
+
scrollbar-color-hover: $primary;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* ============ Modals ============ */
|
|
102
|
+
|
|
103
|
+
ModalScreen {
|
|
104
|
+
align: center middle;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#modal, #dialog {
|
|
108
|
+
width: 60;
|
|
109
|
+
height: auto;
|
|
110
|
+
background: $surface;
|
|
111
|
+
border: round $primary;
|
|
112
|
+
padding: 1 2;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#modal-title, #title {
|
|
116
|
+
text-style: bold;
|
|
117
|
+
color: $primary;
|
|
118
|
+
text-align: center;
|
|
119
|
+
margin-bottom: 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#modal Label, #dialog Label {
|
|
123
|
+
color: $foreground-muted;
|
|
124
|
+
margin-top: 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#modal Input, #dialog Input {
|
|
128
|
+
margin-bottom: 1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#modal Select, #dialog Select {
|
|
132
|
+
margin-bottom: 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#modal-buttons, #buttons {
|
|
136
|
+
margin-top: 1;
|
|
137
|
+
align: center middle;
|
|
138
|
+
layout: horizontal;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#modal-buttons Button, #buttons Button {
|
|
142
|
+
margin: 0 1;
|
|
143
|
+
min-width: 12;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ============ Inputs ============ */
|
|
147
|
+
|
|
148
|
+
Input {
|
|
149
|
+
background: $background;
|
|
150
|
+
border: tall $secondary-darken-2;
|
|
151
|
+
color: $foreground;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
Input:focus {
|
|
155
|
+
border: tall $primary;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
Input.-invalid {
|
|
159
|
+
border: tall $error;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* ============ Selects ============ */
|
|
163
|
+
|
|
164
|
+
Select {
|
|
165
|
+
background: $background;
|
|
166
|
+
border: tall $secondary-darken-2;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Select:focus {
|
|
170
|
+
border: tall $primary;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
SelectCurrent {
|
|
174
|
+
background: $background;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
SelectOverlay {
|
|
178
|
+
background: $surface;
|
|
179
|
+
border: round $secondary-darken-2;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ============ Buttons ============ */
|
|
183
|
+
|
|
184
|
+
Button {
|
|
185
|
+
background: $secondary;
|
|
186
|
+
color: $foreground;
|
|
187
|
+
border: none;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
Button:hover {
|
|
191
|
+
background: $secondary-lighten-1;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
Button.-primary {
|
|
195
|
+
background: $primary;
|
|
196
|
+
color: $background;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
Button.-primary:hover {
|
|
200
|
+
background: $primary-lighten-1;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ============ Footer ============ */
|
|
204
|
+
|
|
205
|
+
Footer {
|
|
206
|
+
background: $panel;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
Footer > .footer--key {
|
|
210
|
+
background: $secondary;
|
|
211
|
+
color: $primary;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
Footer > .footer--description {
|
|
215
|
+
color: $foreground-muted;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* ============ Scrollbars ============ */
|
|
219
|
+
|
|
220
|
+
* {
|
|
221
|
+
scrollbar-color: $secondary-darken-2;
|
|
222
|
+
scrollbar-color-hover: $secondary;
|
|
223
|
+
scrollbar-color-active: $primary;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* ============ Status Classes ============ */
|
|
227
|
+
|
|
228
|
+
.status-active {
|
|
229
|
+
color: $accent;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.status-success {
|
|
233
|
+
color: $success;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.status-error {
|
|
237
|
+
color: $error;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.status-warning {
|
|
241
|
+
color: $warning;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.status-muted {
|
|
245
|
+
color: $foreground-muted;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* ============ Notifications ============ */
|
|
249
|
+
|
|
250
|
+
Toast {
|
|
251
|
+
background: $surface;
|
|
252
|
+
border: round $secondary-darken-2;
|
|
253
|
+
color: $foreground;
|
|
254
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ranger TUI theme definition.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from textual.theme import Theme
|
|
6
|
+
|
|
7
|
+
# Custom theme based on Tokyo Night with orchestration-focused colors
|
|
8
|
+
RANGER_THEME = Theme(
|
|
9
|
+
name="ranger",
|
|
10
|
+
primary="#7aa2f7", # Blue - primary actions
|
|
11
|
+
secondary="#565f89", # Muted - secondary elements
|
|
12
|
+
accent="#bb9af7", # Purple - highlights
|
|
13
|
+
foreground="#a9b1d6", # Light text
|
|
14
|
+
background="#1a1b26", # Dark background
|
|
15
|
+
success="#9ece6a", # Green - completed
|
|
16
|
+
warning="#e0af68", # Orange - warnings
|
|
17
|
+
error="#f7768e", # Red - errors
|
|
18
|
+
surface="#24283b", # Elevated surfaces
|
|
19
|
+
panel="#16161e", # Sidebar/panels
|
|
20
|
+
dark=True,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Status symbols for consistent display
|
|
24
|
+
STATUS_SYMBOLS = {
|
|
25
|
+
# Session status
|
|
26
|
+
"active": "●",
|
|
27
|
+
"completed": "✓",
|
|
28
|
+
"archived": "◌",
|
|
29
|
+
|
|
30
|
+
# Task status
|
|
31
|
+
"pending": "○",
|
|
32
|
+
"queued": "◐",
|
|
33
|
+
"running": "●",
|
|
34
|
+
"failed": "✗",
|
|
35
|
+
"cancelled": "⊘",
|
|
36
|
+
|
|
37
|
+
# Agent status
|
|
38
|
+
"idle": "○",
|
|
39
|
+
"working": "●",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def format_status(status: str, include_label: bool = True) -> str:
|
|
44
|
+
"""Format status with symbol and optional label."""
|
|
45
|
+
symbol = STATUS_SYMBOLS.get(status, "?")
|
|
46
|
+
if include_label:
|
|
47
|
+
return f"{symbol} {status.capitalize()}"
|
|
48
|
+
return symbol
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_status_class(status: str) -> str:
|
|
52
|
+
"""Get CSS class for status styling."""
|
|
53
|
+
status_classes = {
|
|
54
|
+
"active": "status-active",
|
|
55
|
+
"running": "status-active",
|
|
56
|
+
"working": "status-active",
|
|
57
|
+
"completed": "status-success",
|
|
58
|
+
"archived": "status-muted",
|
|
59
|
+
"pending": "status-muted",
|
|
60
|
+
"idle": "status-muted",
|
|
61
|
+
"queued": "status-warning",
|
|
62
|
+
"failed": "status-error",
|
|
63
|
+
"cancelled": "status-error",
|
|
64
|
+
}
|
|
65
|
+
return status_classes.get(status, "")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Model display names with versions
|
|
69
|
+
MODEL_DISPLAY_NAMES = {
|
|
70
|
+
# Claude models
|
|
71
|
+
"claude-opus": "Opus 4",
|
|
72
|
+
"claude-sonnet": "Sonnet 4",
|
|
73
|
+
"claude-haiku": "Haiku 3.5",
|
|
74
|
+
# Gemini models
|
|
75
|
+
"gemini-default": "Gemini 2.5",
|
|
76
|
+
"gemini-2.5-pro": "Gemini 2.5 Pro",
|
|
77
|
+
"gemini-2.5-flash": "Gemini 2.5 Flash",
|
|
78
|
+
"gemini-3-pro": "Gemini 3 Pro",
|
|
79
|
+
# OpenAI models
|
|
80
|
+
"gpt-5": "GPT-5",
|
|
81
|
+
"gpt-5-codex": "GPT-5 Codex",
|
|
82
|
+
"gpt-5.2": "GPT-5.2",
|
|
83
|
+
# Cursor models
|
|
84
|
+
"sonnet-4.5": "Sonnet 4.5",
|
|
85
|
+
"opus-4.5": "Opus 4.5",
|
|
86
|
+
# Grok
|
|
87
|
+
"grok-default": "Grok 3",
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def format_model(model: str) -> str:
|
|
92
|
+
"""Format model name with version for display."""
|
|
93
|
+
return MODEL_DISPLAY_NAMES.get(model, model)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom widgets for Ranger TUI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from ranger_tui.widgets.topbar import TopBar
|
|
6
|
+
from ranger_tui.widgets.sidebar import Sidebar
|
|
7
|
+
from ranger_tui.widgets.logo import Logo, LogoBanner, LogoSplash
|
|
8
|
+
from ranger_tui.widgets.accessible import (
|
|
9
|
+
AccessibleWidget,
|
|
10
|
+
AccessibleDataTable,
|
|
11
|
+
AccessibilityChangedMessage,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"TopBar",
|
|
16
|
+
"Sidebar",
|
|
17
|
+
"Logo",
|
|
18
|
+
"LogoBanner",
|
|
19
|
+
"LogoSplash",
|
|
20
|
+
"AccessibleWidget",
|
|
21
|
+
"AccessibleDataTable",
|
|
22
|
+
"AccessibilityChangedMessage",
|
|
23
|
+
]
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Accessible widget mixin for enhanced accessibility support.
|
|
3
|
+
|
|
4
|
+
Provides accessibility features to widgets:
|
|
5
|
+
- ARIA-like metadata attachment
|
|
6
|
+
- Screen reader announcements
|
|
7
|
+
- Keyboard help generation
|
|
8
|
+
- Status announcements for dynamic updates
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Optional, Any, Dict
|
|
12
|
+
from textual.widget import Widget
|
|
13
|
+
from textual.message import Message
|
|
14
|
+
|
|
15
|
+
from ranger_tui.accessibility import (
|
|
16
|
+
AccessibilityMetadata,
|
|
17
|
+
AccessibilityHelper,
|
|
18
|
+
get_widget_metadata,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AccessibilityChangedMessage(Message):
|
|
23
|
+
"""Message for accessibility state changes."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, announcement: str) -> None:
|
|
26
|
+
super().__init__()
|
|
27
|
+
self.announcement = announcement
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AccessibleWidget(Widget):
|
|
31
|
+
"""Mixin for accessible widgets with ARIA-like metadata."""
|
|
32
|
+
|
|
33
|
+
_accessibility_metadata: Optional[AccessibilityMetadata] = None
|
|
34
|
+
_previous_announcement: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
37
|
+
super().__init__(**kwargs)
|
|
38
|
+
# Load metadata on initialization if available
|
|
39
|
+
if self.id:
|
|
40
|
+
self._accessibility_metadata = get_widget_metadata(self.id)
|
|
41
|
+
|
|
42
|
+
def set_accessibility_metadata(
|
|
43
|
+
self,
|
|
44
|
+
metadata: AccessibilityMetadata,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Set accessibility metadata for this widget."""
|
|
47
|
+
self._accessibility_metadata = metadata
|
|
48
|
+
|
|
49
|
+
def get_accessibility_metadata(self) -> Optional[AccessibilityMetadata]:
|
|
50
|
+
"""Get accessibility metadata for this widget."""
|
|
51
|
+
return self._accessibility_metadata
|
|
52
|
+
|
|
53
|
+
def get_accessibility_label(self) -> str:
|
|
54
|
+
"""Get the accessibility label for this widget."""
|
|
55
|
+
if self._accessibility_metadata:
|
|
56
|
+
return self._accessibility_metadata.label
|
|
57
|
+
return self.id or self.__class__.__name__
|
|
58
|
+
|
|
59
|
+
def get_accessibility_announcement(self) -> str:
|
|
60
|
+
"""Get a complete accessibility announcement for this widget."""
|
|
61
|
+
if not self._accessibility_metadata:
|
|
62
|
+
return ""
|
|
63
|
+
return self._accessibility_metadata.to_announcement()
|
|
64
|
+
|
|
65
|
+
def get_keyboard_help(self) -> str:
|
|
66
|
+
"""Get keyboard help for this widget."""
|
|
67
|
+
if not self._accessibility_metadata:
|
|
68
|
+
return ""
|
|
69
|
+
return AccessibilityHelper.announce_keyboard_help(self.id or "")
|
|
70
|
+
|
|
71
|
+
def announce_status_change(
|
|
72
|
+
self,
|
|
73
|
+
action: str,
|
|
74
|
+
details: Optional[Dict[str, Any]] = None,
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Announce a status change for accessibility."""
|
|
77
|
+
from ranger_tui.accessibility import format_accessibility_announcement
|
|
78
|
+
|
|
79
|
+
announcement = format_accessibility_announcement(action, details)
|
|
80
|
+
self.post_message(AccessibilityChangedMessage(announcement))
|
|
81
|
+
self._previous_announcement = announcement
|
|
82
|
+
|
|
83
|
+
def get_role(self) -> str:
|
|
84
|
+
"""Get the ARIA-like role for this widget."""
|
|
85
|
+
if self._accessibility_metadata:
|
|
86
|
+
return self._accessibility_metadata.role.value
|
|
87
|
+
return "widget"
|
|
88
|
+
|
|
89
|
+
def get_help_text(self) -> str:
|
|
90
|
+
"""Get the help text for this widget."""
|
|
91
|
+
if self._accessibility_metadata and self._accessibility_metadata.help_text:
|
|
92
|
+
return self._accessibility_metadata.help_text
|
|
93
|
+
return ""
|
|
94
|
+
|
|
95
|
+
def is_landmark(self) -> bool:
|
|
96
|
+
"""Check if this widget is a landmark region."""
|
|
97
|
+
if self._accessibility_metadata:
|
|
98
|
+
return self._accessibility_metadata.landmark is not None
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
def get_landmark_name(self) -> Optional[str]:
|
|
102
|
+
"""Get the landmark name if this is a landmark region."""
|
|
103
|
+
if self._accessibility_metadata:
|
|
104
|
+
return self._accessibility_metadata.landmark
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
def should_be_hidden_from_screen_readers(self) -> bool:
|
|
108
|
+
"""Check if this widget should be hidden from screen readers."""
|
|
109
|
+
if self._accessibility_metadata:
|
|
110
|
+
return self._accessibility_metadata.aria_hidden
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
def is_disabled(self) -> bool:
|
|
114
|
+
"""Check if this widget is disabled."""
|
|
115
|
+
if self._accessibility_metadata:
|
|
116
|
+
return self._accessibility_metadata.aria_disabled
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
def is_loading(self) -> bool:
|
|
120
|
+
"""Check if this widget is loading or processing."""
|
|
121
|
+
if self._accessibility_metadata:
|
|
122
|
+
return self._accessibility_metadata.aria_busy
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
def get_live_region_politeness(self) -> str:
|
|
126
|
+
"""Get the ARIA live region politeness level."""
|
|
127
|
+
if self._accessibility_metadata and self._accessibility_metadata.aria_live:
|
|
128
|
+
return self._accessibility_metadata.aria_live
|
|
129
|
+
return "off"
|
|
130
|
+
|
|
131
|
+
def should_announce_all_changes(self) -> bool:
|
|
132
|
+
"""Check if all widget changes should be announced."""
|
|
133
|
+
if self._accessibility_metadata:
|
|
134
|
+
return self._accessibility_metadata.aria_atomic
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class AccessibleDataTable(AccessibleWidget):
|
|
139
|
+
"""Accessible data table widget with ARIA-like metadata."""
|
|
140
|
+
|
|
141
|
+
def get_table_description(self) -> str:
|
|
142
|
+
"""Get description of the entire table."""
|
|
143
|
+
if self._accessibility_metadata:
|
|
144
|
+
return self._accessibility_metadata.description or ""
|
|
145
|
+
return ""
|
|
146
|
+
|
|
147
|
+
def get_column_headers_announcement(self) -> str:
|
|
148
|
+
"""Get announcement of all column headers."""
|
|
149
|
+
# This should be implemented by subclasses
|
|
150
|
+
return ""
|
|
151
|
+
|
|
152
|
+
def get_current_row_announcement(self, row_index: int) -> str:
|
|
153
|
+
"""Get announcement for the current row."""
|
|
154
|
+
# This should be implemented by subclasses
|
|
155
|
+
return ""
|
|
156
|
+
|
|
157
|
+
def announce_row_selection(self, row_index: int) -> None:
|
|
158
|
+
"""Announce row selection."""
|
|
159
|
+
announcement = f"Row {row_index + 1} selected. {self.get_current_row_announcement(row_index)}"
|
|
160
|
+
self.post_message(AccessibilityChangedMessage(announcement))
|
|
161
|
+
|
|
162
|
+
def announce_navigation(self, direction: str, row_index: int) -> None:
|
|
163
|
+
"""Announce navigation within the table."""
|
|
164
|
+
direction_text = "down" if direction == "down" else "up"
|
|
165
|
+
announcement = (
|
|
166
|
+
f"Moved {direction_text} to row {row_index + 1}. "
|
|
167
|
+
f"{self.get_current_row_announcement(row_index)}"
|
|
168
|
+
)
|
|
169
|
+
self.post_message(AccessibilityChangedMessage(announcement))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
__all__ = [
|
|
173
|
+
"AccessibleWidget",
|
|
174
|
+
"AccessibleDataTable",
|
|
175
|
+
"AccessibilityChangedMessage",
|
|
176
|
+
]
|