// =============================== // HELP & DOCS PAGE // =============================== const DOCS_SECTIONS = [ { id: 'getting-started', title: 'Getting Started', icon: '/static/dashboard.png', children: [ { id: 'gs-overview', title: 'Overview' }, { id: 'gs-first-setup', title: 'First-Time Setup' }, { id: 'gs-connecting', title: 'Connecting Services' }, { id: 'gs-interface', title: 'Understanding the Interface' } ], content: () => `

Overview

SoulSync is a self-hosted music download, sync, and library management platform. It connects to Spotify, Apple Music/iTunes, Tidal, YouTube, and Beatport for metadata, and uses Soulseek (via slskd) as the primary download source. Your library is served through Plex, Jellyfin, or Navidrome.

🎵 Download Music

Search and download tracks in FLAC, MP3, and more from Soulseek, with automatic metadata tagging and file organization.

🔄 Playlist Sync

Mirror playlists from Spotify, YouTube, Tidal, and Beatport. Discover official metadata and sync to your media server.

📚 Library Management

Browse, edit, and enrich your music library with metadata from 7 services. Write tags directly to audio files.

🤖 Automations

Schedule tasks, chain workflows with signals, and get notified via Discord, Pushbullet, or Telegram.

🔍 Artist Discovery

Discover new artists via similar-artist recommendations, seasonal playlists, genre exploration, and time-machine browsing.

👀 Watchlist

Follow artists and automatically scan for new releases. New tracks are added to your wishlist for download.

First-Time Setup

After launching SoulSync, head to the Settings page to configure your services. At minimum you need:

  1. Soulseek (slskd) — Enter your slskd URL and API key. This is required for downloading music.
  2. Media Server — Connect Plex, Jellyfin, or Navidrome so SoulSync knows where your library lives and can trigger scans.
  3. Spotify (Recommended) — Connect Spotify for the richest metadata. Create an app at developer.spotify.com, enter your Client ID and Secret, then click Authenticate.
  4. Download Path — Set your download and transfer paths in the Download Settings section. The transfer path should point to your media server's monitored folder.
💡
You can start using SoulSync with just Soulseek connected. Spotify and other services add metadata enrichment but aren't strictly required — iTunes/Apple Music is always available as a free fallback.

Connecting Services

SoulSync integrates with many external services. Here's a quick reference for each:

ServicePurposeAuth Required
SpotifyPrimary metadata source (artists, albums, tracks, cover art, genres)OAuth — Client ID + Secret
iTunes / Apple MusicFallback metadata source, always free, no auth neededNone
Soulseek (slskd)Music download sourceURL + API key
PlexMedia server — library scanning and metadata syncURL + Token
JellyfinMedia server — library scanningURL + API Key
NavidromeMedia server — auto-detects changesURL + Username + Password
TidalPlaylist import, optional download sourceOAuth — Client ID + Secret
Last.fmEnrichment — listener stats, tags, bios, similar artistsAPI Key
GeniusEnrichment — lyrics, descriptions, alternate namesAccess Token
AcoustIDAudio fingerprint verification of downloadsAPI Key
ListenBrainzListening history and recommendationsURL + Token

Understanding the Interface

SoulSync uses a sidebar navigation layout. The left sidebar contains links to every page, a media player at the bottom, and service status indicators. The main content area changes based on the selected page.

` }, { id: 'dashboard', title: 'Dashboard', icon: '/static/dashboard.png', children: [ { id: 'dash-overview', title: 'Overview & Stats' }, { id: 'dash-workers', title: 'Enrichment Workers' }, { id: 'dash-tools', title: 'Tool Cards' }, { id: 'dash-activity', title: 'Activity Feed' } ], content: () => `

Overview & Stats

The dashboard is your command center. At the top you'll see service status indicators for Spotify, your media server, and Soulseek — showing connected/disconnected state at a glance. Below that, stat cards display your library totals: artists, albums, tracks, and total library size.

Stats update in real-time via WebSocket — no page refresh needed.

