add tag-album and clean-playlist-files tasks, script step support, stop fix, rename fix

- New tag-album task: renames tracks to NNN format, sets ID3 tags via mutagen
- New clean-playlist-files task: deletes .cue, .m3u, .nfo and similar junk files
- Pipeline executor now supports script-mode steps (runs directly, no agent)
- Build discography pipeline: convert-audio → clean-playlist-files → prepare → fetch → tag
- Fix stop button: abort signal now kills running agent processes
- Fix job manager: broadcast stopped/error events to WebSocket viewers
- Fix file browser rename: delay focus to avoid context menu close race, left-align input

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 04:10:14 +00:00
co-authored by Claude Opus 4.6
parent 5e38343f44
commit 53c78c76cb
7 changed files with 367 additions and 15 deletions
@@ -368,13 +368,14 @@ const InlineRenameInput = ({
}) => {
const [value, setValue] = useState(initialName);
const inputRef = useRef<HTMLInputElement | null>(null);
const focused = useRef(false);
const ready = useRef(false);
useEffect(() => {
const node = inputRef.current;
if (!node || focused.current) return;
focused.current = true;
requestAnimationFrame(() => {
if (!node) return;
// Delay focus to let the context menu fully close and avoid focus races
const timer = setTimeout(() => {
ready.current = true;
node.focus();
const dotIndex = initialName.lastIndexOf('.');
if (dotIndex > 0) {
@@ -382,7 +383,8 @@ const InlineRenameInput = ({
} else {
node.select();
}
});
}, 50);
return () => clearTimeout(timer);
}, []);
const commit = () => {
@@ -399,13 +401,17 @@ const InlineRenameInput = ({
ref={inputRef}
value={value}
onChange={(ev) => setValue(ev.target.value)}
onBlur={commit}
onBlur={() => {
// Ignore blur before the input is ready (context menu closing steals focus)
if (!ready.current) return;
commit();
}}
onKeyDown={(ev) => {
if (ev.key === 'Enter') commit();
if (ev.key === 'Escape') onCancel();
}}
onClick={(ev) => ev.stopPropagation()}
className="text-sm font-medium text-duck-dark bg-background border border-duck-teal/50 rounded px-1 py-0.5 outline-none w-full text-center"
className="text-sm font-medium text-duck-dark bg-background border border-duck-teal/50 rounded px-1 py-0.5 outline-none max-w-full text-left"
/>
);
};