feat(ui): tabbed settings and onboarding prompts

This commit is contained in:
2025-10-01 10:40:10 -04:00
parent 6af3603c7d
commit de04dbc323
2 changed files with 180 additions and 99 deletions

View File

@@ -1,8 +1,38 @@
<script lang="ts">
import { settings } from '$lib/stores/settings';
</script>
<div> <div>
<img src="/Square150x150Logo.png" alt="cat" style="width: 128px; height: 128px;" /> <img src="/Square150x150Logo.png" alt="cat" style="width: 128px; height: 128px;" />
<h2>Welcome to Shark!</h2> <h2>Welcome to Shark!</h2>
<p>Your music library manager and player.</p> <p>Your music library manager and player.</p>
{#if !$settings.musicFolder}
<div class="window welcome-prompt">
<div class="title-bar">
<div class="title-bar-text">Ahoy!</div>
</div>
<div class="window-body">
<p>It seems you're new here! To get started, set up your music downloads folder.</p>
<div class="button-row">
<a href="/settings"><button>Go to Settings</button></a>
</div>
</div>
</div>
{:else if !$settings.playlistsFolder}
<div class="window welcome-prompt">
<div class="title-bar">
<div class="title-bar-text">Welcome Back!</div>
</div>
<div class="window-body">
<p>Head to settings to set up your playlists folder! Life is more fun with playlists.</p>
<div class="button-row">
<a href="/settings"><button>Go to Settings</button></a>
</div>
</div>
</div>
{/if}
<section class="quick-stats"> <section class="quick-stats">
<div class="field-row-stacked"> <div class="field-row-stacked">
</div> </div>
@@ -17,4 +47,20 @@
.quick-stats { .quick-stats {
margin-top: 20px; margin-top: 20px;
} }
.welcome-prompt {
margin-top: 20px;
max-width: 500px;
}
.welcome-prompt p {
line-height: 1.5;
letter-spacing: 0.01em;
word-spacing: normal;
}
.button-row {
margin-top: 12px;
text-align: center;
}
</style> </style>

View File

@@ -16,6 +16,7 @@
let currentDeezerConcurrency = $state<number>(1); let currentDeezerConcurrency = $state<number>(1);
let currentDeezerFormat = $state<'FLAC' | 'MP3_320' | 'MP3_128'>('FLAC'); let currentDeezerFormat = $state<'FLAC' | 'MP3_320' | 'MP3_128'>('FLAC');
let currentDeezerOverwrite = $state<boolean>(false); let currentDeezerOverwrite = $state<boolean>(false);
let activeTab = $state<'library' | 'deezer' | 'advanced'>('library');
onMount(async () => { onMount(async () => {
await loadSettings(); await loadSettings();
@@ -57,11 +58,32 @@
await setPlaylistsFolder(selected); await setPlaylistsFolder(selected);
} }
} }
async function clearAllPaths() {
await setMusicFolder(null);
await setPlaylistsFolder(null);
}
</script> </script>
<div> <div>
<h2>Settings</h2> <h2>Settings</h2>
<section class="library-content">
<menu role="tablist">
<li role="tab" aria-selected={activeTab === 'library'}>
<a href="#library" onclick={(e) => { e.preventDefault(); activeTab = 'library'; }}>Library</a>
</li>
<li role="tab" aria-selected={activeTab === 'deezer'}>
<a href="#deezer" onclick={(e) => { e.preventDefault(); activeTab = 'deezer'; }}>Deezer</a>
</li>
<li role="tab" aria-selected={activeTab === 'advanced'}>
<a href="#advanced" onclick={(e) => { e.preventDefault(); activeTab = 'advanced'; }}>Advanced</a>
</li>
</menu>
<div class="window" role="tabpanel">
<div class="window-body">
{#if activeTab === 'library'}
<section class="tab-content">
<h3>Library Folders</h3> <h3>Library Folders</h3>
<p class="info-note"> <p class="info-note">
<strong>Note:</strong> Music and Playlists folders should be in the same parent directory for playlists to work properly in other apps. <strong>Note:</strong> Music and Playlists folders should be in the same parent directory for playlists to work properly in other apps.
@@ -105,8 +127,8 @@
{/if} {/if}
</div> </div>
</section> </section>
{:else if activeTab === 'deezer'}
<section class="library-content"> <section class="tab-content">
<h3>Deezer Download Settings</h3> <h3>Deezer Download Settings</h3>
<div class="field-row-stacked"> <div class="field-row-stacked">
@@ -124,7 +146,7 @@
</div> </div>
<div class="field-row-stacked"> <div class="field-row-stacked">
<label for="deezer-concurrency">Download Concurrency (tracks within album/playlist)</label> <label for="deezer-concurrency">Download Concurrency</label>
<div class="slider-container"> <div class="slider-container">
<input <input
id="deezer-concurrency" id="deezer-concurrency"
@@ -136,11 +158,10 @@
/> />
<span class="slider-value">{currentDeezerConcurrency}</span> <span class="slider-value">{currentDeezerConcurrency}</span>
</div> </div>
<small class="help-text"> <small class="help-text">Number of tracks to download simultaneously (default: 1)</small>
Number of tracks to download simultaneously within collections (default: 1)
</small>
</div> </div>
<div class="field-row-stacked">
<div class="field-row"> <div class="field-row">
<input <input
id="deezer-overwrite" id="deezer-overwrite"
@@ -150,12 +171,27 @@
/> />
<label for="deezer-overwrite">Overwrite existing files</label> <label for="deezer-overwrite">Overwrite existing files</label>
</div> </div>
</div>
</section> </section>
{:else if activeTab === 'advanced'}
<section class="tab-content">
<h3>Advanced Settings</h3>
<div class="field-row-stacked">
<label>Clear All Paths</label>
<small class="help-text">This will reset your music and playlists folder paths. You'll need to set them up again.</small>
<button onclick={clearAllPaths}>Clear All Paths</button>
</div>
</section>
{/if}
</div>
</div>
</div> </div>
<style> <style>
h2 { h2 {
margin-top: 0; margin-top: 0;
margin-bottom: 12px;
} }
h3 { h3 {
@@ -164,8 +200,12 @@
font-size: 1.1em; font-size: 1.1em;
} }
.library-content { menu[role="tablist"] {
margin-top: 20px; margin-bottom: 0;
}
.tab-content {
margin: 0;
} }
.info-note { .info-note {
@@ -173,7 +213,8 @@
padding: 12px; padding: 12px;
margin-bottom: 20px; margin-bottom: 20px;
border-left: 3px solid var(--button-highlight, #606060); border-left: 3px solid var(--button-highlight, #606060);
font-size: 0.9em; font-size: 11px;
line-height: 1.4;
} }
.info-note strong { .info-note strong {
@@ -202,17 +243,11 @@
.help-text { .help-text {
color: var(--text-color, #FFFFFF); color: var(--text-color, #FFFFFF);
opacity: 0.7; opacity: 0.7;
font-size: 0.9em; font-size: 11px;
line-height: 1.4;
} }
.field-row { .field-row-stacked .field-row {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 12px;
}
.field-row label {
margin: 0; margin: 0;
} }