feat: add preferences store with cookie source and output directory

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-21 10:54:42 -04:00
parent a62cd05f6c
commit f74eb17039
3 changed files with 263 additions and 0 deletions

View File

@@ -6,17 +6,29 @@
import ClipList from '$lib/components/ClipList.svelte'; import ClipList from '$lib/components/ClipList.svelte';
import StatusBar from '$lib/components/StatusBar.svelte'; import StatusBar from '$lib/components/StatusBar.svelte';
import SetupWizard from '$lib/components/SetupWizard.svelte'; import SetupWizard from '$lib/components/SetupWizard.svelte';
import PreferencesPanel from '$lib/components/PreferencesPanel.svelte';
import { loadPreferences } from '$lib/stores/preferences.svelte';
let showSetupWizard = $state(true); let showSetupWizard = $state(true);
let showPreferences = $state(false);
$effect(() => {
loadPreferences();
});
</script> </script>
{#if showSetupWizard} {#if showSetupWizard}
<SetupWizard onComplete={() => (showSetupWizard = false)} /> <SetupWizard onComplete={() => (showSetupWizard = false)} />
{/if} {/if}
{#if showPreferences}
<PreferencesPanel onClose={() => (showPreferences = false)} />
{/if}
<div class="app-shell"> <div class="app-shell">
<header class="toolbar"> <header class="toolbar">
<UrlInput /> <UrlInput />
<button class="prefs-btn" onclick={() => (showPreferences = true)} title="Preferences">⚙</button>
</header> </header>
<main class="content"> <main class="content">
@@ -51,4 +63,9 @@
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
} }
.prefs-btn {
font-size: 18px;
padding: 4px 8px;
}
</style> </style>

View File

@@ -0,0 +1,203 @@
<script lang="ts">
import { open } from '@tauri-apps/plugin-dialog';
import {
preferences,
setOutputDirectory,
setCookieSource,
type CookieSource,
} from '$lib/stores/preferences.svelte';
let { onClose }: { onClose: () => void } = $props();
let cookieType = $derived(preferences.cookieSource.type);
let cookieFilePath = $state(
preferences.cookieSource.type === 'file' ? preferences.cookieSource.path : ''
);
async function pickOutputDirectory() {
const selected = await open({
directory: true,
defaultPath: preferences.outputDirectory,
title: 'Choose output directory',
});
if (selected) {
await setOutputDirectory(selected as string);
}
}
async function handleCookieTypeChange(newType: string) {
let newSource: CookieSource;
switch (newType) {
case 'browser':
newSource = { type: 'browser', browser: 'firefox' };
break;
case 'file':
newSource = { type: 'file', path: cookieFilePath || '' };
break;
case 'none':
newSource = { type: 'none' };
break;
default: {
const _exhaustive: never = newType as never;
throw new Error(`Unhandled cookie type: ${_exhaustive}`);
}
}
await setCookieSource(newSource);
}
async function pickCookieFile() {
const selected = await open({
filters: [{ name: 'Cookie files', extensions: ['txt'] }],
title: 'Select cookies.txt file',
});
if (selected) {
cookieFilePath = selected as string;
await setCookieSource({ type: 'file', path: cookieFilePath });
}
}
</script>
<div class="overlay" role="presentation" onclick={onClose}>
<div class="panel" role="dialog" onclick={(e) => e.stopPropagation()}>
<h2>Preferences</h2>
<section>
<h3>Output Directory</h3>
<div class="row">
<code class="path">{preferences.outputDirectory}</code>
<button onclick={pickOutputDirectory}>Change…</button>
</div>
</section>
<section>
<h3>YouTube Cookie Source</h3>
<div class="radio-group">
<label>
<input
type="radio"
name="cookie"
value="browser"
checked={cookieType === 'browser'}
onchange={() => handleCookieTypeChange('browser')}
/>
Firefox (recommended)
</label>
<label>
<input
type="radio"
name="cookie"
value="file"
checked={cookieType === 'file'}
onchange={() => handleCookieTypeChange('file')}
/>
cookies.txt file
{#if cookieType === 'file'}
<div class="file-picker">
<code class="path">{cookieFilePath || 'No file selected'}</code>
<button onclick={pickCookieFile}>Browse…</button>
</div>
{/if}
</label>
<label>
<input
type="radio"
name="cookie"
value="none"
checked={cookieType === 'none'}
onchange={() => handleCookieTypeChange('none')}
/>
None (public content only)
</label>
</div>
</section>
<div class="actions">
<button class="primary" onclick={onClose}>Done</button>
</div>
</div>
</div>
<style>
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 900;
}
.panel {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px 32px;
max-width: 480px;
width: 100%;
}
h2 {
margin-bottom: 16px;
}
h3 {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
section {
margin-bottom: 20px;
}
.row {
display: flex;
align-items: center;
gap: 12px;
}
.path {
background: var(--bg-primary);
padding: 4px 8px;
border-radius: 4px;
font-size: 13px;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.radio-group label {
display: flex;
align-items: flex-start;
gap: 8px;
font-size: 14px;
cursor: pointer;
}
.file-picker {
display: flex;
align-items: center;
gap: 8px;
margin-top: 4px;
margin-left: 20px;
}
.actions {
display: flex;
justify-content: flex-end;
}
.primary {
background: var(--accent);
color: var(--bg-primary);
font-weight: 600;
}
</style>

View File

@@ -0,0 +1,43 @@
import { load, Store } from '@tauri-apps/plugin-store';
import type { CookieSource } from '$lib/bindings/dependencies';
export type { CookieSource };
export interface Preferences {
outputDirectory: string;
cookieSource: CookieSource;
}
const DEFAULT_PREFERENCES: Preferences = {
outputDirectory: '~/',
cookieSource: { type: 'browser', browser: 'firefox' },
};
let store: Store | null = null;
export const preferences = $state<Preferences>({ ...DEFAULT_PREFERENCES });
export async function loadPreferences(): Promise<void> {
store = await load('preferences.json', { autoSave: true });
const savedDir = await store.get<string>('outputDirectory');
const savedCookie = await store.get<CookieSource>('cookieSource');
if (savedDir) preferences.outputDirectory = savedDir;
if (savedCookie) preferences.cookieSource = savedCookie;
}
export async function savePreferences(): Promise<void> {
if (!store) return;
await store.set('outputDirectory', preferences.outputDirectory);
await store.set('cookieSource', preferences.cookieSource);
await store.save();
}
export async function setOutputDirectory(dir: string): Promise<void> {
preferences.outputDirectory = dir;
await savePreferences();
}
export async function setCookieSource(source: CookieSource): Promise<void> {
preferences.cookieSource = source;
await savePreferences();
}