diff --git a/src/webui/static/config-editor.js b/src/webui/static/config-editor.js
index d0f69468..0ed612b3 100644
--- a/src/webui/static/config-editor.js
+++ b/src/webui/static/config-editor.js
@@ -15,6 +15,12 @@ function initJSONEditor() {
// Update line numbers
updateLineNumbers();
+ // Always enable line numbers since toggle was removed
+ const editorWrapper = document.querySelector('.editor-wrapper');
+ if (editorWrapper) {
+ editorWrapper.classList.add('with-line-numbers');
+ }
+
// Add event listeners
textarea.addEventListener('input', function() {
updateLineNumbers();
@@ -58,20 +64,7 @@ function syncLineNumbers() {
lineNumbersContainer.scrollTop = textarea.scrollTop;
}
-// Toggle line numbers visibility
-function toggleLineNumbers() {
- const checkbox = document.getElementById('line-numbers');
- const lineNumbersContainer = document.getElementById('line-numbers-container');
- const editorWrapper = document.querySelector('.editor-wrapper');
-
- if (checkbox.checked) {
- lineNumbersContainer.style.display = 'block';
- editorWrapper.classList.add('with-line-numbers');
- } else {
- lineNumbersContainer.style.display = 'none';
- editorWrapper.classList.remove('with-line-numbers');
- }
-}
+
// Handle special editor keydown events
function handleEditorKeydown(event) {
@@ -150,13 +143,13 @@ function updateEditorStatus() {
try {
if (textarea.value.trim() === '') {
- statusElement.textContent = 'Пустая конфигурация';
+ statusElement.innerHTML = 'Пустая конфигурация';
statusElement.className = 'status-text warning';
return;
}
JSON.parse(textarea.value);
- statusElement.textContent = 'Валидный JSON';
+ statusElement.innerHTML = '';
statusElement.className = 'status-text success';
} catch (error) {
statusElement.textContent = `Ошибка JSON: ${error.message}`;
@@ -176,7 +169,7 @@ function updateCursorPosition() {
const line = beforeCursor.split('\n').length;
const column = beforeCursor.length - beforeCursor.lastIndexOf('\n');
- cursorElement.textContent = `Строка ${line}, Столбец ${column}`;
+ cursorElement.innerHTML = `Строка ${line}, Столбец ${column}`;
}
// Format JSON with proper indentation
@@ -191,7 +184,10 @@ function formatJSON() {
textarea.value = formatted;
updateLineNumbers();
updateEditorStatus();
- showNotification('JSON отформатирован', 'success');
+ const formattedMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['json_formatted']
+ ? window.translations[currentLanguage]['json_formatted']
+ : 'JSON отформатирован';
+ showNotification(formattedMessage, 'success');
} catch (error) {
showNotification(`Ошибка форматирования: ${error.message}`, 'error');
}
@@ -205,7 +201,10 @@ function validateJSON() {
try {
JSON.parse(textarea.value);
- showNotification('JSON конфигурация валидна', 'success');
+ const validationMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['json_validation_success']
+ ? window.translations[currentLanguage]['json_validation_success']
+ : 'JSON конфигурация валидна';
+ showNotification(validationMessage, 'success');
} catch (error) {
showNotification(`Ошибка валидации JSON: ${error.message}`, 'error');
}
@@ -246,9 +245,15 @@ async function saveConfiguration(restart = false) {
if (result.success) {
currentConfigJSON = textarea.value;
if (restart) {
- showNotification('Конфигурация сохранена. Сервер перезапускается...', 'success');
+ const restartMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['config_saved_restarting']
+ ? window.translations[currentLanguage]['config_saved_restarting']
+ : 'Конфигурация сохранена. Сервер перезапускается...';
+ showNotification(restartMessage, 'success');
} else {
- showNotification('Конфигурация сохранена успешно', 'success');
+ const successMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['config_saved_success']
+ ? window.translations[currentLanguage]['config_saved_success']
+ : 'Конфигурация сохранена успешно';
+ showNotification(successMessage, 'success');
}
onConfigChange(); // Update button states
} else {
@@ -262,11 +267,22 @@ async function saveConfiguration(restart = false) {
// Save configuration and restart server
async function saveAndRestartConfiguration() {
- const confirmation = confirm('Сохранить конфигурацию и перезапустить сервер?\n\nВнимание: Соединение будет прервано на время перезапуска.');
+ const title = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['save_and_restart_title']
+ ? window.translations[currentLanguage]['save_and_restart_title']
+ : 'Сохранить и перезапустить';
- if (confirmation) {
- await saveConfiguration(true);
- }
+ const message = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['save_and_restart_message']
+ ? window.translations[currentLanguage]['save_and_restart_message']
+ : 'Сохранить конфигурацию и перезапустить сервер?\n\nВнимание: Соединение будет прервано на время перезапуска.';
+
+ showConfirmModal({
+ title: title,
+ message: message,
+ type: 'danger',
+ onConfirm: async () => {
+ await saveConfiguration(true);
+ }
+ });
}
// Refresh configuration from server
@@ -275,18 +291,31 @@ async function refreshConfiguration() {
// Check if there are unsaved changes
if (textarea && textarea.value !== currentConfigJSON) {
- const confirmation = confirm('У вас есть несохраненные изменения. Продолжить обновление?');
- if (!confirmation) {
- return;
- }
+ const title = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['refresh_unsaved_changes_title']
+ ? window.translations[currentLanguage]['refresh_unsaved_changes_title']
+ : 'Несохраненные изменения';
+
+ const message = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['refresh_unsaved_changes_message']
+ ? window.translations[currentLanguage]['refresh_unsaved_changes_message']
+ : 'У вас есть несохраненные изменения. Продолжить обновление?';
+
+ showConfirmModal({
+ title: title,
+ message: message,
+ type: 'warning',
+ onConfirm: async () => {
+ try {
+ await loadConfiguration();
+ showNotification('Конфигурация обновлена', 'success');
+ } catch (error) {
+ showNotification('Ошибка обновления конфигурации', 'error');
+ }
+ }
+ });
+ return;
}
- try {
- await loadConfiguration();
- showNotification('Конфигурация обновлена', 'success');
- } catch (error) {
- showNotification('Ошибка обновления конфигурации', 'error');
- }
+
}
// Update configuration status
diff --git a/src/webui/static/config.js b/src/webui/static/config.js
index 575c200c..9183037d 100644
--- a/src/webui/static/config.js
+++ b/src/webui/static/config.js
@@ -55,29 +55,10 @@ function renderConfigEditor() {
${configMeta.path}
${configMeta.format.toUpperCase()}
- ${configMeta.isWritable ? '✏️ Редактируемый' : '🔒 Только чтение'}
+ ${configMeta.isWritable ? '✏️ Редактируемый' : '🔒 Только чтение'}
-
-
-
-
- ${configMeta.isWritable ? `
-
-
- ` : ''}
-
@@ -85,10 +66,25 @@ function renderConfigEditor() {
diff --git a/src/webui/static/lang/en.js b/src/webui/static/lang/en.js
index 6b7f2a31..41d65886 100644
--- a/src/webui/static/lang/en.js
+++ b/src/webui/static/lang/en.js
@@ -108,7 +108,7 @@ window.translations.en = {
'format': 'Format',
'validate': 'Validate',
'json_configuration': 'JSON Configuration',
- 'line_numbers': 'Line Numbers',
+
'config_save_success': 'Configuration saved successfully',
'config_save_error': 'Error saving configuration',
'config_load_error': 'Error loading configuration',
@@ -116,5 +116,27 @@ window.translations.en = {
'config_save_confirm_title': 'Confirm Save',
'config_save_confirm_text': 'Are you sure you want to save changes to the configuration file?',
'config_backup_info': 'Backup will be created automatically',
- 'config_warning': '⚠️ Warning: Incorrect configuration may cause node failure!'
+ 'config_warning': '⚠️ Warning: Incorrect configuration may cause node failure!',
+
+ // Editor status translations
+ 'editable': 'Editable',
+ 'readonly': 'Read Only',
+ 'empty_config': 'Empty Configuration',
+ 'valid_json': 'Valid JSON',
+ 'line': 'Line',
+ 'column': 'Column',
+
+ // Configuration save messages
+ 'config_saved_restarting': 'Configuration saved. Server is restarting...',
+ 'config_saved_success': 'Configuration saved successfully',
+
+ // Validation messages
+ 'json_validation_success': 'JSON configuration is valid',
+ 'json_formatted': 'JSON formatted',
+
+ // Confirmation dialogs
+ 'save_and_restart_title': 'Save and Restart',
+ 'save_and_restart_message': 'Save configuration and restart server?\n\nWarning: Connection will be interrupted during restart.',
+ 'refresh_unsaved_changes_title': 'Unsaved Changes',
+ 'refresh_unsaved_changes_message': 'You have unsaved changes. Continue refreshing?'
};
\ No newline at end of file
diff --git a/src/webui/static/lang/ru.js b/src/webui/static/lang/ru.js
index b4b0d3c5..e9e0ed2f 100644
--- a/src/webui/static/lang/ru.js
+++ b/src/webui/static/lang/ru.js
@@ -108,7 +108,7 @@ window.translations.ru = {
'format': 'Форматировать',
'validate': 'Проверить',
'json_configuration': 'JSON Конфигурация',
- 'line_numbers': 'Номера строк',
+
'config_save_success': 'Конфигурация сохранена успешно',
'config_save_error': 'Ошибка сохранения конфигурации',
'config_load_error': 'Ошибка загрузки конфигурации',
@@ -116,5 +116,27 @@ window.translations.ru = {
'config_save_confirm_title': 'Подтверждение сохранения',
'config_save_confirm_text': 'Вы уверены, что хотите сохранить изменения в конфигурационный файл?',
'config_backup_info': 'Резервная копия будет создана автоматически',
- 'config_warning': '⚠️ Внимание: Неправильная конфигурация может привести к сбою работы узла!'
+ 'config_warning': '⚠️ Внимание: Неправильная конфигурация может привести к сбою работы узла!',
+
+ // Editor status translations
+ 'editable': 'Редактируемый',
+ 'readonly': 'Только чтение',
+ 'empty_config': 'Пустая конфигурация',
+ 'valid_json': 'Валидный JSON',
+ 'line': 'Строка',
+ 'column': 'Столбец',
+
+ // Configuration save messages
+ 'config_saved_restarting': 'Конфигурация сохранена. Сервер перезапускается...',
+ 'config_saved_success': 'Конфигурация сохранена успешно',
+
+ // Validation messages
+ 'json_validation_success': 'JSON конфигурация валидна',
+ 'json_formatted': 'JSON отформатирован',
+
+ // Confirmation dialogs
+ 'save_and_restart_title': 'Сохранить и перезапустить',
+ 'save_and_restart_message': 'Сохранить конфигурацию и перезапустить сервер?\n\nВнимание: Соединение будет прервано на время перезапуска.',
+ 'refresh_unsaved_changes_title': 'Несохраненные изменения',
+ 'refresh_unsaved_changes_message': 'У вас есть несохраненные изменения. Продолжить обновление?'
};
\ No newline at end of file
diff --git a/src/webui/static/style.css b/src/webui/static/style.css
index 81630570..1ebf7e1f 100644
--- a/src/webui/static/style.css
+++ b/src/webui/static/style.css
@@ -1539,7 +1539,6 @@ button[onclick="copyNodeKey()"]:hover {
/* Configuration Editor Styles */
.config-container {
- max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
@@ -1556,6 +1555,11 @@ button[onclick="copyNodeKey()"]:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
+.config-info {
+ display: flex;
+ flex-direction: column;
+}
+
.config-info h3 {
margin: 0 0 10px 0;
color: var(--text-heading);
@@ -1617,31 +1621,7 @@ button[onclick="copyNodeKey()"]:hover {
color: var(--text-warning);
}
-.config-actions {
- display: flex;
- gap: 10px;
-}
-.refresh-btn {
- background: var(--bg-nav-active);
- color: white;
-}
-
-.save-btn {
- background: var(--bg-success-dark);
- color: white;
-}
-
-.save-btn.modified {
- background: var(--bg-warning-dark);
- animation: pulse 2s infinite;
-}
-
-@keyframes pulse {
- 0% { opacity: 1; }
- 50% { opacity: 0.7; }
- 100% { opacity: 1; }
-}
/* Configuration Groups */
.config-groups {
@@ -1943,17 +1923,7 @@ input:checked + .slider:before {
gap: 15px;
}
-.line-numbers-toggle {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 0.9em;
- color: var(--text-muted);
-}
-.line-numbers-toggle input[type="checkbox"] {
- margin: 0;
-}
.editor-wrapper {
position: relative;
@@ -2050,24 +2020,34 @@ input:checked + .slider:before {
font-size: 0.8em;
}
-/* Additional action buttons */
-.format-btn {
- background: var(--bg-nav-active);
- color: white;
+/* Action buttons group */
+.action-buttons-group {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ justify-content: flex-end;
}
-.validate-btn {
- background: var(--bg-warning-dark);
- color: white;
+.action-buttons-group .action-btn {
+ background: var(--bg-nav-item);
+ color: var(--text-nav);
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: 500;
+ transition: all 0.2s ease;
+ margin: 0;
+ user-select: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
-.restart-btn {
- background: var(--text-error);
- color: white;
-}
-
-.restart-btn:hover {
- background: #c62828;
+.action-buttons-group .action-btn:hover {
+ background: var(--bg-nav-hover);
+ transform: translateY(-1px);
}
/* JSON Syntax highlighting (basic) */
@@ -2105,13 +2085,16 @@ input:checked + .slider:before {
padding-left: 45px;
}
- .config-actions {
- flex-wrap: wrap;
+ .action-buttons-group {
+ flex-direction: row;
+ gap: 4px;
+ justify-content: center;
}
- .action-btn {
- flex: 1;
- min-width: 120px;
+ .action-buttons-group .action-btn {
+ flex: none;
+ min-width: auto;
+ justify-content: center;
}
}
@@ -2127,4 +2110,14 @@ input:checked + .slider:before {
[data-theme="dark"] .json-editor {
background: var(--bg-info-card);
color: var(--text-body);
+}
+
+/* Dark theme support for action buttons */
+[data-theme="dark"] .action-buttons-group .action-btn {
+ color: var(--text-nav);
+}
+
+[data-theme="dark"] .action-buttons-group .action-btn:hover {
+ background: var(--bg-nav-hover);
+ transform: translateY(-1px);
}
\ No newline at end of file