Enrichment Workers

The header bar contains enrichment worker icons for each metadata service. Hover over any icon to see its current status, what item it's processing, and progress counts (e.g., "142/500 matched").

Workers run automatically in the background, enriching your library with metadata from:

Spotify

Artist genres, follower counts, images, album release dates, track preview URLs

MusicBrainz

MBIDs for artists, albums, and tracks — enables accurate cross-referencing

Deezer

Deezer IDs, genres, album metadata

AudioDB

Artist descriptions, artist art, album info

iTunes

iTunes/Apple Music IDs, preview links

Last.fm

Listener/play counts, bios, tags, similar artists for every artist/album/track

Genius

Lyrics, descriptions, alternate names, song artwork

ℹ️
Workers retry "not found" items every 30 days and errored items every 7 days. You can pause/resume any worker from the dashboard.

Tool Cards

The dashboard features several tool cards for library maintenance:

ToolWhat It Does
Database UpdaterRefreshes your library by scanning your media server. Choose incremental (new only) or full refresh.
Metadata UpdaterUpdates artist photos, genres, styles, and biographies from MusicBrainz, Spotify, iTunes, and Last.fm.
Quality ScannerScans library for tracks below your quality preferences. Shows how many meet standards and finds replacements.
Duplicate CleanerIdentifies and removes duplicate tracks from your library, freeing up disk space.
Discovery PoolView and fix matched/failed discovery results across all mirrored playlists.
Retag ToolBatch retag downloaded files with correct album metadata from Spotify/iTunes.
Backup ManagerCreate, download, restore, and delete database backups. Rolling cleanup keeps the 5 most recent.
💡
Each tool card has a help button (?) that opens detailed instructions for that specific tool.

Activity Feed

The activity feed at the bottom of the dashboard shows recent system events: downloads completed, syncs started, settings changed, automation runs, and errors. Events appear in real-time via WebSocket.

` }, { id: 'sync', title: 'Playlist Sync', icon: '/static/sync.png', children: [ { id: 'sync-overview', title: 'Overview' }, { id: 'sync-spotify', title: 'Spotify Playlists' }, { id: 'sync-youtube', title: 'YouTube Playlists' }, { id: 'sync-tidal', title: 'Tidal Playlists' }, { id: 'sync-beatport', title: 'Beatport' }, { id: 'sync-mirrored', title: 'Mirrored Playlists' }, { id: 'sync-discovery', title: 'Discovery Pipeline' } ], content: () => `

Overview

The Sync page lets you import playlists from Spotify, YouTube, Tidal, and Beatport. Once imported, playlists are mirrored — they persist in your SoulSync instance and can be refreshed, discovered, and synced to your wishlist for downloading.

Spotify Playlists

If Spotify is connected, click Refresh to load all your Spotify playlists. Each playlist shows its cover art, track count, and sync status.

For each playlist you can:

💡
Spotify-sourced playlists are auto-discovered at confidence 1.0 during refresh — no separate discovery step needed.

YouTube Playlists

Paste a YouTube playlist URL into the input field and click Parse Playlist. SoulSync extracts the track list and attempts to match each track to official Spotify/iTunes metadata.

⚠️
YouTube tracks often have non-standard titles (e.g., "Artist - Song (Official Video)"). The discovery pipeline handles this, but some manual fixes may be needed for edge cases.

Tidal Playlists

Requires Tidal authentication in Settings. Once connected, refresh to load your Tidal playlists. You can also select Tidal download quality: HQ (320kbps), HiFi (FLAC 16-bit), or HiFi Plus (up to 24-bit).

Beatport

The Beatport tab has three views: Browse (featured content, genre browsing), Charts (Top 100, Hype charts), and My Playlists. Browse 12+ electronic music genres and download directly from chart listings.

Mirrored Playlists

Every parsed playlist from any source is automatically mirrored. The Mirrored tab shows all saved playlists with source-branded cards, live discovery status, and download progress.

Discovery Pipeline

