fix: selector overflow for 10+ items, drag-drop status guard, filename claim set for parallel safety
- Queue selector uses min-width instead of fixed width for double-digit numbers - Drag-start handler validates item is still pending before allowing drag - ensureUniqueFilename uses in-memory claim set to prevent TOCTOU race Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6c47c63fa8
commit
cf9d7b8334
14
src/main.ts
14
src/main.ts
@ -685,20 +685,26 @@ function formatDurationDashed(seconds: number): string {
|
||||
return `${h.toString().padStart(2, '0')}-${m.toString().padStart(2, '0')}-${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
const claimedFilenames = new Set<string>();
|
||||
|
||||
function ensureUniqueFilename(filePath: string): string {
|
||||
if (!fs.existsSync(filePath)) return filePath;
|
||||
const dir = path.dirname(filePath);
|
||||
const ext = path.extname(filePath);
|
||||
const base = path.basename(filePath, ext);
|
||||
let counter = 1;
|
||||
let candidate = filePath;
|
||||
while (fs.existsSync(candidate)) {
|
||||
candidate = path.join(dir, `${base}_${counter}${ext}`);
|
||||
let counter = 0;
|
||||
while (fs.existsSync(candidate) || claimedFilenames.has(candidate)) {
|
||||
counter++;
|
||||
candidate = path.join(dir, `${base}_${counter}${ext}`);
|
||||
}
|
||||
claimedFilenames.add(candidate);
|
||||
return candidate;
|
||||
}
|
||||
|
||||
function releaseClaimedFilename(filePath: string): void {
|
||||
claimedFilenames.delete(filePath);
|
||||
}
|
||||
|
||||
function sanitizeFilenamePart(input: string, fallback = 'unnamed'): string {
|
||||
const cleaned = (input || '')
|
||||
.replace(/[<>:"|?*\x00-\x1f]/g, '_')
|
||||
|
||||
@ -216,6 +216,15 @@ function initQueueDragDrop(): void {
|
||||
list.addEventListener('dragstart', (e: DragEvent) => {
|
||||
const el = (e.target as HTMLElement).closest('.queue-item') as HTMLElement;
|
||||
if (!el) return;
|
||||
// Prevent dragging items that are no longer pending (race window between status change and re-render)
|
||||
const itemId = el.dataset.id;
|
||||
if (itemId) {
|
||||
const item = queue.find(i => i.id === itemId);
|
||||
if (!item || item.status !== 'pending') {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
draggedQueueItemId = el.dataset.id || null;
|
||||
el.classList.add('dragging');
|
||||
if (e.dataTransfer) e.dataTransfer.effectAllowed = 'move';
|
||||
|
||||
@ -358,8 +358,9 @@ body {
|
||||
}
|
||||
|
||||
.queue-selector {
|
||||
width: 22px;
|
||||
min-width: 22px;
|
||||
height: 22px;
|
||||
padding: 0 3px;
|
||||
border: 2px solid var(--text-secondary);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
@ -367,7 +368,7 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--bg-primary);
|
||||
user-select: none;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user