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.
Files changed (57) hide show
  1. {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/METADATA +1 -1
  2. virtui_manager-1.3.0.dist-info/RECORD +73 -0
  3. vmanager/constants.py +737 -108
  4. vmanager/dialog.css +24 -0
  5. vmanager/firmware_manager.py +4 -1
  6. vmanager/i18n.py +32 -0
  7. vmanager/libvirt_utils.py +132 -3
  8. vmanager/locales/de/LC_MESSAGES/virtui-manager.po +3012 -0
  9. vmanager/locales/fr/LC_MESSAGES/virtui-manager.mo +0 -0
  10. vmanager/locales/fr/LC_MESSAGES/virtui-manager.po +3124 -0
  11. vmanager/locales/it/LC_MESSAGES/virtui-manager.po +3012 -0
  12. vmanager/locales/virtui-manager.pot +3012 -0
  13. vmanager/modals/bulk_modals.py +13 -12
  14. vmanager/modals/cache_stats_modal.py +6 -5
  15. vmanager/modals/capabilities_modal.py +133 -0
  16. vmanager/modals/config_modal.py +25 -24
  17. vmanager/modals/cpu_mem_pc_modals.py +22 -21
  18. vmanager/modals/custom_migration_modal.py +10 -9
  19. vmanager/modals/disk_pool_modals.py +60 -59
  20. vmanager/modals/host_dashboard_modal.py +137 -0
  21. vmanager/modals/howto_disk_modal.py +2 -1
  22. vmanager/modals/howto_network_modal.py +2 -1
  23. vmanager/modals/howto_overlay_modal.py +2 -1
  24. vmanager/modals/howto_ssh_modal.py +2 -1
  25. vmanager/modals/howto_virtiofs_modal.py +2 -1
  26. vmanager/modals/input_modals.py +11 -10
  27. vmanager/modals/log_modal.py +2 -1
  28. vmanager/modals/migration_modals.py +20 -18
  29. vmanager/modals/network_modals.py +45 -36
  30. vmanager/modals/provisioning_modals.py +56 -56
  31. vmanager/modals/select_server_modals.py +8 -7
  32. vmanager/modals/selection_modals.py +7 -6
  33. vmanager/modals/server_modals.py +24 -23
  34. vmanager/modals/server_prefs_modals.py +78 -71
  35. vmanager/modals/utils_modals.py +10 -9
  36. vmanager/modals/virsh_modals.py +3 -2
  37. vmanager/modals/virtiofs_modals.py +6 -5
  38. vmanager/modals/vm_type_info_modal.py +2 -1
  39. vmanager/modals/vmanager_modals.py +19 -19
  40. vmanager/modals/vmcard_dialog.py +57 -57
  41. vmanager/modals/vmdetails_modals.py +115 -123
  42. vmanager/modals/xml_modals.py +3 -2
  43. vmanager/network_manager.py +4 -1
  44. vmanager/storage_manager.py +157 -39
  45. vmanager/utils.py +39 -6
  46. vmanager/vm_actions.py +28 -24
  47. vmanager/vm_queries.py +67 -25
  48. vmanager/vm_service.py +8 -5
  49. vmanager/vmanager.css +46 -0
  50. vmanager/vmanager.py +178 -112
  51. vmanager/vmcard.py +161 -159
  52. vmanager/webconsole_manager.py +21 -21
  53. virtui_manager-1.1.6.dist-info/RECORD +0 -65
  54. {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/WHEEL +0 -0
  55. {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/entry_points.txt +0 -0
  56. {virtui_manager-1.1.6.dist-info → virtui_manager-1.3.0.dist-info}/licenses/LICENSE +0 -0
  57. {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("Filter by Name")
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("All", id=f"status_{VmStatus.DEFAULT}", value=self.current_status == VmStatus.DEFAULT)
41
- yield RadioButton("Running", id=f"status_{VmStatus.RUNNING}", value=self.current_status == VmStatus.RUNNING)
42
- yield RadioButton("Paused", id=f"status_{VmStatus.PAUSED}", value=self.current_status == VmStatus.PAUSED)
43
- yield RadioButton("PMSuspended", id=f"status_{VmStatus.PMSUSPENDED}", value=self.current_status == VmStatus.PMSUSPENDED)
44
- yield RadioButton("Blocked", id=f"status_{VmStatus.BLOCKED}", value=self.current_status == VmStatus.BLOCKED)
45
- yield RadioButton("Stopped", id=f"status_{VmStatus.STOPPED}", value=self.current_status == VmStatus.STOPPED)
46
- yield RadioButton("Manually Selected", id=f"status_{VmStatus.SELECTED}", value=self.current_status == VmStatus.SELECTED)
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("Select Servers to Display")
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("Apply", id="apply-btn", variant="success")
66
- yield Button("Cancel", id="cancel-btn")
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(str(e))
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(f"Input sanitized: '{search_text_raw}' changed to '{search_text}'")
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(str(e))
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(f"Input sanitized: '{search_text_raw}' changed to '{search_text}'")
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("Create New VM")
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("Create", variant="primary", id="create-btn", classes="Buttonpage")
138
- yield Button("Cancel", variant="default", id="cancel-btn", classes="Buttonpage")
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":
@@ -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, show_warning_message
14
+ BaseDialog
15
15
  )
16
16
  from ..config import load_config, save_config
17
- from ..constants import ButtonLabels, ButtonIds
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("Delete storage volumes", id="delete-storage-checkbox", value=True),
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=ButtonIds.YES, classes="dialog-buttons"),
34
- Button(ButtonLabels.NO, variant="primary", id=ButtonIds.NO, classes="dialog-buttons"),
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 == ButtonIds.YES:
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("Select interface and new network")
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=ButtonIds.CHANGE)
69
- yield Button(ButtonLabels.CANCEL, variant="error", id=ButtonIds.CANCEL)
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 == ButtonIds.CHANGE:
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("Please select an interface and a network.")
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("Enter base name for new VM(s)"),
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("Suffix for clone names (e.g., _C)"),
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("Number of clones to create"),
96
+ Label(StaticText.NUMBER_OF_CLONES_TO_CREATE),
97
97
  Input(value="1", id="clone_count_input", type="integer"),
98
- Label("Do Not Clone storage"),
98
+ Label(StaticText.DO_NOT_CLONE_STORAGE),
99
99
  Checkbox("", id="skip_storage_checkbox", value=False),
100
- Button(ButtonLabels.CLONE, variant="success", id=ButtonIds.CLONE),
101
- Button(ButtonLabels.CANCEL, variant="error", id=ButtonIds.CANCEL),
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 == ButtonIds.CLONE:
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(f"Base name sanitized: [b]{base_name_raw}[/b] changed to [b]{base_name}[/b]")
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(str(e))
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("Base name cannot be empty.")
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(f"Suffix sanitized: [b]{clone_suffix_raw}[/b] changed to [b]{clone_suffix}[/b]")
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(f"Invalid characters in suffix: {e}")
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("Number of clones must be a positive integer.")
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("Suffix is mandatory when creating multiple clones.")
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(f"Current name: {self.current_name}"),
173
- Label("Enter new VM name", id="question"),
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=ButtonIds.RENAME_BUTTON),
177
- Button(ButtonLabels.CANCEL, variant="error", id=ButtonIds.CANCEL),
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 == ButtonIds.RENAME_BUTTON:
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(str(e))
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(f"Input sanitized: [b]{new_name_raw}[/b] changed to [b]{new_name}[/b]")
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("VM name cannot be empty.")
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=ButtonIds.CANCEL),
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 == ButtonIds.CANCEL:
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
- show_warning_message(self.app, "QEMU Guest Agent not detected. It is recommended to pause the VM before taking a snapshot.")
262
+ self.app.show_warning_message(ErrorMessages.QEMU_GUEST_AGENT_RECOMMENDATION)
263
263
 
