Control 설정 모달 확장

This commit is contained in:
seo
2026-06-26 03:04:50 +09:00
parent 41b1ba5336
commit 772984d9f0
5 changed files with 400 additions and 30 deletions
+139
View File
@@ -38,6 +38,12 @@
customServiceList: $('#customServiceList'),
noticeBaseline: $('#noticeBaseline'),
rebootBtn: $('#rebootBtn'),
settingsBtn: $('#settingsBtn'),
settingsDialog: $('#settingsDialog'),
settingsBody: $('#settingsBody'),
settingsClose: $('#settingsClose'),
settingsSave: $('#settingsSave'),
settingsReset: $('#settingsReset'),
translateBtn: $('#translateBtn'),
themeBtn: $('#themeBtn'),
customDialog: $('#customDialog'),
@@ -78,6 +84,8 @@
theme: 'dark',
batteryTooltipOpen: false,
batteryTooltipText: '',
settingsPayload: null,
settingsOpen: false,
};
const storageKeys = {
@@ -165,6 +173,13 @@
hide: 'Hide',
translateButton: 'Translate',
themeButton: 'Theme',
settingsButton: 'Settings',
settingsTitle: 'Settings',
settingsSaved: 'Settings saved',
settingsResetDone: 'Settings reset',
settingsSaveFailed: 'settings save failed',
settingsResetConfirm: 'Reset all Control settings to defaults?',
settingsDefault: 'default',
rebootButton: 'Reboot',
dialogAlert: 'Alert',
dialogConfirm: 'Confirm',
@@ -299,6 +314,13 @@
hide: '숨기기',
translateButton: '번역',
themeButton: '테마',
settingsButton: '설정',
settingsTitle: '설정',
settingsSaved: '설정을 저장했습니다.',
settingsResetDone: '설정을 기본값으로 되돌렸습니다.',
settingsSaveFailed: '설정 저장 실패',
settingsResetConfirm: 'Control 설정을 모두 기본값으로 되돌릴까요?',
settingsDefault: '기본값',
rebootButton: '재부팅',
dialogAlert: '알림',
dialogConfirm: '확인',
@@ -386,6 +408,7 @@
});
els.translateBtn && (els.translateBtn.textContent = t('translateButton'));
els.themeBtn && (els.themeBtn.textContent = t('themeButton'));
els.settingsBtn && (els.settingsBtn.textContent = t('settingsButton'));
els.rebootBtn && (els.rebootBtn.textContent = t('rebootButton'));
if (els.dmesgToggle) {
els.dmesgToggle.textContent = state.dmesgOpen ? t('hide') : t('show');
@@ -583,6 +606,102 @@
return json.data;
}
function renderSettings(payload = state.settingsPayload) {
if (!els.settingsBody || !payload) return;
state.settingsPayload = payload;
const groups = payload.groups || {};
const byGroup = {};
(payload.items || []).forEach(item => {
const group = item.group || 'etc';
if (!byGroup[group]) byGroup[group] = [];
byGroup[group].push(item);
});
els.settingsBody.innerHTML = Object.entries(byGroup).map(([group, items]) => `
<section class="settings-group">
<h4>${escapeHtml(groups[group] || group)}</h4>
<div class="settings-grid">
${items.map(item => `
<label class="settings-field">
<span class="settings-label">
<span>${escapeHtml(item.label || item.key)}</span>
<span>${escapeHtml(item.unit || '')}</span>
</span>
<span class="settings-control">
<input
data-setting-key="${escapeHtml(item.key)}"
type="number"
min="${escapeHtml(item.min ?? '')}"
max="${escapeHtml(item.max ?? '')}"
step="${escapeHtml(item.step ?? '1')}"
value="${escapeHtml(item.value ?? item.default ?? '')}">
<span class="settings-default">${t('settingsDefault')} ${escapeHtml(item.default ?? '')}</span>
</span>
</label>
`).join('')}
</div>
</section>
`).join('');
}
async function openSettings() {
if (!els.settingsDialog) return;
try {
renderSettings(await api('settings'));
} catch (e) {
console.error(e);
notice(e.message || t('refreshFailed'), 'error');
return;
}
state.settingsOpen = true;
els.settingsDialog.dataset.open = '1';
els.settingsDialog.setAttribute('aria-hidden', 'false');
}
function closeSettings() {
if (!els.settingsDialog) return;
state.settingsOpen = false;
delete els.settingsDialog.dataset.open;
els.settingsDialog.setAttribute('aria-hidden', 'true');
}
function currentSettingsFormValues() {
const values = {};
els.settingsBody?.querySelectorAll('[data-setting-key]').forEach(input => {
values[input.dataset.settingKey] = input.value;
});
return values;
}
async function saveSettings() {
try {
const data = await api('settings_save', {
settings: JSON.stringify(currentSettingsFormValues()),
});
render(data);
renderSettings(data.settings);
notice(t('settingsSaved'), 'success');
} catch (e) {
console.error(e);
notice(e.message || t('settingsSaveFailed'), 'error');
}
}
async function resetSettings() {
if (!await window.customConfirm(t('settingsResetConfirm'), { title: t('settingsTitle') })) {
return;
}
try {
const data = await api('settings_reset', {});
render(data);
renderSettings(data.settings);
notice(t('settingsResetDone'), 'success');
} catch (e) {
console.error(e);
notice(e.message || t('settingsSaveFailed'), 'error');
}
}
function websocketUrl() {
const scheme = location.protocol === 'https:' ? 'wss:' : 'ws:';
return `${scheme}//${location.host}/ws`;
@@ -1622,6 +1741,12 @@
}
function render(data) {
if (data.settings) {
state.settingsPayload = data.settings;
if (state.settingsOpen) {
renderSettings(data.settings);
}
}
renderTop(data);
renderSystemStatus(data);
renderWifi(data);
@@ -1866,6 +1991,15 @@
els.rebootBtn?.addEventListener('click', () => {
requestReboot();
});
els.settingsBtn?.addEventListener('click', openSettings);
els.settingsClose?.addEventListener('click', closeSettings);
els.settingsSave?.addEventListener('click', saveSettings);
els.settingsReset?.addEventListener('click', resetSettings);
els.settingsDialog?.addEventListener('click', event => {
if (event.target === els.settingsDialog) {
closeSettings();
}
});
els.translateBtn?.addEventListener('click', () => {
applyLang(state.lang === 'ko' ? 'en' : 'ko');
refreshStatus();
@@ -1884,6 +2018,11 @@
}
}
});
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && state.settingsOpen) {
closeSettings();
}
});
initPreferences();
connectControlSocket();