zjcode 0.0.1__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.
- deepagents_code/__init__.py +42 -0
- deepagents_code/__main__.py +6 -0
- deepagents_code/_ask_user_types.py +90 -0
- deepagents_code/_cli_context.py +96 -0
- deepagents_code/_constants.py +41 -0
- deepagents_code/_debug.py +148 -0
- deepagents_code/_debug_buffer.py +204 -0
- deepagents_code/_env_vars.py +411 -0
- deepagents_code/_fake_models.py +66 -0
- deepagents_code/_git.py +521 -0
- deepagents_code/_paths.py +69 -0
- deepagents_code/_server_config.py +576 -0
- deepagents_code/_session_stats.py +235 -0
- deepagents_code/_startup_error.py +45 -0
- deepagents_code/_testing_models.py +305 -0
- deepagents_code/_textual_patches.py +420 -0
- deepagents_code/_tool_stream.py +694 -0
- deepagents_code/_version.py +46 -0
- deepagents_code/agent.py +1976 -0
- deepagents_code/app.py +19239 -0
- deepagents_code/app.tcss +438 -0
- deepagents_code/approval_mode.py +131 -0
- deepagents_code/ask_user.py +306 -0
- deepagents_code/auth_display.py +185 -0
- deepagents_code/auth_store.py +545 -0
- deepagents_code/built_in_skills/__init__.py +5 -0
- deepagents_code/built_in_skills/remember/SKILL.md +118 -0
- deepagents_code/built_in_skills/skill-creator/SKILL.md +383 -0
- deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +366 -0
- deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +158 -0
- deepagents_code/client/__init__.py +1 -0
- deepagents_code/client/commands/__init__.py +1 -0
- deepagents_code/client/commands/auth.py +541 -0
- deepagents_code/client/commands/config.py +714 -0
- deepagents_code/client/commands/mcp.py +250 -0
- deepagents_code/client/commands/tools.py +416 -0
- deepagents_code/client/launch/__init__.py +1 -0
- deepagents_code/client/launch/server.py +978 -0
- deepagents_code/client/launch/server_manager.py +540 -0
- deepagents_code/client/non_interactive.py +1758 -0
- deepagents_code/client/remote_client.py +794 -0
- deepagents_code/clipboard.py +217 -0
- deepagents_code/command_registry.py +450 -0
- deepagents_code/config.py +4829 -0
- deepagents_code/config_manifest.py +1451 -0
- deepagents_code/configurable_model.py +577 -0
- deepagents_code/default_agent_prompt.md +12 -0
- deepagents_code/doctor.py +559 -0
- deepagents_code/editor.py +142 -0
- deepagents_code/event_bus.py +411 -0
- deepagents_code/extras_info.py +661 -0
- deepagents_code/file_ops.py +576 -0
- deepagents_code/formatting.py +135 -0
- deepagents_code/goal_rubric.py +497 -0
- deepagents_code/goal_tools.py +459 -0
- deepagents_code/hooks.py +348 -0
- deepagents_code/input.py +1041 -0
- deepagents_code/integrations/__init__.py +1 -0
- deepagents_code/integrations/openai_codex.py +551 -0
- deepagents_code/integrations/sandbox_config.py +198 -0
- deepagents_code/integrations/sandbox_factory.py +1124 -0
- deepagents_code/integrations/sandbox_provider.py +137 -0
- deepagents_code/integrations/sandbox_registry.py +350 -0
- deepagents_code/iterm_cursor_guide.py +176 -0
- deepagents_code/local_context.py +926 -0
- deepagents_code/main.py +3985 -0
- deepagents_code/managed_tools.py +642 -0
- deepagents_code/mcp_auth.py +1950 -0
- deepagents_code/mcp_config.py +176 -0
- deepagents_code/mcp_disabled.py +212 -0
- deepagents_code/mcp_login_service.py +281 -0
- deepagents_code/mcp_oauth_ui.py +199 -0
- deepagents_code/mcp_providers/__init__.py +23 -0
- deepagents_code/mcp_providers/_registry.py +39 -0
- deepagents_code/mcp_providers/base.py +133 -0
- deepagents_code/mcp_providers/github.py +102 -0
- deepagents_code/mcp_providers/slack.py +175 -0
- deepagents_code/mcp_tools.py +2427 -0
- deepagents_code/mcp_trust.py +207 -0
- deepagents_code/media_utils.py +826 -0
- deepagents_code/memory_guard.py +474 -0
- deepagents_code/model_config.py +4156 -0
- deepagents_code/notifications.py +247 -0
- deepagents_code/offload.py +402 -0
- deepagents_code/onboarding.py +223 -0
- deepagents_code/output.py +69 -0
- deepagents_code/paste_collapse.py +103 -0
- deepagents_code/project_utils.py +231 -0
- deepagents_code/py.typed +0 -0
- deepagents_code/reasoning_effort.py +641 -0
- deepagents_code/reliable_rubric.py +97 -0
- deepagents_code/resume_state.py +237 -0
- deepagents_code/server_graph.py +310 -0
- deepagents_code/session_end_summary.py +329 -0
- deepagents_code/sessions.py +1716 -0
- deepagents_code/skills/__init__.py +18 -0
- deepagents_code/skills/commands.py +1252 -0
- deepagents_code/skills/invocation.py +112 -0
- deepagents_code/skills/load.py +196 -0
- deepagents_code/skills/trust.py +547 -0
- deepagents_code/state_migration.py +136 -0
- deepagents_code/subagents.py +278 -0
- deepagents_code/system_prompt.md +204 -0
- deepagents_code/terminal_capabilities.py +115 -0
- deepagents_code/terminal_escape.py +287 -0
- deepagents_code/theme.py +891 -0
- deepagents_code/todo_list_prompt.md +12 -0
- deepagents_code/tool_catalog.py +509 -0
- deepagents_code/tool_display.py +367 -0
- deepagents_code/tools.py +516 -0
- deepagents_code/tui/__init__.py +1 -0
- deepagents_code/tui/textual_adapter.py +2553 -0
- deepagents_code/tui/widgets/__init__.py +9 -0
- deepagents_code/tui/widgets/_js_eval_display.py +139 -0
- deepagents_code/tui/widgets/_links.py +261 -0
- deepagents_code/tui/widgets/agent_selector.py +420 -0
- deepagents_code/tui/widgets/approval.py +602 -0
- deepagents_code/tui/widgets/ask_user.py +515 -0
- deepagents_code/tui/widgets/auth.py +1997 -0
- deepagents_code/tui/widgets/autocomplete.py +890 -0
- deepagents_code/tui/widgets/chat_input.py +3181 -0
- deepagents_code/tui/widgets/codex_auth.py +452 -0
- deepagents_code/tui/widgets/cwd_switch.py +242 -0
- deepagents_code/tui/widgets/debug_console.py +868 -0
- deepagents_code/tui/widgets/diff.py +252 -0
- deepagents_code/tui/widgets/effort_selector.py +189 -0
- deepagents_code/tui/widgets/goal_review.py +390 -0
- deepagents_code/tui/widgets/goal_status.py +50 -0
- deepagents_code/tui/widgets/history.py +194 -0
- deepagents_code/tui/widgets/install_confirm.py +248 -0
- deepagents_code/tui/widgets/launch_init.py +482 -0
- deepagents_code/tui/widgets/loading.py +227 -0
- deepagents_code/tui/widgets/mcp_login.py +539 -0
- deepagents_code/tui/widgets/mcp_reconnect.py +212 -0
- deepagents_code/tui/widgets/mcp_viewer.py +1634 -0
- deepagents_code/tui/widgets/message_store.py +999 -0
- deepagents_code/tui/widgets/messages.py +3846 -0
- deepagents_code/tui/widgets/model_selector.py +2343 -0
- deepagents_code/tui/widgets/notification_center.py +456 -0
- deepagents_code/tui/widgets/notification_detail.py +257 -0
- deepagents_code/tui/widgets/notification_settings.py +170 -0
- deepagents_code/tui/widgets/restart_prompt.py +158 -0
- deepagents_code/tui/widgets/skill_trust.py +131 -0
- deepagents_code/tui/widgets/startup_tip.py +91 -0
- deepagents_code/tui/widgets/status.py +781 -0
- deepagents_code/tui/widgets/subagent_panel.py +969 -0
- deepagents_code/tui/widgets/theme_selector.py +401 -0
- deepagents_code/tui/widgets/thread_selector.py +2564 -0
- deepagents_code/tui/widgets/tool_renderers.py +190 -0
- deepagents_code/tui/widgets/tool_widgets.py +304 -0
- deepagents_code/tui/widgets/update_available.py +311 -0
- deepagents_code/tui/widgets/update_confirm.py +184 -0
- deepagents_code/tui/widgets/update_progress.py +327 -0
- deepagents_code/tui/widgets/welcome.py +508 -0
- deepagents_code/turn_end_summary.py +522 -0
- deepagents_code/ui.py +858 -0
- deepagents_code/unicode_security.py +563 -0
- deepagents_code/update_check.py +3124 -0
- zjcode-0.0.1.data/data/deepagents_code/default_agent_prompt.md +12 -0
- zjcode-0.0.1.dist-info/METADATA +220 -0
- zjcode-0.0.1.dist-info/RECORD +163 -0
- zjcode-0.0.1.dist-info/WHEEL +4 -0
- zjcode-0.0.1.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""Progress modal for app self-update installs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections import deque
|
|
6
|
+
from typing import TYPE_CHECKING, ClassVar
|
|
7
|
+
|
|
8
|
+
from textual.binding import Binding, BindingType
|
|
9
|
+
from textual.containers import Horizontal, Vertical
|
|
10
|
+
from textual.screen import ModalScreen
|
|
11
|
+
from textual.widgets import Log, Static
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from textual.app import ComposeResult
|
|
17
|
+
from textual.timer import Timer
|
|
18
|
+
|
|
19
|
+
from deepagents_code import theme
|
|
20
|
+
from deepagents_code.config import get_glyphs, is_ascii_mode
|
|
21
|
+
from deepagents_code.tui.widgets.loading import Spinner
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UpdateProgressScreen(ModalScreen[None]):
|
|
25
|
+
"""Modal that shows self-update progress and a bounded log tail."""
|
|
26
|
+
|
|
27
|
+
BINDINGS: ClassVar[list[BindingType]] = [
|
|
28
|
+
Binding("d", "toggle_details", "Details", show=False),
|
|
29
|
+
Binding("c", "copy_log_path", "Copy log path", show=False),
|
|
30
|
+
Binding("q", "quit_app", "Quit", show=False),
|
|
31
|
+
Binding("escape", "cancel", "Close", show=False),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
CSS = """
|
|
35
|
+
UpdateProgressScreen {
|
|
36
|
+
align: center middle;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
UpdateProgressScreen > Vertical {
|
|
40
|
+
width: 78;
|
|
41
|
+
max-width: 92%;
|
|
42
|
+
height: auto;
|
|
43
|
+
max-height: 85%;
|
|
44
|
+
background: $surface;
|
|
45
|
+
border: solid $primary;
|
|
46
|
+
padding: 1 2;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
UpdateProgressScreen .up-title {
|
|
50
|
+
text-style: bold;
|
|
51
|
+
color: $primary;
|
|
52
|
+
text-align: center;
|
|
53
|
+
margin-bottom: 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
UpdateProgressScreen .up-status {
|
|
57
|
+
color: $text;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
UpdateProgressScreen .up-status-row {
|
|
61
|
+
height: auto;
|
|
62
|
+
margin-bottom: 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
UpdateProgressScreen .up-spinner {
|
|
66
|
+
width: auto;
|
|
67
|
+
color: $primary;
|
|
68
|
+
margin-right: 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
UpdateProgressScreen .up-details,
|
|
72
|
+
UpdateProgressScreen .up-log,
|
|
73
|
+
UpdateProgressScreen Log.up-tail,
|
|
74
|
+
UpdateProgressScreen .up-help {
|
|
75
|
+
color: $text-muted;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
UpdateProgressScreen .up-details {
|
|
79
|
+
margin-top: 1;
|
|
80
|
+
margin-bottom: 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
UpdateProgressScreen Log.up-tail {
|
|
84
|
+
height: 10;
|
|
85
|
+
min-height: 10;
|
|
86
|
+
max-height: 10;
|
|
87
|
+
background: $surface-lighten-1;
|
|
88
|
+
border: solid $surface-lighten-1;
|
|
89
|
+
overflow-x: hidden;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
UpdateProgressScreen .up-log {
|
|
93
|
+
margin-top: 1;
|
|
94
|
+
margin-bottom: 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
UpdateProgressScreen .up-help {
|
|
98
|
+
height: 1;
|
|
99
|
+
text-style: italic;
|
|
100
|
+
margin-top: 1;
|
|
101
|
+
text-align: center;
|
|
102
|
+
}
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(
|
|
106
|
+
self,
|
|
107
|
+
*,
|
|
108
|
+
latest: str,
|
|
109
|
+
command: str,
|
|
110
|
+
log_path: Path,
|
|
111
|
+
tail_limit: int = 30,
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Initialize the progress modal.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
latest: Version being installed.
|
|
117
|
+
command: Upgrade command being run.
|
|
118
|
+
log_path: Persisted log file path.
|
|
119
|
+
tail_limit: Maximum output lines to keep in memory.
|
|
120
|
+
"""
|
|
121
|
+
super().__init__()
|
|
122
|
+
self._latest = latest
|
|
123
|
+
self._command = command
|
|
124
|
+
self._log_path = log_path
|
|
125
|
+
self._tail_limit = tail_limit
|
|
126
|
+
self._tail: deque[str] = deque(maxlen=tail_limit)
|
|
127
|
+
self._details_visible = False
|
|
128
|
+
self._done = False
|
|
129
|
+
self._status = f"Installing v{latest}..."
|
|
130
|
+
self._done_glyph: str | None = None
|
|
131
|
+
self._status_widget: Static | None = None
|
|
132
|
+
self._spinner = Spinner()
|
|
133
|
+
self._spinner_widget: Static | None = None
|
|
134
|
+
self._spinner_timer: Timer | None = None
|
|
135
|
+
self._command_widget: Static | None = None
|
|
136
|
+
self._log_path_widget: Static | None = None
|
|
137
|
+
self._tail_widget: Log | None = None
|
|
138
|
+
self._help_widget: Static | None = None
|
|
139
|
+
self._copy_text: str | None = None
|
|
140
|
+
self._copy_label = "log path"
|
|
141
|
+
|
|
142
|
+
def compose(self) -> ComposeResult:
|
|
143
|
+
"""Compose the modal.
|
|
144
|
+
|
|
145
|
+
Yields:
|
|
146
|
+
Static widgets for status, command, output tail, log path, and help.
|
|
147
|
+
"""
|
|
148
|
+
with Vertical():
|
|
149
|
+
yield Static("Updating dcode", classes="up-title")
|
|
150
|
+
with Horizontal(classes="up-status-row"):
|
|
151
|
+
self._spinner_widget = Static(
|
|
152
|
+
self._spinner.current_frame(),
|
|
153
|
+
classes="up-spinner",
|
|
154
|
+
markup=False,
|
|
155
|
+
)
|
|
156
|
+
yield self._spinner_widget
|
|
157
|
+
self._status_widget = Static(
|
|
158
|
+
self._status, classes="up-status", markup=False
|
|
159
|
+
)
|
|
160
|
+
yield self._status_widget
|
|
161
|
+
self._command_widget = Static(
|
|
162
|
+
f"Running command: {self._command}",
|
|
163
|
+
classes="up-details",
|
|
164
|
+
markup=False,
|
|
165
|
+
)
|
|
166
|
+
self._command_widget.display = False
|
|
167
|
+
yield self._command_widget
|
|
168
|
+
self._tail_widget = Log(
|
|
169
|
+
highlight=False,
|
|
170
|
+
max_lines=self._tail_limit,
|
|
171
|
+
auto_scroll=True,
|
|
172
|
+
classes="up-tail",
|
|
173
|
+
)
|
|
174
|
+
self._tail_widget.display = False
|
|
175
|
+
yield self._tail_widget
|
|
176
|
+
self._log_path_widget = Static(
|
|
177
|
+
f"Log: {self._log_path}",
|
|
178
|
+
classes="up-log",
|
|
179
|
+
markup=False,
|
|
180
|
+
)
|
|
181
|
+
self._log_path_widget.display = False
|
|
182
|
+
yield self._log_path_widget
|
|
183
|
+
self._help_widget = Static(self._help_text(), classes="up-help")
|
|
184
|
+
yield self._help_widget
|
|
185
|
+
|
|
186
|
+
def on_mount(self) -> None:
|
|
187
|
+
"""Apply ASCII border when configured."""
|
|
188
|
+
if is_ascii_mode():
|
|
189
|
+
container = self.query_one(Vertical)
|
|
190
|
+
colors = theme.get_theme_colors(self)
|
|
191
|
+
container.styles.border = ("ascii", colors.primary)
|
|
192
|
+
self._spinner_timer = self.set_interval(0.1, self._update_spinner)
|
|
193
|
+
|
|
194
|
+
def append_line(self, line: str) -> None:
|
|
195
|
+
"""Append a command output line to the in-memory tail."""
|
|
196
|
+
self._tail.append(line)
|
|
197
|
+
if self._tail_widget is not None:
|
|
198
|
+
self._tail_widget.write_line(line)
|
|
199
|
+
|
|
200
|
+
def mark_success(self) -> None:
|
|
201
|
+
"""Render the completed-success state."""
|
|
202
|
+
self._done = True
|
|
203
|
+
self._done_glyph = get_glyphs().checkmark
|
|
204
|
+
self._status = (
|
|
205
|
+
f"Update complete. Quit and relaunch dcode to use v{self._latest}."
|
|
206
|
+
)
|
|
207
|
+
self._stop_spinner_timer()
|
|
208
|
+
self._refresh_status()
|
|
209
|
+
|
|
210
|
+
def mark_failure(self, command: str) -> None:
|
|
211
|
+
"""Render the completed-failure state.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
command: Manual command users can run to retry.
|
|
215
|
+
"""
|
|
216
|
+
self._done = True
|
|
217
|
+
self._done_glyph = get_glyphs().error
|
|
218
|
+
self._status = f"Update failed. Try manually: {command}"
|
|
219
|
+
self._details_visible = True
|
|
220
|
+
self._stop_spinner_timer()
|
|
221
|
+
self._refresh_status()
|
|
222
|
+
self._apply_details_visibility()
|
|
223
|
+
|
|
224
|
+
def mark_warning(
|
|
225
|
+
self,
|
|
226
|
+
warning: str,
|
|
227
|
+
*,
|
|
228
|
+
copy_text: str | None = None,
|
|
229
|
+
copy_label: str = "fix command",
|
|
230
|
+
) -> None:
|
|
231
|
+
"""Render a completed state that needs user action.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
warning: Durable warning text to show in the modal status.
|
|
235
|
+
copy_text: Optional text copied by the `c` key in warning state.
|
|
236
|
+
copy_label: User-facing name for the copied text.
|
|
237
|
+
"""
|
|
238
|
+
self._done = True
|
|
239
|
+
self._done_glyph = get_glyphs().warning
|
|
240
|
+
self._status = warning
|
|
241
|
+
self._copy_text = copy_text
|
|
242
|
+
self._copy_label = copy_label
|
|
243
|
+
self._details_visible = True
|
|
244
|
+
self._stop_spinner_timer()
|
|
245
|
+
self._refresh_status()
|
|
246
|
+
self._apply_details_visibility()
|
|
247
|
+
|
|
248
|
+
def action_toggle_details(self) -> None:
|
|
249
|
+
"""Show or hide the live log tail."""
|
|
250
|
+
self._details_visible = not self._details_visible
|
|
251
|
+
self._apply_details_visibility()
|
|
252
|
+
|
|
253
|
+
def action_cancel(self) -> None:
|
|
254
|
+
"""Close the modal only after the update command has finished."""
|
|
255
|
+
if self._done:
|
|
256
|
+
self.dismiss(None)
|
|
257
|
+
|
|
258
|
+
def action_quit_app(self) -> None:
|
|
259
|
+
"""Quit the app once the update command has finished."""
|
|
260
|
+
if self._done:
|
|
261
|
+
self.app.exit()
|
|
262
|
+
|
|
263
|
+
def action_copy_log_path(self) -> None:
|
|
264
|
+
"""Copy warning action text or the persisted log path."""
|
|
265
|
+
copy_text = self._copy_text
|
|
266
|
+
copy_label = self._copy_label
|
|
267
|
+
if copy_text is None:
|
|
268
|
+
if not self._details_visible:
|
|
269
|
+
return
|
|
270
|
+
copy_text = str(self._log_path)
|
|
271
|
+
copy_label = "log path"
|
|
272
|
+
if not copy_text:
|
|
273
|
+
return
|
|
274
|
+
self.app.copy_to_clipboard(copy_text)
|
|
275
|
+
self.app.notify(
|
|
276
|
+
f"Copied {copy_label}.",
|
|
277
|
+
severity="information",
|
|
278
|
+
timeout=3,
|
|
279
|
+
markup=False,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def _refresh_status(self) -> None:
|
|
283
|
+
if self._status_widget is not None:
|
|
284
|
+
self._status_widget.update(self._status)
|
|
285
|
+
if self._spinner_widget is not None:
|
|
286
|
+
if self._done:
|
|
287
|
+
self._spinner_widget.update(self._done_glyph or get_glyphs().checkmark)
|
|
288
|
+
self._spinner_widget.display = True
|
|
289
|
+
else:
|
|
290
|
+
self._spinner_widget.display = True
|
|
291
|
+
if self._help_widget is not None:
|
|
292
|
+
self._help_widget.update(self._help_text())
|
|
293
|
+
|
|
294
|
+
def _apply_details_visibility(self) -> None:
|
|
295
|
+
if self._tail_widget is None:
|
|
296
|
+
return
|
|
297
|
+
if self._command_widget is not None:
|
|
298
|
+
self._command_widget.display = self._details_visible
|
|
299
|
+
if self._log_path_widget is not None:
|
|
300
|
+
self._log_path_widget.display = self._details_visible
|
|
301
|
+
self._tail_widget.display = self._details_visible
|
|
302
|
+
if self._help_widget is not None:
|
|
303
|
+
self._help_widget.update(self._help_text())
|
|
304
|
+
|
|
305
|
+
def _stop_spinner_timer(self) -> None:
|
|
306
|
+
if self._spinner_timer is not None:
|
|
307
|
+
self._spinner_timer.stop()
|
|
308
|
+
self._spinner_timer = None
|
|
309
|
+
|
|
310
|
+
def _help_text(self) -> str:
|
|
311
|
+
glyphs = get_glyphs()
|
|
312
|
+
details = "Hide details" if self._details_visible else "Show details"
|
|
313
|
+
close = "Esc close" if self._done else "Esc close when complete"
|
|
314
|
+
parts = [f"d {details}", close]
|
|
315
|
+
if self._copy_text is not None:
|
|
316
|
+
parts.insert(1, f"c copy {self._copy_label}")
|
|
317
|
+
elif self._details_visible:
|
|
318
|
+
parts.insert(1, "c copy log path")
|
|
319
|
+
if self._done:
|
|
320
|
+
parts.append("q quit")
|
|
321
|
+
return f" {glyphs.bullet} ".join(parts)
|
|
322
|
+
|
|
323
|
+
def _update_spinner(self) -> None:
|
|
324
|
+
"""Advance the glyph spinner while the update is running."""
|
|
325
|
+
if self._done or self._spinner_widget is None:
|
|
326
|
+
return
|
|
327
|
+
self._spinner_widget.update(self._spinner.next_frame())
|