264
264
  yield Vertical(
265
- Label(f"Current time: {now}", id="timestamp-label"),
266
- Label("Enter snapshot name", id="question"),
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("Description (optional)"),
268
+ Label(StaticText.DESCRIPTION_OPTIONAL),
269
269
  Input(placeholder="snapshot description", id="description-input"),
270
- Checkbox("Quiesce guest (requires agent)",
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=ButtonIds.CREATE),
277
- Button(ButtonLabels.CANCEL, variant="error", id=ButtonIds.CANCEL),
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 == ButtonIds.CREATE:
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(str(e))
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(f"Input sanitized: [b]{snapshot_name_raw}[/b] changed to [b]{snapshot_name}[/b]")
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("Snapshot name cannot be empty.")
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=ButtonIds.STOP),
331
- Button(ButtonLabels.CLOSE, variant="primary", id=ButtonIds.CLOSE),
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 == ButtonIds.STOP:
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("Web Console Configuration", id="webconsole-config-title")
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("Remote"),
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("Web console will run locally.")
387
+ yield Markdown(StaticText.WEBCONSOLE_LOCAL_RUN)
388
388
 
389
- yield Button(ButtonLabels.START, variant="primary", id=ButtonIds.START)
390
- yield Button(ButtonLabels.CANCEL, variant="default", id=ButtonIds.CANCEL)
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 == ButtonIds.START:
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 == ButtonIds.CANCEL:
439
+ elif event.button.id == "cancel":
440
440
  self.dismiss(False)