feat: tracklist column reset, device sync cross-platform template fix, filename template builder, privacy policy

- Add "Reset to defaults" button to column picker in AlbumTrackList, PlaylistDetail, Favorites (all 8 locales)
- Fix device sync cross-OS status detection: store filenameTemplate in manifest, use it for path computation on import
- Redesign filename template UI: preset buttons (Standard/Multi-Disc/Alt. Folder), clickable token chips ({artist}, {album}, etc.), clear button
- Fix suggestions section in PlaylistDetail missing album column rendering
- Add PRIVACY.md documenting all third-party integrations (Last.fm, LRCLIB, Apple Music, Discord); link from README

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-16 19:46:57 +02:00
parent 58851da817
commit 7db42d74ef
17 changed files with 328 additions and 41 deletions
+12 -2
View File
@@ -1,7 +1,7 @@
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
import { createPortal } from 'react-dom';
import { useParams, useNavigate } from 'react-router-dom';
import { ChevronDown, ChevronLeft, Play, ListPlus, Trash2, Search, X, Loader2, Plus, GripVertical, Star, RefreshCw, Shuffle, Heart, HardDriveDownload, Check, Pencil, Globe, Lock, Camera, Download, FileUp } from 'lucide-react';
import { ChevronDown, ChevronLeft, Play, ListPlus, Trash2, Search, X, Loader2, Plus, GripVertical, Star, RefreshCw, Shuffle, Heart, HardDriveDownload, Check, Pencil, Globe, Lock, Camera, Download, FileUp, RotateCcw } from 'lucide-react';
import { useTracklistColumns, type ColDef } from '../utils/useTracklistColumns';
import { AddToPlaylistSubmenu } from '../components/ContextMenu';
import {
@@ -388,7 +388,7 @@ export default function PlaylistDetail() {
// ── Column resize/visibility ──────────────────────────────────────────────
const {
colVisible, visibleCols, gridStyle,
startResize, toggleColumn,
startResize, toggleColumn, resetColumns,
pickerOpen, setPickerOpen, pickerRef, tracklistRef,
} = useTracklistColumns(PL_COLUMNS, 'psysonic_playlist_columns');
@@ -1419,6 +1419,11 @@ export default function PlaylistDetail() {
</button>
);
})}
<div className="tracklist-col-picker-divider" />
<button className="tracklist-col-picker-reset" onClick={resetColumns}>
<RotateCcw size={13} />
{t('albumDetail.resetColumns')}
</button>
</div>
)}
</div>
@@ -1695,6 +1700,11 @@ export default function PlaylistDetail() {
<span className={`track-artist${song.artistId ? ' track-artist-link' : ''}`} style={{ cursor: song.artistId ? 'pointer' : 'default' }} onClick={e => { if (song.artistId) { e.stopPropagation(); navigate(`/artist/${song.artistId}`); } }}>{song.artist}</span>
</div>
);
case 'album': return (
<div key="album" className="track-artist-cell">
<span className={`track-artist${song.albumId ? ' track-artist-link' : ''}`} style={{ cursor: song.albumId ? 'pointer' : 'default' }} onClick={e => { if (song.albumId) { e.stopPropagation(); navigate(`/album/${song.albumId}`); } }}>{song.album}</span>
</div>
);
case 'favorite': return <div key="favorite" />;
case 'rating': return <div key="rating" />;
case 'duration': return <div key="duration" className="track-duration">{formatDuration(song.duration ?? 0)}</div>;