For non-Spotify playlists (YouTube, Tidal), tracks need to be discovered before syncing. Discovery matches raw titles to official Spotify/iTunes metadata using fuzzy matching with a 0.7 confidence threshold.

  1. Import a playlist (YouTube or Tidal)
  2. Click Discover on the playlist card (or automate with the "Discover Playlist" action)
  3. SoulSync matches each track to official metadata — results are cached globally
  4. Sync the playlist — only discovered tracks are included; unmatched tracks are skipped
💡
Chain automations for hands-free operation: Refresh Playlist → Playlist Changed → Discover → Discovery Complete → Sync
` }, { id: 'search', title: 'Music Downloads', icon: '/static/search.png', children: [ { id: 'search-enhanced', title: 'Enhanced Search' }, { id: 'search-basic', title: 'Basic Search' }, { id: 'search-downloading', title: 'Downloading Music' }, { id: 'search-quality', title: 'Quality Profiles' }, { id: 'search-manager', title: 'Download Manager' } ], content: () => `

Enhanced Search

The default search mode. Type an artist, album, or track name and results appear in a categorized dropdown: In Your Library, Artists, Albums, Singles & EPs, and Tracks. Results come from Spotify (or iTunes if Spotify is unavailable).

Basic Search

Toggle to Basic Search mode for direct Soulseek queries. This shows raw search results with detailed info: format, bitrate, quality score, file size, uploader name, upload speed, and availability.

Filters let you narrow results by type (Albums/Singles), format (FLAC/MP3/OGG/AAC/WMA), and sort by relevance, quality, size, bitrate, duration, or uploader speed.

Downloading Music

When you select an album or track to download, a modal appears with:

After downloading, files go through post-processing: optional AcoustID fingerprint verification, automatic metadata tagging (title, artist, album, track number, genre, cover art), and organized file placement in your library.

Quality Profiles

Configure your quality preferences in Settings → Quality Profile. Quick presets:

PresetPriority
AudiophileFLAC first, then MP3 320
BalancedMP3 320 first, then FLAC, then MP3 256
Space SaverMP3 256 first, then MP3 192

Each format has configurable bitrate ranges and a priority order. Enable Fallback to accept any quality when preferred formats aren't available.

Download Manager

Toggle the download manager panel (right sidebar) to see all active and completed downloads. Each download shows real-time progress: track name, format, speed, ETA, and a cancel button. Use Clear Completed to clean up finished items.

` }, { id: 'discover', title: 'Discover Artists', icon: '/static/discover.png', children: [ { id: 'disc-hero', title: 'Featured Artists' }, { id: 'disc-playlists', title: 'Discovery Playlists' }, { id: 'disc-build', title: 'Build Custom Playlist' }, { id: 'disc-seasonal', title: 'Seasonal & Curated' }, { id: 'disc-timemachine', title: 'Time Machine' } ], content: () => `

Featured Artists

The hero slider showcases recommended artists based on your watchlist. Each slide shows the artist's image, name, popularity score, genres, and similarity context. Use the arrows or dots to navigate, or click:

Discovery Playlists

SoulSync generates curated playlists from your discovery pool (50 similar artists refreshed during watchlist scans):

Each playlist can be downloaded or synced to your media server.

Build Custom Playlist

Search for 1–5 artists, select them, and click Generate to create a custom playlist from their catalogs. You can then download or sync the generated playlist.

Seasonal & Curated Content

The Discover page includes auto-generated seasonal content based on the current time of year, plus two curated sections:

Both can be synced to your media server with live progress tracking.

Time Machine

Browse discovery pool content by decade — tabs from the 1950s through the 2020s. Each decade pulls top tracks from pool artists active in that era.

` }, { id: 'artists', title: 'Artists & Watchlist', icon: '/static/artists.png', children: [ { id: 'art-search', title: 'Artist Search' }, { id: 'art-watchlist', title: 'Watchlist' }, { id: 'art-scanning', title: 'New Release Scanning' }, { id: 'art-settings', title: 'Watchlist Settings' } ], content: () => `

