fix: restore show on profile toggle for playlists (#553)

the show_on_profile toggle was removed when the playlist page was
refactored from modal editing to inline editing (PR #531).

this restores the UI control so users can choose whether their
playlists appear on their public profile. the backend support
was already in place, just the UI was missing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>

authored by zzstoatzz.io Claude and committed by GitHub 5c8a05d5 150748ca

Changed files
+62 -11
frontend
src
routes
playlist
+62 -11
frontend/src/routes/playlist/[id]/+page.svelte
··· 37 37 38 38 // inline edit state 39 39 let editName = $state(""); 40 + let editShowOnProfile = $state(false); 40 41 let coverInputElement = $state<HTMLInputElement | null>(null); 41 42 let uploadingCover = $state(false); 42 43 ··· 215 216 } else { 216 217 // entering edit mode - initialize edit state 217 218 editName = playlist.name; 219 + editShowOnProfile = playlist.show_on_profile; 218 220 } 219 221 isEditMode = !isEditMode; 220 222 } ··· 223 225 // save track order 224 226 await saveOrder(); 225 227 226 - // save name if changed 227 - if (editName.trim() && editName.trim() !== playlist.name) { 228 - await saveNameChange(); 228 + // save name and/or show_on_profile if changed 229 + const nameChanged = 230 + !!editName.trim() && editName.trim() !== playlist.name; 231 + const showOnProfileChanged = 232 + editShowOnProfile !== playlist.show_on_profile; 233 + 234 + if (nameChanged || showOnProfileChanged) { 235 + await savePlaylistMetadata(nameChanged, showOnProfileChanged); 229 236 } 230 237 } 231 238 232 - async function saveNameChange() { 233 - if (!editName.trim() || editName.trim() === playlist.name) return; 234 - 239 + async function savePlaylistMetadata( 240 + nameChanged: boolean, 241 + showOnProfileChanged: boolean, 242 + ) { 235 243 try { 236 244 const formData = new FormData(); 237 - formData.append("name", editName.trim()); 245 + if (nameChanged) { 246 + formData.append("name", editName.trim()); 247 + } 248 + if (showOnProfileChanged) { 249 + formData.append("show_on_profile", String(editShowOnProfile)); 250 + } 238 251 239 252 const response = await fetch( 240 253 `${API_URL}/lists/playlists/${playlist.id}`, ··· 246 259 ); 247 260 248 261 if (!response.ok) { 249 - throw new Error("failed to update name"); 262 + throw new Error("failed to update playlist"); 250 263 } 251 264 252 265 const updated = await response.json(); 253 266 playlist.name = updated.name; 267 + playlist.show_on_profile = updated.show_on_profile; 254 268 } catch (e) { 255 - console.error("failed to save name:", e); 256 - toast.error(e instanceof Error ? e.message : "failed to save name"); 257 - // revert to original name 269 + console.error("failed to save playlist:", e); 270 + toast.error( 271 + e instanceof Error ? e.message : "failed to save playlist", 272 + ); 273 + // revert changes 258 274 editName = playlist.name; 275 + editShowOnProfile = playlist.show_on_profile; 259 276 } 260 277 } 261 278 ··· 680 697 : "tracks"}</span 681 698 > 682 699 </div> 700 + {#if isEditMode && isOwner} 701 + <label class="show-on-profile-toggle"> 702 + <input 703 + type="checkbox" 704 + bind:checked={editShowOnProfile} 705 + /> 706 + <span class="toggle-label">show on profile</span> 707 + </label> 708 + {/if} 683 709 </div> 684 710 685 711 <div class="side-buttons"> ··· 1379 1405 .meta-separator { 1380 1406 color: var(--text-muted); 1381 1407 font-size: 0.7rem; 1408 + } 1409 + 1410 + .show-on-profile-toggle { 1411 + display: flex; 1412 + align-items: center; 1413 + gap: 0.5rem; 1414 + margin-top: 0.75rem; 1415 + cursor: pointer; 1416 + font-size: 0.85rem; 1417 + color: var(--text-secondary); 1418 + } 1419 + 1420 + .show-on-profile-toggle input[type="checkbox"] { 1421 + width: 16px; 1422 + height: 16px; 1423 + accent-color: var(--accent); 1424 + cursor: pointer; 1425 + } 1426 + 1427 + .show-on-profile-toggle .toggle-label { 1428 + user-select: none; 1429 + } 1430 + 1431 + .show-on-profile-toggle:hover .toggle-label { 1432 + color: var(--text-primary); 1382 1433 } 1383 1434 1384 1435 .icon-btn {