Refactor process picker into palette pattern, add notification app picker
- Refactor process-picker.ts into generic NamePalette with two concrete instances: ProcessPalette (running processes) and NotificationAppPalette (OS notification history apps) - Notification color strip app colors and filter list now use NotificationAppPalette (shows display names like "Telegram" instead of process names like "telegram.exe") - Fix case-insensitive matching for app_colors and app_filter_list in notification_stream.py - Compact browse/remove buttons in notification app color rows with proper search icon - Remove old inline process-picker HTML/CSS (replaced by palette overlay) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -507,6 +507,60 @@ function _buildConditionTypeItems() {
|
||||
}));
|
||||
}
|
||||
|
||||
/** Wire up the custom time-range picker inputs → sync to hidden fields. */
|
||||
function _wireTimeRangePicker(container: HTMLElement) {
|
||||
const startH = container.querySelector('.tr-start-h') as HTMLInputElement;
|
||||
const startM = container.querySelector('.tr-start-m') as HTMLInputElement;
|
||||
const endH = container.querySelector('.tr-end-h') as HTMLInputElement;
|
||||
const endM = container.querySelector('.tr-end-m') as HTMLInputElement;
|
||||
const hiddenStart = container.querySelector('.condition-start-time') as HTMLInputElement;
|
||||
const hiddenEnd = container.querySelector('.condition-end-time') as HTMLInputElement;
|
||||
if (!startH || !startM || !endH || !endM) return;
|
||||
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
|
||||
function clamp(input: HTMLInputElement, min: number, max: number) {
|
||||
let v = parseInt(input.value, 10);
|
||||
if (isNaN(v)) v = min;
|
||||
if (v < min) v = min;
|
||||
if (v > max) v = max;
|
||||
input.value = pad(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
function sync() {
|
||||
const sh = clamp(startH, 0, 23);
|
||||
const sm = clamp(startM, 0, 59);
|
||||
const eh = clamp(endH, 0, 23);
|
||||
const em = clamp(endM, 0, 59);
|
||||
hiddenStart.value = `${pad(sh)}:${pad(sm)}`;
|
||||
hiddenEnd.value = `${pad(eh)}:${pad(em)}`;
|
||||
}
|
||||
|
||||
[startH, startM, endH, endM].forEach(inp => {
|
||||
inp.addEventListener('focus', () => inp.select());
|
||||
inp.addEventListener('input', sync);
|
||||
inp.addEventListener('blur', sync);
|
||||
inp.addEventListener('keydown', (e) => {
|
||||
const isHour = inp.dataset.role === 'hour';
|
||||
const max = isHour ? 23 : 59;
|
||||
if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
let v = parseInt(inp.value, 10) || 0;
|
||||
inp.value = pad(v >= max ? 0 : v + 1);
|
||||
sync();
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
let v = parseInt(inp.value, 10) || 0;
|
||||
inp.value = pad(v <= 0 ? max : v - 1);
|
||||
sync();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
sync();
|
||||
}
|
||||
|
||||
function addAutomationConditionRow(condition: any) {
|
||||
const list = document.getElementById('automation-conditions-list');
|
||||
const row = document.createElement('div');
|
||||
@@ -545,18 +599,35 @@ function addAutomationConditionRow(condition: any) {
|
||||
if (type === 'time_of_day') {
|
||||
const startTime = data.start_time || '00:00';
|
||||
const endTime = data.end_time || '23:59';
|
||||
const [sh, sm] = startTime.split(':').map(Number);
|
||||
const [eh, em] = endTime.split(':').map(Number);
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
container.innerHTML = `
|
||||
<div class="condition-fields">
|
||||
<div class="condition-field">
|
||||
<label>${t('automations.condition.time_of_day.start_time')}</label>
|
||||
<input type="time" class="condition-start-time" value="${startTime}">
|
||||
</div>
|
||||
<div class="condition-field">
|
||||
<label>${t('automations.condition.time_of_day.end_time')}</label>
|
||||
<input type="time" class="condition-end-time" value="${endTime}">
|
||||
<input type="hidden" class="condition-start-time" value="${startTime}">
|
||||
<input type="hidden" class="condition-end-time" value="${endTime}">
|
||||
<div class="time-range-picker">
|
||||
<div class="time-range-slot">
|
||||
<span class="time-range-label">${t('automations.condition.time_of_day.start_time')}</span>
|
||||
<div class="time-range-input-wrap">
|
||||
<input type="number" class="tr-start-h" min="0" max="23" value="${sh}" data-role="hour">
|
||||
<span class="time-range-colon">:</span>
|
||||
<input type="number" class="tr-start-m" min="0" max="59" value="${pad(sm)}" data-role="minute">
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-range-arrow">→</div>
|
||||
<div class="time-range-slot">
|
||||
<span class="time-range-label">${t('automations.condition.time_of_day.end_time')}</span>
|
||||
<div class="time-range-input-wrap">
|
||||
<input type="number" class="tr-end-h" min="0" max="23" value="${eh}" data-role="hour">
|
||||
<span class="time-range-colon">:</span>
|
||||
<input type="number" class="tr-end-m" min="0" max="59" value="${pad(em)}" data-role="minute">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<small class="condition-always-desc">${t('automations.condition.time_of_day.overnight_hint')}</small>
|
||||
</div>`;
|
||||
_wireTimeRangePicker(container);
|
||||
return;
|
||||
}
|
||||
if (type === 'system_idle') {
|
||||
@@ -660,10 +731,6 @@ function addAutomationConditionRow(condition: any) {
|
||||
<button type="button" class="btn-browse-apps" title="${t('automations.condition.application.browse')}">${t('automations.condition.application.browse')}</button>
|
||||
</div>
|
||||
<textarea class="condition-apps" rows="3" placeholder="firefox.exe chrome.exe">${escapeHtml(appsValue)}</textarea>
|
||||
<div class="process-picker" style="display:none">
|
||||
<input type="text" class="process-picker-search" placeholder="${t('automations.condition.application.search')}" autocomplete="off">
|
||||
<div class="process-picker-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user