9.1 KiB
Component Reference
Complete guide to all Svelte components in SubPlotter.
App.svelte
Location: frontend/src/App.svelte
Main application component with layout and routing.
Features
- Tab-based navigation (Scanner / Settings)
- Health check on mount
- API configuration warning banner
- Gradient header with branding
- Responsive footer
State
currentView: 'scanner' | 'settings'
apiConfigured: boolean
selectedFiles: string[]
scanPanelKey: number // Forces refresh after processing
Routing
Simple state-based routing - no router library needed:
function navigateTo(view) {
currentView = view
}
Styling
- Purple gradient header
- Dark mode support
- Responsive breakpoints at 768px
- Max width: 1200px
SettingsPanel.svelte
Location: frontend/src/components/SettingsPanel.svelte
Configuration interface for API keys and behavior.
Props
None (standalone component)
Features
- API key input (password type)
- Directory path input
- Duration numeric input
- Load settings on mount
- Save with feedback messages
- Loading and error states
State
apiKey: string
defaultDirectory: string
duration: number
loading: boolean
saving: boolean
error: string | null
successMessage: string | null
API Calls
getSettings()on mountupdateSettings()on save
Usage
<SettingsPanel />
ScanPanel.svelte
Location: frontend/src/components/ScanPanel.svelte
Directory scanning interface with results display.
Props (Exported)
selectedFilePaths: string[] // Bind to get selected files
Features
- Directory input field
- Start scan button
- Scanning progress indicator
- Last scan timestamp
- File count display
- Integrates MovieList component
- Loads previous scan results on mount
State
directory: string
files: FileInfo[]
scanning: boolean
error: string | null
lastScan: string | null
selectedFilePaths: string[]
API Calls
getSettings()on mount (for default directory)getScanStatus()on mount (load cached results)startScan(directory)on button click
Usage
<script>
let selected = []
</script>
<ScanPanel bind:selectedFilePaths={selected} />
File Format
{
path: string,
name: string,
has_plot: boolean,
status: string,
summary: string,
selected: boolean
}
MovieList.svelte
Location: frontend/src/components/MovieList.svelte
Display and selection of scanned movie files.
Props
files: FileInfo[] // Array of file objects
onSelectionChange: (selectedPaths: string[]) => void
Features
- Empty state message
- Select all checkbox
- Individual file checkboxes
- File name display
- Status badges
- Summary preview (truncated to 100 chars)
- Selected count in header
- Scrollable list (max-height: 500px)
Methods
toggleSelection(file) // Toggle individual file
toggleAll() // Toggle all files
formatSummary(summary) // Truncate long summaries
Usage
<MovieList
{files}
onSelectionChange={(paths) => console.log(paths)}
/>
Styling
- Hover effects on rows
- Selected rows highlighted (blue tint)
- Dark mode support
ActionToolbar.svelte
Location: frontend/src/components/ActionToolbar.svelte
Processing controls and results display.
Props
selectedFiles: string[] // Array of file paths
disabled: boolean // Disable when no API key
Events
'complete' // Dispatched after successful processing
// Detail: { results: ProcessResult[] }
Features
- Large prominent action button
- Shows selected file count
- Confirmation modal before processing
- Results modal after processing
- Success/failure statistics
- Individual result breakdown
- Error handling and display
State
processing: boolean
showConfirmation: boolean
duration: number
results: ProcessResult[] | null
error: string | null
Modals
Confirmation Modal:
- File count display
- Warning about file modification
- Backup information
- Cancel / Confirm buttons
Results Modal:
- Success/failure counts
- Per-file results list
- Color-coded success/failure
- Close button
API Calls
getSettings()before processing (get duration)processFiles(files, duration)on confirm
Usage
<ActionToolbar
{selectedFiles}
disabled={!apiConfigured}
on:complete={handleComplete}
/>
StatusBadge.svelte
Location: frontend/src/components/StatusBadge.svelte
Reusable status indicator component.
Props
status: string // Default: 'Not Loaded'
Supported Statuses
'Has Plot'- Green (file already processed)'Processed'- Green (just processed)'Not Loaded'- Gray (not yet processed)'Error'- Red (processing failed)'Skipped'- Yellow (skipped, e.g., already has plot)'Processing'- Blue with pulse animation
Styling
- Rounded badge design
- Color-coded by status
- Dark mode variants
- Pulse animation for processing state
Usage
<StatusBadge status="Has Plot" />
<StatusBadge status="Processing" />
<StatusBadge status="Error" />
Component Hierarchy
App.svelte
├── SettingsPanel.svelte (when currentView === 'settings')
└── (when currentView === 'scanner')
├── ScanPanel.svelte
│ └── MovieList.svelte
│ └── StatusBadge.svelte (multiple instances)
└── ActionToolbar.svelte
Common Patterns
Loading States
{#if loading}
<div class="loading">Loading...</div>
{:else}
<!-- Content -->
{/if}
Error Display
{#if error}
<div class="message message-error">{error}</div>
{/if}
Success Messages
{#if successMessage}
<div class="message message-success">{successMessage}</div>
{/if}
Modal Pattern
{#if showModal}
<div class="modal-overlay" on:click={closeModal}>
<div class="modal" on:click|stopPropagation>
<!-- Modal content -->
</div>
</div>
{/if}
Reactive Statements
$: selectedCount = files.filter(f => f.selected).length
$: hasSelection = selectedFiles.length > 0
Styling Guidelines
Color Palette
- Primary:
#3b82f6(blue) - Success:
#d1fae5/#065f46(green) - Error:
#fee2e2/#991b1b(red) - Warning:
#fef3c7/#92400e(yellow) - Processing:
#dbeafe/#1e40af(blue)
Dark Mode
All components support dark mode via:
@media (prefers-color-scheme: dark) {
/* Dark styles */
}
Layout
- Container max-width: 1200px
- Standard padding: 2rem (desktop), 1rem (mobile)
- Standard gap: 0.5rem to 1rem
- Border radius: 6px (small), 8px (large)
Transitions
- Duration: 150ms to 200ms
- Timing:
cubic-bezier(0.4, 0, 0.2, 1) - Properties: background, border, color
Component Communication
Parent → Child (Props)
<MovieList {files} disabled={true} />
Child → Parent (Events)
<!-- Child -->
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
function handleClick() {
dispatch('complete', { data: 'value' })
}
</script>
<!-- Parent -->
<ActionToolbar on:complete={handleComplete} />
Two-Way Binding
<ScanPanel bind:selectedFilePaths={selected} />
Callbacks
<MovieList onSelectionChange={(paths) => { ... }} />
Best Practices
- Keep components small - Each component does one thing well
- Explicit state - No hidden state or magic
- Clear props - Document all props and their types
- Error handling - Always handle loading and error states
- Accessibility - Use semantic HTML and labels
- Responsive - Test at mobile and desktop sizes
- Dark mode - Support system preference
- No external state - Use props and events, not stores
Adding New Components
Template for new component:
<script>
/**
* ComponentName - Brief description
* Props:
* propName: type - description
*/
import { onMount } from 'svelte'
export let propName = 'default'
let loading = false
let error = null
onMount(async () => {
// Initialization
})
async function handleAction() {
loading = true
error = null
try {
// API call or logic
} catch (err) {
error = err.message
} finally {
loading = false
}
}
</script>
<div class="component-name">
{#if loading}
<div class="loading">Loading...</div>
{:else if error}
<div class="error">{error}</div>
{:else}
<!-- Content -->
{/if}
</div>
<style>
.component-name {
/* Styles */
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
/* Dark styles */
}
</style>
Testing Components
While no test framework is included, you can test:
- Manually - Run dev server and interact
- Browser DevTools - Check props and state
- Console logs - Add temporary logging
- Network tab - Verify API calls
- Error scenarios - Test with invalid data
For automated testing, consider:
- Vitest + Svelte Testing Library
- Playwright for E2E tests