Fix device provider kwargs, camera crash guard, target API, and graph color picker
- Refactor all device providers to use explicit kwargs.get() instead of fragile pop-then-passthrough (fixes WLED target start failing with unexpected dmx_protocol kwarg) - Add process-wide camera index registry to prevent concurrent opens of the same physical camera which crashes the DSHOW backend on Windows - Fix OutputTargetResponse validation error when brightness_value_source_id is None (coerce to empty string in response and from_dict) - Replace native <input type="color"> in graph editor with the custom color picker popover used throughout the app, positioned via getScreenCTM() inside an absolute overlay on .graph-container Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
import { ENTITY_COLORS, NODE_WIDTH, NODE_HEIGHT, computePorts } from './graph-layout.js';
|
||||
import { EDGE_COLORS } from './graph-edges.js';
|
||||
import { createColorPicker, registerColorPicker, closeAllColorPickers } from './color-picker.js';
|
||||
import * as P from './icon-paths.js';
|
||||
|
||||
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
@@ -143,34 +144,43 @@ function renderNode(node, callbacks) {
|
||||
barHit.style.cursor = 'pointer';
|
||||
barHit.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
// Create temporary color input positioned near the click
|
||||
const input = document.createElement('input');
|
||||
input.type = 'color';
|
||||
input.value = color;
|
||||
input.style.position = 'fixed';
|
||||
input.style.left = e.clientX + 'px';
|
||||
input.style.top = e.clientY + 'px';
|
||||
input.style.width = '0';
|
||||
input.style.height = '0';
|
||||
input.style.padding = '0';
|
||||
input.style.border = 'none';
|
||||
input.style.opacity = '0';
|
||||
input.style.pointerEvents = 'none';
|
||||
document.body.appendChild(input);
|
||||
input.addEventListener('input', () => {
|
||||
const c = input.value;
|
||||
bar.setAttribute('fill', c);
|
||||
barCover.setAttribute('fill', c);
|
||||
_saveNodeColor(id, c);
|
||||
const svg = barHit.ownerSVGElement;
|
||||
const container = svg?.closest('.graph-container');
|
||||
if (!svg || !container) return;
|
||||
|
||||
// Remove any previous graph color picker overlay
|
||||
container.querySelector('.graph-cp-overlay')?.remove();
|
||||
closeAllColorPickers();
|
||||
|
||||
// Compute position relative to container
|
||||
const ctm = barHit.getScreenCTM();
|
||||
const cr = container.getBoundingClientRect();
|
||||
const px = (ctm ? ctm.e : e.clientX) - cr.left;
|
||||
const py = (ctm ? ctm.f : e.clientY) - cr.top;
|
||||
|
||||
// Create an HTML overlay with the custom color picker
|
||||
const pickerId = `graph-node-${id}`;
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'graph-cp-overlay';
|
||||
overlay.style.cssText = `position:absolute; left:${px}px; top:${py}px; z-index:100;`;
|
||||
overlay.innerHTML = createColorPicker({
|
||||
id: pickerId,
|
||||
currentColor: color,
|
||||
anchor: 'left',
|
||||
});
|
||||
input.addEventListener('change', () => {
|
||||
input.remove();
|
||||
container.appendChild(overlay);
|
||||
|
||||
// Register callback to update the bar color
|
||||
registerColorPicker(pickerId, (hex) => {
|
||||
color = hex;
|
||||
bar.setAttribute('fill', hex);
|
||||
barCover.setAttribute('fill', hex);
|
||||
_saveNodeColor(id, hex);
|
||||
overlay.remove();
|
||||
});
|
||||
// Fallback remove if user cancels
|
||||
input.addEventListener('blur', () => {
|
||||
setTimeout(() => input.remove(), 200);
|
||||
});
|
||||
input.click();
|
||||
|
||||
// Open the popover immediately
|
||||
window._cpToggle(pickerId);
|
||||
});
|
||||
g.appendChild(barHit);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user