virtui-manager 1.1.6__py3-none-any.whl → 1.3.0__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.
- {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/METADATA +1 -1
- virtui_manager-1.3.0.dist-info/RECORD +73 -0
- vmanager/constants.py +737 -108
- vmanager/dialog.css +24 -0
- vmanager/firmware_manager.py +4 -1
- vmanager/i18n.py +32 -0
- vmanager/libvirt_utils.py +132 -3
- vmanager/locales/de/LC_MESSAGES/virtui-manager.po +3012 -0
- vmanager/locales/fr/LC_MESSAGES/virtui-manager.mo +0 -0
- vmanager/locales/fr/LC_MESSAGES/virtui-manager.po +3124 -0
- vmanager/locales/it/LC_MESSAGES/virtui-manager.po +3012 -0
- vmanager/locales/virtui-manager.pot +3012 -0
- vmanager/modals/bulk_modals.py +13 -12
- vmanager/modals/cache_stats_modal.py +6 -5
- vmanager/modals/capabilities_modal.py +133 -0
- vmanager/modals/config_modal.py +25 -24
- vmanager/modals/cpu_mem_pc_modals.py +22 -21
- vmanager/modals/custom_migration_modal.py +10 -9
- vmanager/modals/disk_pool_modals.py +60 -59
- vmanager/modals/host_dashboard_modal.py +137 -0
- vmanager/modals/howto_disk_modal.py +2 -1
- vmanager/modals/howto_network_modal.py +2 -1
- vmanager/modals/howto_overlay_modal.py +2 -1
- vmanager/modals/howto_ssh_modal.py +2 -1
- vmanager/modals/howto_virtiofs_modal.py +2 -1
- vmanager/modals/input_modals.py +11 -10
- vmanager/modals/log_modal.py +2 -1
- vmanager/modals/migration_modals.py +20 -18
- vmanager/modals/network_modals.py +45 -36
- vmanager/modals/provisioning_modals.py +56 -56
- vmanager/modals/select_server_modals.py +8 -7
- vmanager/modals/selection_modals.py +7 -6
- vmanager/modals/server_modals.py +24 -23
- vmanager/modals/server_prefs_modals.py +78 -71
- vmanager/modals/utils_modals.py +10 -9
- vmanager/modals/virsh_modals.py +3 -2
- vmanager/modals/virtiofs_modals.py +6 -5
- vmanager/modals/vm_type_info_modal.py +2 -1
- vmanager/modals/vmanager_modals.py +19 -19
- vmanager/modals/vmcard_dialog.py +57 -57
- vmanager/modals/vmdetails_modals.py +115 -123
- vmanager/modals/xml_modals.py +3 -2
- vmanager/network_manager.py +4 -1
- vmanager/storage_manager.py +157 -39
- vmanager/utils.py +39 -6
- vmanager/vm_actions.py +28 -24
- vmanager/vm_queries.py +67 -25
- vmanager/vm_service.py +8 -5
- vmanager/vmanager.css +46 -0
- vmanager/vmanager.py +178 -112
- vmanager/vmcard.py +161 -159
- vmanager/webconsole_manager.py +21 -21
- virtui_manager-1.1.6.dist-info/RECORD +0 -65
- {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/WHEEL +0 -0
- {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/entry_points.txt +0 -0
- {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -8,7 +8,7 @@ from textual.widgets import (
|
|
|
8
8
|
Button, Input, Label,
|
|
9
9
|
RadioButton, RadioSet, Checkbox
|
|
10
10
|
)
|
|
11
|
-
from ..constants import VmStatus
|
|
11
|
+
from ..constants import VmStatus, ErrorMessages, SuccessMessages, ButtonLabels, StaticText
|
|
12
12
|
from .base_modals import BaseModal
|
|
13
13
|
from .input_modals import _sanitize_input
|
|
14
14
|
|
|
@@ -33,20 +33,20 @@ class FilterModal(BaseModal[None]):
|
|
|
33
33
|
|
|
34
34
|
def compose(self) -> ComposeResult:
|
|
35
35
|
with ScrollableContainer(id="filter-dialog"):
|
|
36
|
-
yield Label(
|
|
36
|
+
yield Label(StaticText.FILTER_BY_NAME)
|
|
37
37
|
with Vertical(classes="info-details"):
|
|
38
38
|
yield Input(placeholder="Enter VM name...", id="search-input", value=self.current_search)
|
|
39
39
|
with RadioSet(id="status-radioset"):
|
|
40
|
-
yield RadioButton(
|
|
41
|
-
yield RadioButton(
|
|
42
|
-
yield RadioButton(
|
|
43
|
-
yield RadioButton(
|
|
44
|
-
yield RadioButton(
|
|
45
|
-
yield RadioButton(
|
|
46
|
-
yield RadioButton(
|
|
40
|
+
yield RadioButton(StaticText.ALL, id=f"status_{VmStatus.DEFAULT}", value=self.current_status == VmStatus.DEFAULT)
|
|
41
|
+
yield RadioButton(StaticText.RUNNING, id=f"status_{VmStatus.RUNNING}", value=self.current_status == VmStatus.RUNNING)
|
|
42
|
+
yield RadioButton(StaticText.PAUSED, id=f"status_{VmStatus.PAUSED}", value=self.current_status == VmStatus.PAUSED)
|
|
43
|
+
yield RadioButton(StaticText.PMSUSPENDED, id=f"status_{VmStatus.PMSUSPENDED}", value=self.current_status == VmStatus.PMSUSPENDED)
|
|
44
|
+
yield RadioButton(StaticText.BLOCKED, id=f"status_{VmStatus.BLOCKED}", value=self.current_status == VmStatus.BLOCKED)
|
|
45
|
+
yield RadioButton(StaticText.STOPPED, id=f"status_{VmStatus.STOPPED}", value=self.current_status == VmStatus.STOPPED)
|
|
46
|
+
yield RadioButton(StaticText.MANUALLY_SELECTED, id=f"status_{VmStatus.SELECTED}", value=self.current_status == VmStatus.SELECTED)
|
|
47
47
|
|
|
48
48
|
if self.available_servers:
|
|
49
|
-
yield Label(
|
|
49
|
+
yield Label(StaticText.SELECT_SERVERS_TO_DISPLAY)
|
|
50
50
|
|
|
51
51
|
checkboxes = []
|
|
52
52
|
for i, server in enumerate(self.available_servers):
|
|
@@ -62,8 +62,8 @@ class FilterModal(BaseModal[None]):
|
|
|
62
62
|
yield grid
|
|
63
63
|
|
|
64
64
|
with Horizontal():
|
|
65
|
-
yield Button(
|
|
66
|
-
yield Button(
|
|
65
|
+
yield Button(ButtonLabels.APPLY, id="apply-btn", variant="success")
|
|
66
|
+
yield Button(ButtonLabels.CANCEL, id="cancel-btn")
|
|
67
67
|
|
|
68
68
|
def _get_selected_servers(self) -> list[str]:
|
|
69
69
|
selected = []
|
|
@@ -81,11 +81,11 @@ class FilterModal(BaseModal[None]):
|
|
|
81
81
|
try:
|
|
82
82
|
search_text, was_modified = _sanitize_input(search_text_raw)
|
|
83
83
|
except ValueError as e:
|
|
84
|
-
self.app.show_error_message(
|
|
84
|
+
self.app.show_error_message(ErrorMessages.SANITIZATION_ERROR_TEMPLATE.format(error=e))
|
|
85
85
|
return
|
|
86
86
|
|
|
87
87
|
if was_modified and search_text_raw != search_text: # Only show if actual chars were removed, not just empty
|
|
88
|
-
self.app.show_success_message(
|
|
88
|
+
self.app.show_success_message(SuccessMessages.INPUT_SANITIZED.format(original_input=search_text_raw, sanitized_input=search_text))
|
|
89
89
|
|
|
90
90
|
radioset = self.query_one(RadioSet)
|
|
91
91
|
status_button = radioset.pressed_button
|
|
@@ -105,11 +105,11 @@ class FilterModal(BaseModal[None]):
|
|
|
105
105
|
try:
|
|
106
106
|
search_text, was_modified = _sanitize_input(search_text_raw)
|
|
107
107
|
except ValueError as e:
|
|
108
|
-
self.app.show_error_message(
|
|
108
|
+
self.app.show_error_message(ErrorMessages.SANITIZATION_ERROR_TEMPLATE.format(error=e))
|
|
109
109
|
return
|
|
110
110
|
|
|
111
111
|
if was_modified and search_text_raw != search_text: # Only show if actual chars were removed, not just empty
|
|
112
|
-
self.app.show_success_message(
|
|
112
|
+
self.app.show_success_message(SuccessMessages.INPUT_SANITIZED.format(original_input=search_text_raw, sanitized_input=search_text))
|
|
113
113
|
|
|
114
114
|
radioset = self.query_one(RadioSet)
|
|
115
115
|
status_button = radioset.pressed_button
|
|
@@ -127,15 +127,15 @@ class CreateVMModal(BaseModal[dict | None]):
|
|
|
127
127
|
|
|
128
128
|
def compose(self) -> ComposeResult:
|
|
129
129
|
with Vertical(id="create-vm-dialog"):
|
|
130
|
-
yield Label(
|
|
130
|
+
yield Label(StaticText.CREATE_NEW_VM)
|
|
131
131
|
yield Input(placeholder="VM Name", id="vm-name-input", value="new_vm")
|
|
132
132
|
yield Input(placeholder="Memory (MB, e.g., 2048)", id="vm-memory-input", value="2048")
|
|
133
133
|
yield Input(placeholder="VCPU (e.g., 2)", id="vm-vcpu-input", value="2")
|
|
134
134
|
yield Input(placeholder="Disk Image Path (e.g., /var/lib/libvirt/images/myvm.qcow2)", id="vm-disk-input", value="/var/lib/libvirt/images/new_vm.qcow2")
|
|
135
135
|
# For simplicity, we won't add network details yet.
|
|
136
136
|
with Horizontal():
|
|
137
|
-
yield Button(
|
|
138
|
-
yield Button(
|
|
137
|
+
yield Button(ButtonLabels.CREATE, variant="primary", id="create-btn", classes="Buttonpage")
|
|
138
|
+
yield Button(ButtonLabels.CANCEL, variant="default", id="cancel-btn", classes="Buttonpage")
|
|
139
139
|
|
|
140
140
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
141
141
|
if event.button.id == "create-btn":
|
vmanager/modals/vmcard_dialog.py
CHANGED
|
@@ -11,10 +11,10 @@ from textual.widgets import (
|
|
|
11
11
|
)
|
|
12
12
|
from .input_modals import _sanitize_input
|
|
13
13
|
from .utils_modals import (
|
|
14
|
-
BaseDialog
|
|
14
|
+
BaseDialog
|
|
15
15
|
)
|
|
16
16
|
from ..config import load_config, save_config
|
|
17
|
-
from ..constants import ButtonLabels,
|
|
17
|
+
from ..constants import ButtonLabels, ErrorMessages, SuccessMessages, WarningMessages, StaticText
|
|
18
18
|
from ..vm_queries import is_qemu_agent_running
|
|
19
19
|
|
|
20
20
|
class DeleteVMConfirmationDialog(BaseDialog[tuple[bool, bool]]):
|
|
@@ -27,18 +27,18 @@ class DeleteVMConfirmationDialog(BaseDialog[tuple[bool, bool]]):
|
|
|
27
27
|
def compose(self):
|
|
28
28
|
yield Vertical(
|
|
29
29
|
Markdown(f"Are you sure you want to delete VM '{self.vm_name}'?", id="question"),
|
|
30
|
-
Checkbox(
|
|
30
|
+
Checkbox(StaticText.DELETE_STORAGE_VOLUMES, id="delete-storage-checkbox", value=True),
|
|
31
31
|
Label(""),
|
|
32
32
|
Horizontal(
|
|
33
|
-
Button(ButtonLabels.YES, variant="error", id=
|
|
34
|
-
Button(ButtonLabels.NO, variant="primary", id=
|
|
33
|
+
Button(ButtonLabels.YES, variant="error", id="yes", classes="dialog-buttons"),
|
|
34
|
+
Button(ButtonLabels.NO, variant="primary", id="no", classes="dialog-buttons"),
|
|
35
35
|
id="dialog-buttons",
|
|
36
36
|
),
|
|
37
37
|
id="delete-vm-dialog",
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
41
|
-
if event.button.id ==
|
|
41
|
+
if event.button.id == "yes":
|
|
42
42
|
delete_storage = self.query_one("#delete-storage-checkbox", Checkbox).value
|
|
43
43
|
self.dismiss((True, delete_storage))
|
|
44
44
|
else:
|
|
@@ -61,15 +61,15 @@ class ChangeNetworkDialog(BaseDialog[dict | None]):
|
|
|
61
61
|
network_options = [(str(net), str(net)) for net in self.networks]
|
|
62
62
|
|
|
63
63
|
with Vertical(id="dialog"):
|
|
64
|
-
yield Label(
|
|
64
|
+
yield Label(StaticText.SELECT_INTERFACE_AND_NETWORK)
|
|
65
65
|
yield Select(interface_options, id="interface-select")
|
|
66
66
|
yield Select(network_options, id="network-select")
|
|
67
67
|
with Horizontal(id="dialog-buttons"):
|
|
68
|
-
yield Button(ButtonLabels.CHANGE, variant="success", id=
|
|
69
|
-
yield Button(ButtonLabels.CANCEL, variant="error", id=
|
|
68
|
+
yield Button(ButtonLabels.CHANGE, variant="success", id="change")
|
|
69
|
+
yield Button(ButtonLabels.CANCEL, variant="error", id="cancel")
|
|
70
70
|
|
|
71
71
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
72
|
-
if event.button.id ==
|
|
72
|
+
if event.button.id == "change":
|
|
73
73
|
interface_select = self.query_one("#interface-select", Select)
|
|
74
74
|
network_select = self.query_one("#network-select", Select)
|
|
75
75
|
|
|
@@ -77,7 +77,7 @@ class ChangeNetworkDialog(BaseDialog[dict | None]):
|
|
|
77
77
|
new_network = network_select.value
|
|
78
78
|
|
|
79
79
|
if mac_address is Select.BLANK or new_network is Select.BLANK:
|
|
80
|
-
self.app.show_error_message(
|
|
80
|
+
self.app.show_error_message(ErrorMessages.PLEASE_SELECT_INTERFACE_AND_NETWORK)
|
|
81
81
|
return
|
|
82
82
|
|
|
83
83
|
self.dismiss({"mac_address": mac_address, "new_network": new_network})
|
|
@@ -89,21 +89,21 @@ class AdvancedCloneDialog(BaseDialog[dict | None]):
|
|
|
89
89
|
|
|
90
90
|
def compose(self):
|
|
91
91
|
yield Grid(
|
|
92
|
-
Label(
|
|
92
|
+
Label(StaticText.ENTER_BASE_NAME),
|
|
93
93
|
Input(placeholder="new_vm_base_name", id="base_name_input", restrict=r"[a-zA-Z0-9_-]*"),
|
|
94
|
-
Label(
|
|
94
|
+
Label(StaticText.SUFFIX_FOR_CLONE_NAMES),
|
|
95
95
|
Input(placeholder="e.g., -clone", id="clone_suffix_input", restrict=r"[a-zA-Z0-9_-]*"),
|
|
96
|
-
Label(
|
|
96
|
+
Label(StaticText.NUMBER_OF_CLONES_TO_CREATE),
|
|
97
97
|
Input(value="1", id="clone_count_input", type="integer"),
|
|
98
|
-
Label(
|
|
98
|
+
Label(StaticText.DO_NOT_CLONE_STORAGE),
|
|
99
99
|
Checkbox("", id="skip_storage_checkbox", value=False),
|
|
100
|
-
Button(ButtonLabels.CLONE, variant="success", id=
|
|
101
|
-
Button(ButtonLabels.CANCEL, variant="error", id=
|
|
100
|
+
Button(ButtonLabels.CLONE, variant="success", id="clone"),
|
|
101
|
+
Button(ButtonLabels.CANCEL, variant="error", id="cancel"),
|
|
102
102
|
id="clone-dialog"
|
|
103
|
-
|
|
103
|
+
)
|
|
104
104
|
|
|
105
105
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
106
|
-
if event.button.id ==
|
|
106
|
+
if event.button.id == "clone":
|
|
107
107
|
base_name_input = self.query_one("#base_name_input", Input)
|
|
108
108
|
clone_count_input = self.query_one("#clone_count_input", Input)
|
|
109
109
|
clone_suffix_input = self.query_one("#clone_suffix_input", Input)
|
|
@@ -116,13 +116,13 @@ class AdvancedCloneDialog(BaseDialog[dict | None]):
|
|
|
116
116
|
try:
|
|
117
117
|
base_name, base_name_modified = _sanitize_input(base_name_raw)
|
|
118
118
|
if base_name_modified:
|
|
119
|
-
self.app.show_success_message(
|
|
119
|
+
self.app.show_success_message(SuccessMessages.BASE_NAME_SANITIZED_TEMPLATE.format(original=base_name_raw, sanitized=base_name))
|
|
120
120
|
except ValueError as e:
|
|
121
|
-
self.app.show_error_message(
|
|
121
|
+
self.app.show_error_message(ErrorMessages.SANITIZATION_ERROR_TEMPLATE.format(error=e))
|
|
122
122
|
return
|
|
123
123
|
|
|
124
124
|
if not base_name:
|
|
125
|
-
self.app.show_error_message(
|
|
125
|
+
self.app.show_error_message(ErrorMessages.BASE_NAME_EMPTY)
|
|
126
126
|
return
|
|
127
127
|
|
|
128
128
|
# Sanitize suffix only if it's provided, otherwise keep it empty string
|
|
@@ -131,9 +131,9 @@ class AdvancedCloneDialog(BaseDialog[dict | None]):
|
|
|
131
131
|
try:
|
|
132
132
|
clone_suffix, suffix_modified = _sanitize_input(clone_suffix_raw)
|
|
133
133
|
if suffix_modified:
|
|
134
|
-
self.app.show_success_message(
|
|
134
|
+
self.app.show_success_message(SuccessMessages.INPUT_SANITIZED_TEMPLATE.format(original=clone_suffix_raw, sanitized=clone_suffix))
|
|
135
135
|
except ValueError as e:
|
|
136
|
-
self.app.show_error_message(
|
|
136
|
+
self.app.show_error_message(ErrorMessages.INVALID_CHARS_IN_SUFFIX.format(error=e))
|
|
137
137
|
return
|
|
138
138
|
|
|
139
139
|
try:
|
|
@@ -141,11 +141,11 @@ class AdvancedCloneDialog(BaseDialog[dict | None]):
|
|
|
141
141
|
if clone_count < 1:
|
|
142
142
|
raise ValueError()
|
|
143
143
|
except ValueError:
|
|
144
|
-
self.app.show_error_message(
|
|
144
|
+
self.app.show_error_message(ErrorMessages.CLONE_COUNT_POSITIVE_INTEGER)
|
|
145
145
|
return
|
|
146
146
|
|
|
147
147
|
if clone_count > 1 and not clone_suffix:
|
|
148
|
-
self.app.show_error_message(
|
|
148
|
+
self.app.show_error_message(ErrorMessages.SUFFIX_MANDATORY_FOR_MULTIPLE_CLONES)
|
|
149
149
|
return
|
|
150
150
|
|
|
151
151
|
clone_storage = not skip_storage_checkbox.value
|
|
@@ -169,12 +169,12 @@ class RenameVMDialog(BaseDialog[str | None]):
|
|
|
169
169
|
|
|
170
170
|
def compose(self):
|
|
171
171
|
yield Vertical(
|
|
172
|
-
Label(
|
|
173
|
-
Label(
|
|
172
|
+
Label(StaticText.CURRENT_NAME.format(current_name=self.current_name)),
|
|
173
|
+
Label(StaticText.ENTER_NEW_VM_NAME, id="question"),
|
|
174
174
|
Input(placeholder="new_vm_name", restrict=r"[a-zA-Z0-9_-]*"),
|
|
175
175
|
Horizontal(
|
|
176
|
-
Button(ButtonLabels.RENAME, variant="success", id=
|
|
177
|
-
Button(ButtonLabels.CANCEL, variant="error", id=
|
|
176
|
+
Button(ButtonLabels.RENAME, variant="success", id="rename-button"),
|
|
177
|
+
Button(ButtonLabels.CANCEL, variant="error", id="cancel"),
|
|
178
178
|
id="dialog-buttons",
|
|
179
179
|
),
|
|
180
180
|
id="dialog",
|
|
@@ -182,21 +182,21 @@ class RenameVMDialog(BaseDialog[str | None]):
|
|
|
182
182
|
)
|
|
183
183
|
|
|
184
184
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
185
|
-
if event.button.id ==
|
|
185
|
+
if event.button.id == "rename-button":
|
|
186
186
|
input_widget = self.query_one(Input)
|
|
187
187
|
new_name_raw = input_widget.value
|
|
188
188
|
|
|
189
189
|
try:
|
|
190
190
|
new_name, was_modified = _sanitize_input(new_name_raw)
|
|
191
191
|
except ValueError as e:
|
|
192
|
-
self.app.show_error_message(
|
|
192
|
+
self.app.show_error_message(ErrorMessages.SANITIZATION_ERROR_TEMPLATE.format(error=e))
|
|
193
193
|
return
|
|
194
194
|
|
|
195
195
|
if was_modified:
|
|
196
|
-
self.app.show_success_message(
|
|
196
|
+
self.app.show_success_message(SuccessMessages.INPUT_SANITIZED_TEMPLATE.format(original=new_name_raw, sanitized=new_name))
|
|
197
197
|
|
|
198
198
|
if not new_name:
|
|
199
|
-
self.app.show_error_message(
|
|
199
|
+
self.app.show_error_message(ErrorMessages.VM_NAME_CANNOT_BE_EMPTY_RENAME)
|
|
200
200
|
return
|
|
201
201
|
|
|
202
202
|
error = self.validate_name(new_name)
|
|
@@ -220,7 +220,7 @@ class SelectSnapshotDialog(BaseDialog[str | None]):
|
|
|
220
220
|
yield Vertical(
|
|
221
221
|
Label(self.prompt, id="prompt-label"),
|
|
222
222
|
DataTable(id="snapshot-table"),
|
|
223
|
-
Button(ButtonLabels.CANCEL, variant="error", id=
|
|
223
|
+
Button(ButtonLabels.CANCEL, variant="error", id="cancel"),
|
|
224
224
|
id="dialog",
|
|
225
225
|
classes="snapshot-select-dialog"
|
|
226
226
|
)
|
|
@@ -243,7 +243,7 @@ class SelectSnapshotDialog(BaseDialog[str | None]):
|
|
|
243
243
|
self.dismiss(str(event.row_key.value))
|
|
244
244
|
|
|
245
245
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
246
|
-
if event.button.id ==
|
|
246
|
+
if event.button.id == "cancel":
|
|
247
247
|
self.dismiss(None)
|
|
248
248
|
|
|
249
249
|
class SnapshotNameDialog(BaseDialog[dict | None]):
|
|
@@ -259,22 +259,22 @@ class SnapshotNameDialog(BaseDialog[dict | None]):
|
|
|
259
259
|
agent_running = is_qemu_agent_running(self.domain) if self.domain else False
|
|
260
260
|
|
|
261
261
|
if not agent_running and self.domain:
|
|
262
|
-
|
|
262
|
+
self.app.show_warning_message(ErrorMessages.QEMU_GUEST_AGENT_RECOMMENDATION)
|
|
263
263
|
|
|
264
264
|
yield Vertical(
|
|
265
|
-
Label(
|
|
266
|
-
Label(
|
|
265
|
+
Label(StaticText.CURRENT_TIME.format(now=now), id="timestamp-label"),
|
|
266
|
+
Label(StaticText.ENTER_SNAPSHOT_NAME, id="question"),
|
|
267
267
|
Input(value=default_name, placeholder="snapshot_name", id="name-input", restrict=r"[a-zA-Z0-9_-]*"),
|
|
268
|
-
Label(
|
|
268
|
+
Label(StaticText.DESCRIPTION_OPTIONAL),
|
|
269
269
|
Input(placeholder="snapshot description", id="description-input"),
|
|
270
|
-
Checkbox(
|
|
270
|
+
Checkbox(StaticText.QUIESCE_GUEST,
|
|
271
271
|
value=agent_running,
|
|
272
272
|
disabled=not agent_running,
|
|
273
273
|
id="quiesce-checkbox",
|
|
274
274
|
tooltip="Pause the guest filesystem to ensure a clean snapshot. Requires QEMU Guest Agent to be running in the VM."),
|
|
275
275
|
Horizontal(
|
|
276
|
-
Button(ButtonLabels.CREATE, variant="success", id=
|
|
277
|
-
Button(ButtonLabels.CANCEL, variant="error", id=
|
|
276
|
+
Button(ButtonLabels.CREATE, variant="success", id="create"),
|
|
277
|
+
Button(ButtonLabels.CANCEL, variant="error", id="cancel"),
|
|
278
278
|
id="dialog-buttons",
|
|
279
279
|
),
|
|
280
280
|
id="dialog",
|
|
@@ -282,7 +282,7 @@ class SnapshotNameDialog(BaseDialog[dict | None]):
|
|
|
282
282
|
)
|
|
283
283
|
|
|
284
284
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
285
|
-
if event.button.id ==
|
|
285
|
+
if event.button.id == "create":
|
|
286
286
|
name_input = self.query_one("#name-input", Input)
|
|
287
287
|
description_input = self.query_one("#description-input", Input)
|
|
288
288
|
quiesce_checkbox = self.query_one("#quiesce-checkbox", Checkbox)
|
|
@@ -294,14 +294,14 @@ class SnapshotNameDialog(BaseDialog[dict | None]):
|
|
|
294
294
|
try:
|
|
295
295
|
snapshot_name, was_modified = _sanitize_input(snapshot_name_raw)
|
|
296
296
|
except ValueError as e:
|
|
297
|
-
self.app.show_error_message(
|
|
297
|
+
self.app.show_error_message(ErrorMessages.SANITIZATION_ERROR_TEMPLATE.format(error=e))
|
|
298
298
|
return
|
|
299
299
|
|
|
300
300
|
if was_modified:
|
|
301
|
-
self.app.show_success_message(
|
|
301
|
+
self.app.show_success_message(SuccessMessages.INPUT_SANITIZED_TEMPLATE.format(original=snapshot_name_raw, sanitized=snapshot_name))
|
|
302
302
|
|
|
303
303
|
if not snapshot_name:
|
|
304
|
-
self.app.show_error_message(
|
|
304
|
+
self.app.show_error_message(ErrorMessages.SNAPSHOT_NAME_CANNOT_BE_EMPTY)
|
|
305
305
|
return
|
|
306
306
|
|
|
307
307
|
error = self.validate_name(snapshot_name)
|
|
@@ -327,15 +327,15 @@ class WebConsoleDialog(BaseDialog[str | None]):
|
|
|
327
327
|
Markdown("Wesockify will handle a **single WebSocket** connection and exit. So it will be possible to **connect only ONE** time. If you disconnect you need to restart a new Web Console."),
|
|
328
328
|
Label(""),
|
|
329
329
|
Horizontal(
|
|
330
|
-
Button(ButtonLabels.STOP, variant="error", id=
|
|
331
|
-
Button(ButtonLabels.CLOSE, variant="primary", id=
|
|
330
|
+
Button(ButtonLabels.STOP, variant="error", id="stop"),
|
|
331
|
+
Button(ButtonLabels.CLOSE, variant="primary", id="close"),
|
|
332
332
|
id="dialog-buttons",
|
|
333
333
|
),
|
|
334
334
|
id="webconsole-dialog",
|
|
335
335
|
)
|
|
336
336
|
|
|
337
337
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
338
|
-
if event.button.id ==
|
|
338
|
+
if event.button.id == "stop":
|
|
339
339
|
self.dismiss("stop")
|
|
340
340
|
else:
|
|
341
341
|
self.dismiss(None)
|
|
@@ -351,7 +351,7 @@ class WebConsoleConfigDialog(BaseDialog[bool]):
|
|
|
351
351
|
|
|
352
352
|
def compose(self) -> ComposeResult:
|
|
353
353
|
with Vertical(id="webconsole-config-dialog"):
|
|
354
|
-
yield Label(
|
|
354
|
+
yield Label(StaticText.WEB_CONSOLE_CONFIGURATION, id="webconsole-config-title")
|
|
355
355
|
|
|
356
356
|
if self.is_remote:
|
|
357
357
|
remote_console_enabled = self.config.get('REMOTE_WEBCONSOLE', False)
|
|
@@ -365,7 +365,7 @@ class WebConsoleConfigDialog(BaseDialog[bool]):
|
|
|
365
365
|
switch_widget.add_class("switch-off")
|
|
366
366
|
|
|
367
367
|
yield Grid(
|
|
368
|
-
Label(
|
|
368
|
+
Label(StaticText.REMOTE),
|
|
369
369
|
switch_widget,
|
|
370
370
|
id="grid-remote-local"
|
|
371
371
|
)
|
|
@@ -384,10 +384,10 @@ class WebConsoleConfigDialog(BaseDialog[bool]):
|
|
|
384
384
|
id="grid-vnc-config"
|
|
385
385
|
)
|
|
386
386
|
else:
|
|
387
|
-
yield Markdown(
|
|
387
|
+
yield Markdown(StaticText.WEBCONSOLE_LOCAL_RUN)
|
|
388
388
|
|
|
389
|
-
yield Button(ButtonLabels.START, variant="primary", id=
|
|
390
|
-
yield Button(ButtonLabels.CANCEL, variant="default", id=
|
|
389
|
+
yield Button(ButtonLabels.START, variant="primary", id="start")
|
|
390
|
+
yield Button(ButtonLabels.CANCEL, variant="default", id="cancel")
|
|
391
391
|
|
|
392
392
|
def on_switch_changed(self, event: Switch.Changed) -> None:
|
|
393
393
|
if event.control.id == "remote-console-switch":
|
|
@@ -406,7 +406,7 @@ class WebConsoleConfigDialog(BaseDialog[bool]):
|
|
|
406
406
|
remote_opts.display = False
|
|
407
407
|
|
|
408
408
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
409
|
-
if event.button.id ==
|
|
409
|
+
if event.button.id == "start":
|
|
410
410
|
config_changed = False
|
|
411
411
|
if self.is_remote:
|
|
412
412
|
remote_switch = self.query_one("#remote-console-switch", Switch)
|
|
@@ -436,5 +436,5 @@ class WebConsoleConfigDialog(BaseDialog[bool]):
|
|
|
436
436
|
if config_changed:
|
|
437
437
|
save_config(self.config)
|
|
438
438
|
self.dismiss(True)
|
|
439
|
-
elif event.button.id ==
|
|
439
|
+
elif event.button.id == "cancel":
|
|
440
440
|
self.dismiss(False)
|