Unify process picker, improve notification CSS editor, remove notification led_count
- Extract shared process picker module (core/process-picker.js) used by both automation conditions and notification CSS app filter - Remove led_count property from notification CSS source (backend + frontend) - Replace comma-separated app filter with newline-separated textarea + browse - Inline color cycle add button (+) into the color row - Fix notification app color layout to horizontal rows Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,9 @@ import { showToast, showConfirm, setTabRefreshing } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
import { CardSection } from '../core/card-sections.js';
|
||||
import { updateTabBadge } from './tabs.js';
|
||||
import { ICON_SETTINGS, ICON_START, ICON_PAUSE, ICON_CLOCK, ICON_AUTOMATION, ICON_HELP, ICON_OK, ICON_TIMER, ICON_MONITOR, ICON_RADIO, ICON_SCENE } from '../core/icons.js';
|
||||
import { ICON_SETTINGS, ICON_START, ICON_PAUSE, ICON_CLOCK, ICON_AUTOMATION, ICON_HELP, ICON_OK, ICON_TIMER, ICON_MONITOR, ICON_RADIO, ICON_SCENE, ICON_CLONE } from '../core/icons.js';
|
||||
import { wrapCard } from '../core/card-colors.js';
|
||||
import { attachProcessPicker } from '../core/process-picker.js';
|
||||
import { csScenes, createSceneCard } from './scene-presets.js';
|
||||
|
||||
class AutomationEditorModal extends Modal {
|
||||
@@ -558,11 +559,8 @@ function addAutomationConditionRow(condition) {
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const browseBtn = container.querySelector('.btn-browse-apps');
|
||||
const picker = container.querySelector('.process-picker');
|
||||
browseBtn.addEventListener('click', () => toggleProcessPicker(picker, row));
|
||||
const searchInput = container.querySelector('.process-picker-search');
|
||||
searchInput.addEventListener('input', () => filterProcessPicker(picker));
|
||||
const textarea = container.querySelector('.condition-apps');
|
||||
attachProcessPicker(container, textarea);
|
||||
}
|
||||
|
||||
renderFields(condType, condition);
|
||||
@@ -573,65 +571,7 @@ function addAutomationConditionRow(condition) {
|
||||
list.appendChild(row);
|
||||
}
|
||||
|
||||
async function toggleProcessPicker(picker, row) {
|
||||
if (picker.style.display !== 'none') {
|
||||
picker.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const listEl = picker.querySelector('.process-picker-list');
|
||||
const searchEl = picker.querySelector('.process-picker-search');
|
||||
searchEl.value = '';
|
||||
listEl.innerHTML = `<div class="process-picker-loading">${t('common.loading')}</div>`;
|
||||
picker.style.display = '';
|
||||
|
||||
try {
|
||||
const resp = await fetchWithAuth('/system/processes');
|
||||
if (!resp.ok) throw new Error('Failed to fetch processes');
|
||||
const data = await resp.json();
|
||||
|
||||
const textarea = row.querySelector('.condition-apps');
|
||||
const existing = new Set(textarea.value.split('\n').map(a => a.trim().toLowerCase()).filter(Boolean));
|
||||
|
||||
picker._processes = data.processes;
|
||||
picker._existing = existing;
|
||||
renderProcessPicker(picker, data.processes, existing);
|
||||
searchEl.focus();
|
||||
} catch (e) {
|
||||
listEl.innerHTML = `<div class="process-picker-loading" style="color:var(--danger-color)">${e.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderProcessPicker(picker, processes, existing) {
|
||||
const listEl = picker.querySelector('.process-picker-list');
|
||||
if (processes.length === 0) {
|
||||
listEl.innerHTML = `<div class="process-picker-loading">${t('automations.condition.application.no_processes')}</div>`;
|
||||
return;
|
||||
}
|
||||
listEl.innerHTML = processes.map(p => {
|
||||
const added = existing.has(p.toLowerCase());
|
||||
return `<div class="process-picker-item${added ? ' added' : ''}" data-process="${escapeHtml(p)}">${escapeHtml(p)}${added ? ' \u2713' : ''}</div>`;
|
||||
}).join('');
|
||||
|
||||
listEl.querySelectorAll('.process-picker-item:not(.added)').forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const proc = item.dataset.process;
|
||||
const row = picker.closest('.automation-condition-row');
|
||||
const textarea = row.querySelector('.condition-apps');
|
||||
const current = textarea.value.trim();
|
||||
textarea.value = current ? current + '\n' + proc : proc;
|
||||
item.classList.add('added');
|
||||
item.textContent = proc + ' \u2713';
|
||||
picker._existing.add(proc.toLowerCase());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function filterProcessPicker(picker) {
|
||||
const query = picker.querySelector('.process-picker-search').value.toLowerCase();
|
||||
const filtered = (picker._processes || []).filter(p => p.includes(query));
|
||||
renderProcessPicker(picker, filtered, picker._existing || new Set());
|
||||
}
|
||||
|
||||
function getAutomationEditorConditions() {
|
||||
const rows = document.querySelectorAll('#automation-conditions-list .automation-condition-row');
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
ICON_LINK, ICON_SPARKLES, ICON_ACTIVITY, ICON_CLOCK, ICON_BELL,
|
||||
} from '../core/icons.js';
|
||||
import { wrapCard } from '../core/card-colors.js';
|
||||
import { attachProcessPicker } from '../core/process-picker.js';
|
||||
|
||||
class CSSEditorModal extends Modal {
|
||||
constructor() {
|
||||
@@ -117,7 +118,7 @@ export function onCSSTypeChange() {
|
||||
_syncAnimationSpeedState();
|
||||
|
||||
// LED count — only shown for picture, api_input, notification
|
||||
const hasLedCount = ['picture', 'api_input', 'notification'];
|
||||
const hasLedCount = ['picture', 'api_input'];
|
||||
document.getElementById('css-editor-led-count-group').style.display =
|
||||
hasLedCount.includes(type) ? '' : 'none';
|
||||
|
||||
@@ -264,7 +265,7 @@ function _colorCycleRenderList() {
|
||||
onclick="colorCycleRemoveColor(${i})">✕</button>`
|
||||
: `<div style="height:14px"></div>`}
|
||||
</div>
|
||||
`).join('');
|
||||
`).join('') + `<div class="color-cycle-item"><button type="button" class="btn btn-secondary color-cycle-add-btn" onclick="colorCycleAddColor()">+</button></div>`;
|
||||
}
|
||||
|
||||
export function colorCycleAddColor() {
|
||||
@@ -597,10 +598,10 @@ function _notificationAppColorsRenderList() {
|
||||
const list = document.getElementById('notification-app-colors-list');
|
||||
if (!list) return;
|
||||
list.innerHTML = _notificationAppColors.map((entry, i) => `
|
||||
<div class="color-cycle-item">
|
||||
<input type="text" class="notif-app-name" data-idx="${i}" value="${escapeHtml(entry.app)}" placeholder="App name" style="flex:1">
|
||||
<div class="notif-app-color-row">
|
||||
<input type="text" class="notif-app-name" data-idx="${i}" value="${escapeHtml(entry.app)}" placeholder="App name">
|
||||
<input type="color" class="notif-app-color" data-idx="${i}" value="${entry.color}">
|
||||
<button type="button" class="btn btn-secondary color-cycle-remove-btn"
|
||||
<button type="button" class="btn btn-secondary notif-app-color-remove"
|
||||
onclick="notificationRemoveAppColor(${i})">✕</button>
|
||||
</div>
|
||||
`).join('');
|
||||
@@ -647,8 +648,9 @@ function _loadNotificationState(css) {
|
||||
document.getElementById('css-editor-notification-duration-val').textContent = dur;
|
||||
document.getElementById('css-editor-notification-default-color').value = css.default_color || '#ffffff';
|
||||
document.getElementById('css-editor-notification-filter-mode').value = css.app_filter_mode || 'off';
|
||||
document.getElementById('css-editor-notification-filter-list').value = (css.app_filter_list || []).join(', ');
|
||||
document.getElementById('css-editor-notification-filter-list').value = (css.app_filter_list || []).join('\n');
|
||||
onNotificationFilterModeChange();
|
||||
_attachNotificationProcessPicker();
|
||||
|
||||
// App colors dict → list
|
||||
const ac = css.app_colors || {};
|
||||
@@ -666,11 +668,18 @@ function _resetNotificationState() {
|
||||
document.getElementById('css-editor-notification-filter-mode').value = 'off';
|
||||
document.getElementById('css-editor-notification-filter-list').value = '';
|
||||
onNotificationFilterModeChange();
|
||||
_attachNotificationProcessPicker();
|
||||
_notificationAppColors = [];
|
||||
_notificationAppColorsRenderList();
|
||||
_showNotificationEndpoint(null);
|
||||
}
|
||||
|
||||
function _attachNotificationProcessPicker() {
|
||||
const container = document.getElementById('css-editor-notification-filter-picker-container');
|
||||
const textarea = document.getElementById('css-editor-notification-filter-list');
|
||||
if (container && textarea) attachProcessPicker(container, textarea);
|
||||
}
|
||||
|
||||
function _showNotificationEndpoint(cssId) {
|
||||
const el = document.getElementById('css-editor-notification-endpoint');
|
||||
if (!el) return;
|
||||
@@ -811,7 +820,6 @@ export function createColorStripCard(source, pictureSourceMap, audioSourceMap) {
|
||||
<span style="display:inline-block;width:14px;height:14px;background:${defColor};border:1px solid #888;border-radius:2px;vertical-align:middle;margin-right:4px"></span>${defColor.toUpperCase()}
|
||||
</span>
|
||||
${appCount > 0 ? `<span class="stream-card-prop">${ICON_PALETTE} ${appCount} ${t('color_strip.notification.app_count')}</span>` : ''}
|
||||
${source.led_count ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">${ICON_LED} ${source.led_count}</span>` : ''}
|
||||
`;
|
||||
} else {
|
||||
const ps = pictureSourceMap && pictureSourceMap[source.picture_source_id];
|
||||
@@ -1155,7 +1163,7 @@ export async function saveCSSEditor() {
|
||||
if (!cssId) payload.source_type = 'api_input';
|
||||
} else if (sourceType === 'notification') {
|
||||
const filterList = document.getElementById('css-editor-notification-filter-list').value
|
||||
.split(',').map(s => s.trim()).filter(Boolean);
|
||||
.split('\n').map(s => s.trim()).filter(Boolean);
|
||||
payload = {
|
||||
name,
|
||||
notification_effect: document.getElementById('css-editor-notification-effect').value,
|
||||
@@ -1164,7 +1172,6 @@ export async function saveCSSEditor() {
|
||||
app_filter_mode: document.getElementById('css-editor-notification-filter-mode').value,
|
||||
app_filter_list: filterList,
|
||||
app_colors: _notificationGetAppColorsDict(),
|
||||
led_count: parseInt(document.getElementById('css-editor-led-count').value) || 0,
|
||||
};
|
||||
if (!cssId) payload.source_type = 'notification';
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user