Watchlist

The watchlist tracks artists you want to follow for new releases. When SoulSync scans your watchlist, it checks each artist's discography and adds any new tracks to your wishlist for downloading.

New Release Scanning

Click Scan for New Releases or let the system automation handle it (runs every 24 hours). The scan shows a live activity panel with:

Watchlist Settings

Per-Artist Settings — Click the config icon on any watched artist to customize what release types to include: Albums, EPs, Singles, Live versions, Remixes, Acoustic versions, Compilations.

Global Settings — Override all per-artist settings at once. Enable Global Override, select which types to include, and all watchlist scans will follow the global config.

` }, { id: 'automations', title: 'Automations', icon: '/static/automation.png', children: [ { id: 'auto-overview', title: 'Overview' }, { id: 'auto-builder', title: 'Builder' }, { id: 'auto-triggers', title: 'All Triggers' }, { id: 'auto-actions', title: 'All Actions' }, { id: 'auto-then', title: 'Then-Actions & Signals' }, { id: 'auto-system', title: 'System Automations' } ], content: () => `

Overview

Automations let you schedule tasks and react to events with a visual WHEN → DO → THEN builder. Create custom workflows like "When a download completes, update the database, then notify me on Discord."

Each automation card shows its trigger/action flow, last run time, next scheduled run (with countdown), and a Run Now button for instant execution.

Builder

Click + New Automation to open the builder. Drag or click blocks from the sidebar into the three slots:

  1. WHEN (Trigger) — What event starts this automation
  2. DO (Action) — What task to perform. Optionally add a delay (minutes) before executing.
  3. THEN (Post-Action) — Up to 3 notification or signal actions after the DO completes

Add Conditions to filter when the automation runs. Match modes: All (AND) or Any (OR). Operators: contains, equals, starts_with, not_contains.

All Triggers

TriggerDescription
ScheduleRun on a timer interval (minutes/hours/days)
Daily TimeRun every day at a specific time
Weekly TimeRun on specific weekdays at a set time
App StartedFires when SoulSync starts up
Track DownloadedWhen a track finishes downloading
Download FailedWhen a track permanently fails to download
Download QuarantinedWhen AcoustID verification rejects a download
Batch CompleteWhen an album/playlist batch download finishes
Wishlist Item AddedWhen a track is added to the wishlist
Wishlist Processing DoneWhen auto-wishlist processing finishes
New Release FoundWhen a watchlist scan finds new music
Watchlist Scan DoneWhen the full watchlist scan completes
Artist Watched/UnwatchedWhen an artist is added to or removed from the watchlist
Playlist SyncedWhen a playlist sync completes
Playlist ChangedWhen a mirrored playlist detects changes from the source
Discovery CompleteWhen playlist track discovery finishes
Library Scan DoneWhen a media library scan finishes
Database UpdatedWhen a library database refresh finishes
Quality/Duplicate Scan DoneWhen quality or duplicate scanning finishes
Import CompleteWhen an album/track import finishes
Signal ReceivedCustom signal fired by another automation

All Actions

ActionDescription
Process WishlistRetry failed downloads (all, albums only, or singles only)
Scan WatchlistCheck watched artists for new releases
Cleanup WishlistRemove duplicate/owned tracks from wishlist
Scan LibraryTrigger a media server library scan
Update DatabaseRefresh library database (incremental or full)
Deep Scan LibraryFull library comparison without losing enrichment data
Refresh Mirrored PlaylistRe-fetch playlist tracks from the source
Sync PlaylistSync a specific playlist to your media server
Discover PlaylistFind official metadata for playlist tracks
Run Duplicate CleanerScan for and remove duplicate files
Run Quality ScanScan for low-quality audio files
Clear QuarantineDelete all quarantined files
Update DiscoveryRefresh the discovery artist pool
Backup DatabaseCreate a timestamped database backup
Full CleanupClear quarantine, queue, staging, and search history
Notify OnlyNo action — just trigger notifications

