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:
@@ -15,7 +15,8 @@ import { API_BASE, fetchWithAuth, escapeHtml } from '../core/api.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { showToast, showConfirm, lockBody, unlockBody } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
import { ICON_MUSIC } from '../core/icons.js';
|
||||
import { ICON_MUSIC, getAudioSourceIcon, ICON_AUDIO_TEMPLATE, ICON_AUDIO_INPUT, ICON_AUDIO_LOOPBACK } from '../core/icons.js';
|
||||
import { EntitySelect } from '../core/entity-palette.js';
|
||||
import { loadPictureSources } from './streams.js';
|
||||
|
||||
class AudioSourceModal extends Modal {
|
||||
@@ -36,6 +37,11 @@ class AudioSourceModal extends Modal {
|
||||
|
||||
const audioSourceModal = new AudioSourceModal();
|
||||
|
||||
// ── EntitySelect instances for audio source editor ──
|
||||
let _asTemplateEntitySelect = null;
|
||||
let _asDeviceEntitySelect = null;
|
||||
let _asParentEntitySelect = null;
|
||||
|
||||
// ── Modal ─────────────────────────────────────────────────────
|
||||
|
||||
export async function showAudioSourceModal(sourceType, editData) {
|
||||
@@ -242,6 +248,20 @@ function _filterDevicesBySelectedTemplate() {
|
||||
const match = Array.from(select.options).find(o => o.textContent === prevName);
|
||||
if (match) select.value = match.value;
|
||||
}
|
||||
|
||||
if (_asDeviceEntitySelect) _asDeviceEntitySelect.destroy();
|
||||
if (devices.length > 0) {
|
||||
_asDeviceEntitySelect = new EntitySelect({
|
||||
target: select,
|
||||
getItems: () => devices.map(d => ({
|
||||
value: `${d.index}:${d.is_loopback ? '1' : '0'}`,
|
||||
label: d.name,
|
||||
icon: d.is_loopback ? ICON_AUDIO_LOOPBACK : ICON_AUDIO_INPUT,
|
||||
desc: d.is_loopback ? 'Loopback' : 'Input',
|
||||
})),
|
||||
placeholder: t('palette.search'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _selectAudioDevice(deviceIndex, isLoopback) {
|
||||
@@ -259,6 +279,19 @@ function _loadMultichannelSources(selectedId) {
|
||||
select.innerHTML = multichannel.map(s =>
|
||||
`<option value="${s.id}"${s.id === selectedId ? ' selected' : ''}>${escapeHtml(s.name)}</option>`
|
||||
).join('');
|
||||
|
||||
if (_asParentEntitySelect) _asParentEntitySelect.destroy();
|
||||
if (multichannel.length > 0) {
|
||||
_asParentEntitySelect = new EntitySelect({
|
||||
target: select,
|
||||
getItems: () => multichannel.map(s => ({
|
||||
value: s.id,
|
||||
label: s.name,
|
||||
icon: getAudioSourceIcon('multichannel'),
|
||||
})),
|
||||
placeholder: t('palette.search'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _loadAudioTemplates(selectedId) {
|
||||
@@ -268,6 +301,20 @@ function _loadAudioTemplates(selectedId) {
|
||||
select.innerHTML = templates.map(t =>
|
||||
`<option value="${t.id}"${t.id === selectedId ? ' selected' : ''}>${escapeHtml(t.name)} (${t.engine_type.toUpperCase()})</option>`
|
||||
).join('');
|
||||
|
||||
if (_asTemplateEntitySelect) _asTemplateEntitySelect.destroy();
|
||||
if (templates.length > 0) {
|
||||
_asTemplateEntitySelect = new EntitySelect({
|
||||
target: select,
|
||||
getItems: () => templates.map(tmpl => ({
|
||||
value: tmpl.id,
|
||||
label: tmpl.name,
|
||||
icon: ICON_AUDIO_TEMPLATE,
|
||||
desc: tmpl.engine_type.toUpperCase(),
|
||||
})),
|
||||
placeholder: t('palette.search'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Audio Source Test (real-time spectrum) ────────────────────
|
||||
|
||||
Reference in New Issue
Block a user