feat: add preferences store with cookie source and output directory
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
203
src/lib/components/PreferencesPanel.svelte
Normal file
203
src/lib/components/PreferencesPanel.svelte
Normal 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>
|
||||
43
src/lib/stores/preferences.svelte.ts
Normal file
43
src/lib/stores/preferences.svelte.ts
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user