Then-Actions & Signals

After the DO action completes, up to 3 THEN actions run:

All notification messages support variable substitution: {name}, {status}, {time}, {run_count}, and context-specific variables from the action result.

ℹ️
Signal chaining lets you build multi-step workflows. Safety features include cycle detection (DFS), a 5-level chain depth limit, and a 10-second cooldown between signal fires.

System Automations

SoulSync ships with 10 built-in automations that handle routine maintenance. You can enable/disable them and modify their configs, but you can't delete them or rename them.

AutomationSchedule
Auto-Process WishlistEvery 30 minutes
Auto-Scan WatchlistEvery 24 hours
Auto-Scan After DownloadsOn batch_complete event
Auto-Update DatabaseOn library_scan_completed event
Refresh Beatport CacheEvery 24 hours
Clean Search HistoryEvery 1 hour
Clean Completed DownloadsEvery 5 minutes
Auto-Deep Scan LibraryEvery 7 days
Auto-Backup DatabaseEvery 3 days
Full CleanupEvery 12 hours
` }, { id: 'library', title: 'Music Library', icon: '/static/library.png', children: [ { id: 'lib-standard', title: 'Standard View' }, { id: 'lib-enhanced', title: 'Enhanced Library Manager' }, { id: 'lib-matching', title: 'Service Matching' }, { id: 'lib-tags', title: 'Write Tags to File' }, { id: 'lib-bulk', title: 'Bulk Operations' }, { id: 'lib-missing', title: 'Download Missing Tracks' } ], content: () => `

Standard View

The Library page shows all artists in your collection as cards with images, album/track counts, and service badges (Spotify, MusicBrainz, Deezer, AudioDB, iTunes, Last.fm, Genius) indicating which services have matched this artist.

