Add command palette entity selector for all editor dropdowns

Replace plain <select> dropdowns with a searchable command palette modal
for 16 entity selectors across 6 editors (targets, streams, CSS sources,
value sources, audio sources, pattern templates). Unified EntityPalette
singleton + EntitySelect wrapper in core/entity-palette.js.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 00:17:44 +03:00
parent b4d89e271d
commit 6fc0e20e1d
12 changed files with 657 additions and 7 deletions

View File

@@ -16,17 +16,22 @@ import { t } from '../core/i18n.js';
import { showToast, showConfirm } from '../core/ui.js';
import { Modal } from '../core/modal.js';
import {
getValueSourceIcon,
getValueSourceIcon, getAudioSourceIcon, getPictureSourceIcon,
ICON_CLONE, ICON_EDIT, ICON_TEST,
ICON_LED_PREVIEW, ICON_ACTIVITY, ICON_TIMER, ICON_MOVE_VERTICAL,
ICON_MUSIC, ICON_TRENDING_UP, ICON_MAP_PIN, ICON_MONITOR, ICON_REFRESH,
} from '../core/icons.js';
import { wrapCard } from '../core/card-colors.js';
import { IconSelect } from '../core/icon-select.js';
import { EntitySelect } from '../core/entity-palette.js';
import { loadPictureSources } from './streams.js';
export { getValueSourceIcon };
// ── EntitySelect instances for value source editor ──
let _vsAudioSourceEntitySelect = null;
let _vsPictureSourceEntitySelect = null;
class ValueSourceModal extends Modal {
constructor() { super('value-source-modal'); }
@@ -585,6 +590,20 @@ function _populateAudioSourceDropdown(selectedId) {
const badge = s.source_type === 'multichannel' ? ' [multichannel]' : ' [mono]';
return `<option value="${s.id}"${s.id === selectedId ? ' selected' : ''}>${escapeHtml(s.name)}${badge}</option>`;
}).join('');
if (_vsAudioSourceEntitySelect) _vsAudioSourceEntitySelect.destroy();
if (_cachedAudioSources.length > 0) {
_vsAudioSourceEntitySelect = new EntitySelect({
target: select,
getItems: () => _cachedAudioSources.map(s => ({
value: s.id,
label: s.name,
icon: getAudioSourceIcon(s.source_type),
desc: s.source_type,
})),
placeholder: t('palette.search'),
});
}
}
// ── Adaptive helpers ──────────────────────────────────────────
@@ -595,6 +614,19 @@ function _populatePictureSourceDropdown(selectedId) {
select.innerHTML = _cachedStreams.map(s =>
`<option value="${s.id}"${s.id === selectedId ? ' selected' : ''}>${escapeHtml(s.name)}</option>`
).join('');
if (_vsPictureSourceEntitySelect) _vsPictureSourceEntitySelect.destroy();
if (_cachedStreams.length > 0) {
_vsPictureSourceEntitySelect = new EntitySelect({
target: select,
getItems: () => _cachedStreams.map(s => ({
value: s.id,
label: s.name,
icon: getPictureSourceIcon(s.stream_type),
})),
placeholder: t('palette.search'),
});
}
}
export function addSchedulePoint(time = '', value = 1.0) {