Add EntitySelect/IconSelect UI improvements across modals
- Portal IconSelect popups to document.body with position:fixed to prevent clipping by modal overflow-y:auto - Replace custom scene selectors in automation editor with EntitySelect command-palette pickers (main scene + fallback scene) - Add IconSelect grid for automation deactivation mode (none/revert/fallback) - Add IconSelect grid for automation condition type and match type - Replace mapped zone source dropdowns with EntitySelect pickers - Replace scene target selector with EntityPalette.pick() pattern - Remove effect palette preview bar from CSS editor - Remove sensitivity badge from audio color strip source cards - Clean up unused scene-selector CSS and scene-target-add-row CSS - Add locale keys for all new UI elements across en/ru/zh Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@ from wled_controller.core.capture.calibration import (
|
||||
)
|
||||
from wled_controller.core.capture.screen_capture import get_available_displays
|
||||
from wled_controller.core.processing.processor_manager import ProcessorManager
|
||||
from wled_controller.storage.color_strip_source import ApiInputColorStripSource, NotificationColorStripSource, PictureColorStripSource
|
||||
from wled_controller.storage.color_strip_source import AdvancedPictureColorStripSource, ApiInputColorStripSource, NotificationColorStripSource, PictureColorStripSource
|
||||
from wled_controller.storage.color_strip_store import ColorStripStore
|
||||
from wled_controller.storage.picture_source import ProcessedPictureSource, ScreenCapturePictureSource
|
||||
from wled_controller.storage.picture_source_store import PictureSourceStore
|
||||
@@ -47,7 +47,7 @@ router = APIRouter()
|
||||
def _css_to_response(source, overlay_active: bool = False) -> ColorStripSourceResponse:
|
||||
"""Convert a ColorStripSource to a ColorStripSourceResponse."""
|
||||
calibration = None
|
||||
if isinstance(source, PictureColorStripSource) and source.calibration:
|
||||
if isinstance(source, (PictureColorStripSource, AdvancedPictureColorStripSource)) and source.calibration:
|
||||
calibration = CalibrationSchema(**calibration_to_dict(source.calibration))
|
||||
|
||||
# Convert raw stop dicts to ColorStop schema objects for gradient sources
|
||||
@@ -376,7 +376,7 @@ async def test_css_calibration(
|
||||
if body.edges:
|
||||
try:
|
||||
source = store.get_source(source_id)
|
||||
if not isinstance(source, PictureColorStripSource):
|
||||
if not isinstance(source, (PictureColorStripSource, AdvancedPictureColorStripSource)):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Calibration test is only available for picture color strip sources",
|
||||
@@ -422,12 +422,13 @@ async def start_css_overlay(
|
||||
"""Start screen overlay visualization for a color strip source."""
|
||||
try:
|
||||
source = store.get_source(source_id)
|
||||
if not isinstance(source, PictureColorStripSource):
|
||||
if not isinstance(source, (PictureColorStripSource, AdvancedPictureColorStripSource)):
|
||||
raise HTTPException(status_code=400, detail="Overlay is only supported for picture color strip sources")
|
||||
if not source.calibration:
|
||||
raise HTTPException(status_code=400, detail="Color strip source has no calibration configured")
|
||||
|
||||
display_index = _resolve_display_index(source.picture_source_id, picture_source_store)
|
||||
ps_id = getattr(source, "picture_source_id", "") or ""
|
||||
display_index = _resolve_display_index(ps_id, picture_source_store)
|
||||
displays = get_available_displays()
|
||||
if not displays:
|
||||
raise HTTPException(status_code=409, detail="No displays available")
|
||||
|
||||
@@ -45,7 +45,7 @@ from wled_controller.core.capture.screen_capture import (
|
||||
get_available_displays,
|
||||
)
|
||||
from wled_controller.storage.color_strip_store import ColorStripStore
|
||||
from wled_controller.storage.color_strip_source import PictureColorStripSource
|
||||
from wled_controller.storage.color_strip_source import AdvancedPictureColorStripSource, PictureColorStripSource
|
||||
from wled_controller.storage import DeviceStore
|
||||
from wled_controller.storage.pattern_template_store import PatternTemplateStore
|
||||
from wled_controller.storage.picture_source import ScreenCapturePictureSource, StaticImagePictureSource
|
||||
@@ -820,11 +820,12 @@ async def start_target_overlay(
|
||||
if first_css_id:
|
||||
try:
|
||||
css = color_strip_store.get_source(first_css_id)
|
||||
if isinstance(css, PictureColorStripSource) and css.calibration:
|
||||
if isinstance(css, (PictureColorStripSource, AdvancedPictureColorStripSource)) and css.calibration:
|
||||
calibration = css.calibration
|
||||
# Resolve the display this CSS is capturing
|
||||
from wled_controller.api.routes.color_strip_sources import _resolve_display_index
|
||||
display_index = _resolve_display_index(css.picture_source_id, picture_source_store)
|
||||
ps_id = getattr(css, "picture_source_id", "") or ""
|
||||
display_index = _resolve_display_index(ps_id, picture_source_store)
|
||||
displays = get_available_displays()
|
||||
if displays:
|
||||
display_index = min(display_index, len(displays) - 1)
|
||||
|
||||
@@ -133,14 +133,42 @@ async def update_scene_preset(
|
||||
data: ScenePresetUpdate,
|
||||
_auth: AuthRequired,
|
||||
store: ScenePresetStore = Depends(get_scene_preset_store),
|
||||
target_store: OutputTargetStore = Depends(get_output_target_store),
|
||||
manager: ProcessorManager = Depends(get_processor_manager),
|
||||
):
|
||||
"""Update scene preset metadata."""
|
||||
"""Update scene preset metadata and optionally change targets."""
|
||||
# If target_ids changed, update the snapshot: keep state for existing targets,
|
||||
# capture fresh state for newly added targets, drop removed ones.
|
||||
new_targets = None
|
||||
if data.target_ids is not None:
|
||||
try:
|
||||
existing = store.get_preset(preset_id)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
existing_map = {t.target_id: t for t in existing.targets}
|
||||
new_target_ids = set(data.target_ids)
|
||||
|
||||
# Capture fresh state for newly added targets
|
||||
added_ids = new_target_ids - set(existing_map.keys())
|
||||
fresh = capture_current_snapshot(target_store, manager, added_ids) if added_ids else []
|
||||
fresh_map = {t.target_id: t for t in fresh}
|
||||
|
||||
# Build new target list preserving order from target_ids
|
||||
new_targets = []
|
||||
for tid in data.target_ids:
|
||||
if tid in existing_map:
|
||||
new_targets.append(existing_map[tid])
|
||||
elif tid in fresh_map:
|
||||
new_targets.append(fresh_map[tid])
|
||||
|
||||
try:
|
||||
preset = store.update_preset(
|
||||
preset_id,
|
||||
name=data.name,
|
||||
description=data.description,
|
||||
order=data.order,
|
||||
targets=new_targets,
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404 if "not found" in str(e).lower() else 400, detail=str(e))
|
||||
|
||||
Reference in New Issue
Block a user