Use the search bar, alphabet navigation (A–Z, #), and watchlist filter (All/Watched/Unwatched) to browse. Click any artist card to view their discography.

The artist detail page shows albums, EPs, and singles as cards with completion percentages. Filter by category, content type (live/compilations/featured), or status (owned/missing). At the top, View on buttons link to the artist on each matched external service.

Enhanced Library Manager

Toggle Enhanced on any artist's detail page to access the professional library management tool:

Service Matching

In the Enhanced view, each artist, album, and track shows match status chips for all 7 services. Click any chip to manually search and link the correct external ID. Run per-service enrichment from the dropdown to pull in metadata from a specific source.

Matched services show as clickable badges linking to the entity on that service's website.

Write Tags to File

Sync your database metadata to actual audio file tags:

  1. Click the pencil icon on any track, or use Write All Tags for an entire album, or select tracks and use the bulk bar's Write Tags
  2. A tag preview modal shows a diff table: current file tags vs. database values
  3. Optionally enable Embed cover art and Sync to server
  4. Click Write Tags to apply changes to the file

Supports MP3, FLAC, OGG, and M4A via Mutagen. After writing, optional server sync pushes metadata to Plex (per-track update), Jellyfin (library scan), or Navidrome (auto-detects).

Bulk Operations

Select tracks across multiple albums using the checkboxes. The bulk bar appears showing the selection count with actions:

Download Missing Tracks

From any album card showing missing tracks, click Download Missing to open a modal listing all tracks not in your library. Select tracks, choose a download source, and start the download. Progress is tracked per-track with status indicators.

` }, { id: 'import', title: 'Import Music', icon: '/static/import.png', children: [ { id: 'imp-setup', title: 'Staging Setup' }, { id: 'imp-workflow', title: 'Import Workflow' } ], content: () => `

Staging Setup

Set your staging folder path in Settings → Download Settings. Place audio files you want to import into this folder. SoulSync scans the folder and detects albums from the file structure.

The import page header shows the total files in staging and their combined size.

Import Workflow

  1. Place audio files in your staging folder
  2. Navigate to the Import page — SoulSync detects and suggests album matches
  3. Search for the correct album on Spotify/iTunes if the suggestion is wrong
  4. Match tracks — Drag-and-drop staged files onto album track slots, or let auto-match attempt it
  5. Review the match and click Confirm to import — files are tagged, organized, and added to your library

The Singles tab handles individual tracks that aren't part of an album.

` }, { id: 'player', title: 'Media Player', icon: '/static/library.png', children: [ { id: 'player-controls', title: 'Playback Controls' }, { id: 'player-queue', title: 'Queue & Smart Radio' }, { id: 'player-shortcuts', title: 'Keyboard Shortcuts' } ], content: () => `

Playback Controls

The sidebar media player is always visible when a track is loaded. It shows album art, track info, a seekable progress bar, and playback controls (play/pause, previous, next, volume, repeat, shuffle).

Click the sidebar player to open the Now Playing modal — a full-screen experience with large album art, ambient glow (dominant color from cover art), a frequency-driven audio visualizer, and expanded controls.

Queue & Smart Radio

Add tracks to the queue from the Enhanced Library Manager or download results. Manage the queue in the Now Playing modal: reorder, remove individual tracks, or clear all.

Smart Radio mode (toggle in queue header) automatically adds similar tracks when the queue runs out, based on genre, mood, style, and artist similarity. Playback continues seamlessly.

Repeat modes: Off → Repeat All (loop queue) → Repeat One. Shuffle randomizes the next track from the remaining queue.

Keyboard Shortcuts

KeyAction
SpacePlay / Pause
Next track
Previous track
Volume up
Volume down
MMute / Unmute

Media Session API — SoulSync integrates with your OS media controls (lock screen, system tray) for play/pause, next/previous, and seek.

` }, { id: 'settings', title: 'Settings', icon: '/static/settings.png', children: [ { id: 'set-services', title: 'Service Credentials' }, { id: 'set-download', title: 'Download Settings' }, { id: 'set-quality', title: 'Quality Profiles' }, { id: 'set-other', title: 'Other Settings' } ], content: () => `

Service Credentials

Configure credentials for each external service. All fields are saved to your local config — nothing is sent to external servers except during actual API calls.

Download Settings

Quality Profiles

Set your preferred audio quality with presets (Audiophile/Balanced/Space Saver) or custom configuration per format. Each format has a configurable bitrate range and priority order. Enable Fallback to accept any quality when nothing matches.

Other Settings

` }, { id: 'profiles', title: 'Multi-Profile', icon: '/static/settings.png', children: [ { id: 'prof-overview', title: 'How Profiles Work' }, { id: 'prof-manage', title: 'Managing Profiles' } ], content: () => `

How Profiles Work

SoulSync supports Netflix-style multiple profiles for shared households. Each profile gets its own:

Shared across all profiles: Music library (files and metadata), service credentials, settings, and automations.

💡
Single-user installs see no changes until a second profile is created. The first profile is automatically the admin.

Managing Profiles

` }, { id: 'api', title: 'REST API', icon: '/static/settings.png', children: [ { id: 'api-auth', title: 'Authentication' }, { id: 'api-endpoints', title: 'Key Endpoints' } ], content: () => `

Authentication

Generate API keys in Settings → API Keys. Use them via header or query parameter:

Keys use a sk_ prefix. The raw key is shown once at creation; only a SHA-256 hash is stored.

Key Endpoints

EndpointDescription
GET /api/system/statusUptime and service connectivity
GET /api/system/statsLibrary counts and sizes
GET /api/library/artistsPaginated artist list with filters
GET /api/artist-detail/{id}Full artist info and discography
POST /api/downloadStart a download
GET /api/downloads/statusActive download status
POST /api/searchSearch Soulseek
POST /api/enhanced-searchEnhanced metadata search
GET /api/automationsList all automations
POST /api/database/backupCreate a backup

The full API has 90+ endpoints. Use reverse proxy support for external access.

` } ]; let _docsInitialized = false; function initializeDocsPage() { if (_docsInitialized) return; _docsInitialized = true; const nav = document.getElementById('docs-nav'); const content = document.getElementById('docs-content'); if (!nav || !content) return; // Build sidebar nav let navHTML = ''; DOCS_SECTIONS.forEach(section => { navHTML += `
`; navHTML += `
`; navHTML += ``; navHTML += `${section.title}`; navHTML += ``; navHTML += `
`; if (section.children && section.children.length) { navHTML += `
`; section.children.forEach(child => { navHTML += `
${child.title}
`; }); navHTML += `
`; } navHTML += `
`; }); nav.innerHTML = navHTML; // Build content let contentHTML = ''; DOCS_SECTIONS.forEach(section => { contentHTML += `
`; contentHTML += `

`; contentHTML += ``; contentHTML += `${section.title}`; contentHTML += `

`; contentHTML += section.content(); contentHTML += `
`; }); content.innerHTML = contentHTML; // Section title click → expand/collapse children + scroll nav.querySelectorAll('.docs-nav-section-title').forEach(title => { title.addEventListener('click', () => { const sectionId = title.dataset.target; const children = nav.querySelector(`.docs-nav-children[data-parent="${sectionId}"]`); // Toggle expanded const isExpanded = title.classList.contains('expanded'); // Collapse all nav.querySelectorAll('.docs-nav-section-title').forEach(t => t.classList.remove('expanded', 'active')); nav.querySelectorAll('.docs-nav-children').forEach(c => c.classList.remove('expanded')); if (!isExpanded) { title.classList.add('expanded', 'active'); if (children) children.classList.add('expanded'); } // Scroll to section const target = document.getElementById('docs-' + sectionId); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }); }); // Child click → scroll to subsection nav.querySelectorAll('.docs-nav-child').forEach(child => { child.addEventListener('click', () => { nav.querySelectorAll('.docs-nav-child').forEach(c => c.classList.remove('active')); child.classList.add('active'); const target = document.getElementById(child.dataset.target); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }); }); // Search filter const searchInput = document.getElementById('docs-search'); if (searchInput) { searchInput.addEventListener('input', () => { const q = searchInput.value.toLowerCase().trim(); document.querySelectorAll('.docs-section').forEach(sec => { if (!q) { sec.style.display = ''; return; } sec.style.display = sec.textContent.toLowerCase().includes(q) ? '' : 'none'; }); // Also filter nav nav.querySelectorAll('.docs-nav-section').forEach(navSec => { const sectionId = navSec.dataset.section; const docSection = document.getElementById('docs-' + sectionId); navSec.style.display = (!q || (docSection && docSection.style.display !== 'none')) ? '' : 'none'; }); }); } // Scroll spy — highlight active section in nav const docsContent = document.getElementById('docs-content'); if (docsContent) { docsContent.addEventListener('scroll', () => { const scrollTop = docsContent.scrollTop + 100; let activeSection = null; let activeChild = null; DOCS_SECTIONS.forEach(section => { const el = document.getElementById('docs-' + section.id); if (el && el.offsetTop <= scrollTop) { activeSection = section.id; } if (section.children) { section.children.forEach(child => { const childEl = document.getElementById(child.id); if (childEl && childEl.offsetTop <= scrollTop) { activeChild = child.id; } }); } }); // Update nav highlighting nav.querySelectorAll('.docs-nav-section-title').forEach(t => { const isActive = t.dataset.target === activeSection; t.classList.toggle('active', isActive); t.classList.toggle('expanded', isActive); }); nav.querySelectorAll('.docs-nav-children').forEach(c => { c.classList.toggle('expanded', c.dataset.parent === activeSection); }); nav.querySelectorAll('.docs-nav-child').forEach(c => { c.classList.toggle('active', c.dataset.target === activeChild); }); }); } // Auto-expand first section const firstTitle = nav.querySelector('.docs-nav-section-title'); if (firstTitle) { firstTitle.classList.add('expanded', 'active'); const firstChildren = nav.querySelector('.docs-nav-children'); if (firstChildren) firstChildren.classList.add('expanded'); } }