1
0
Fork 0

Update to svelte 5

This commit is contained in:
Leon Grünewald 2024-10-23 19:18:22 +02:00
parent 82bf565ce3
commit 007246da68
14 changed files with 710 additions and 699 deletions

View file

@ -10,28 +10,29 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.6.0",
"@fortawesome/free-regular-svg-icons": "^6.5.2", "@fortawesome/free-regular-svg-icons": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.6.0",
"@tauri-apps/api": "^1.5.6" "@tauri-apps/api": "^2.0.3",
"@tauri-apps/plugin-shell": "~2"
}, },
"devDependencies": { "devDependencies": {
"@skeletonlabs/skeleton": "^2.10.2", "@skeletonlabs/skeleton": "^2.10.2",
"@skeletonlabs/tw-plugin": "^0.3.1", "@skeletonlabs/tw-plugin": "^0.3.1",
"@sveltejs/adapter-auto": "^3.2.1", "@sveltejs/adapter-auto": "^3.3.0",
"@sveltejs/adapter-static": "^3.0.1", "@sveltejs/adapter-static": "^3.0.5",
"@sveltejs/kit": "2.5.26", "@sveltejs/kit": "^2.7.2",
"@sveltejs/vite-plugin-svelte": "4.0.0-next.7", "@sveltejs/vite-plugin-svelte": "^4.0.0",
"@tauri-apps/cli": "^1.5.14", "@tauri-apps/cli": "^2.0.4",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.20",
"postcss": "^8.4.38", "postcss": "^8.4.47",
"postcss-load-config": "^5.1.0", "postcss-load-config": "^5.1.0",
"svelte": "5.0.0-next.244", "svelte": "^5.0.5",
"svelte-check": "^3.7.1", "svelte-check": "^4.0.0",
"svelte-fa": "^4.0.2", "svelte-fa": "^4.0.3",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.14",
"typescript": "^5.4.5", "typescript": "^5.6.3",
"vite": "^5.4.3" "vite": "^5.4.10"
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,9 @@
import { RangeSlider, Tab, TabGroup, InputChip, SlideToggle } from '@skeletonlabs/skeleton'; import { RangeSlider, Tab, TabGroup, InputChip, SlideToggle } from '@skeletonlabs/skeleton';
import { imageGridColumns, postGridPreviewQuality, blacklist, limitImageHeight } from '$lib/settings'; import { imageGridColumns, postGridPreviewQuality, blacklist, limitImageHeight } from '$lib/settings';
let tabPos = 0; let tabPos = $state(0);
let showApikey = false; let showApikey = $state(false);
let apikey = ''; let apikey = $state('');
</script> </script>
<div class="flex flex-col"> <div class="flex flex-col">
<h2 class="h2 mb-2 text-dark-token">Settings</h2> <h2 class="h2 mb-2 text-dark-token">Settings</h2>
@ -14,75 +14,77 @@
<Tab class="text-dark-token" bind:group={tabPos} name="tab3" value={2}>User</Tab> <Tab class="text-dark-token" bind:group={tabPos} name="tab3" value={2}>User</Tab>
<Tab class="text-dark-token" bind:group={tabPos} name="tab4" value={3}>Chats</Tab> <Tab class="text-dark-token" bind:group={tabPos} name="tab4" value={3}>Chats</Tab>
<svelte:fragment slot="panel"> {#snippet panel()}
{#if tabPos === 0}
<div class="flex flex-col gap-3">
<h3 class="h3 text-dark-token font-bold">Image</h3>
<SlideToggle name="image-limit-height" bind:checked={$limitImageHeight}><span class="text-dark-token">Limit image height</span></SlideToggle>
<h3 class="h3 text-dark-token font-bold">Grid</h3> {#if tabPos === 0}
<div class="basis-1/2"> <div class="flex flex-col gap-3">
<RangeSlider accent="text-dark-token" name="grid-columns" min={1} max={12} bind:value={$imageGridColumns} ticked={true}> <h3 class="h3 text-dark-token font-bold">Image</h3>
<div class="flex flex-grow justify-between items-center dark"> <SlideToggle name="image-limit-height" bind:checked={$limitImageHeight}><span class="text-dark-token">Limit image height</span></SlideToggle>
<div class="text-dark-token font-bold mb-1">Grid Columns</div>
<div class="text-xs text-dark-token">{$imageGridColumns} / {12}</div>
</div>
</RangeSlider>
</div>
<div class="basis-1/2">
<label class="label">
<span class="text-dark-token font-bold">Preview type</span>
<select class="select" bind:value={$postGridPreviewQuality}>
<option value="preview">Preview</option>
<option value="sample">Sample</option>
</select>
</label>
</div>
</div>
{/if}
{#if tabPos === 1} <h3 class="h3 text-dark-token font-bold">Grid</h3>
<div class="flex flex-col gap-3"> <div class="basis-1/2">
<div class=""> <RangeSlider accent="text-dark-token" name="grid-columns" min={1} max={12} bind:value={$imageGridColumns} ticked={true}>
<label class="label"> <div class="flex flex-grow justify-between items-center dark">
<span class="text-dark-token font-bold">e621 Username</span> <div class="text-dark-token font-bold mb-1">Grid Columns</div>
<input class="input" /> <div class="text-xs text-dark-token">{$imageGridColumns} / {12}</div>
</label> </div>
</RangeSlider>
</div>
<div class="basis-1/2">
<label class="label">
<span class="text-dark-token font-bold">Preview type</span>
<select class="select" bind:value={$postGridPreviewQuality}>
<option value="preview">Preview</option>
<option value="sample">Sample</option>
</select>
</label>
</div>
</div> </div>
<div class=""> {/if}
<label class="label">
<span class="text-dark-token font-bold">e621 Apikey</span>
{#if showApikey}
<input class="input" type="text" bind:value={apikey} />
{:else}
<input class="input" type="password" bind:value={apikey} />
{/if}
</label>
<label class="label">
<span class="text-dark-token font-bold">Show apikey</span>
<input class="checkbox" type="checkbox" bind:checked={showApikey} />
</label>
</div>
</div>
{/if}
{#if tabPos === 2} {#if tabPos === 1}
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<div class=""> <div class="">
<p class="text-dark-token font-bold">Blacklist</p> <label class="label">
<InputChip name="blacklist" label="Blacklist" bind:value={$blacklist} /> <span class="text-dark-token font-bold">e621 Username</span>
<input class="input" />
</label>
</div>
<div class="">
<label class="label">
<span class="text-dark-token font-bold">e621 Apikey</span>
{#if showApikey}
<input class="input" type="text" bind:value={apikey} />
{:else}
<input class="input" type="password" bind:value={apikey} />
{/if}
</label>
<label class="label">
<span class="text-dark-token font-bold">Show apikey</span>
<input class="checkbox" type="checkbox" bind:checked={showApikey} />
</label>
</div>
</div> </div>
</div> {/if}
{/if}
{#if tabPos === 3} {#if tabPos === 2}
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<div class=""> <div class="">
<p class="text-dark-token font-bold">Chats</p> <p class="text-dark-token font-bold">Blacklist</p>
<InputChip name="blacklist" label="Blacklist" bind:value={$blacklist} /> <InputChip name="blacklist" label="Blacklist" bind:value={$blacklist} />
</div>
</div> </div>
</div> {/if}
{/if}
</svelte:fragment> {#if tabPos === 3}
<div class="flex flex-col gap-3">
<div class="">
<p class="text-dark-token font-bold">Chats</p>
<InputChip name="blacklist" label="Blacklist" bind:value={$blacklist} />
</div>
</div>
{/if}
{/snippet}
</TabGroup> </TabGroup>
</div> </div>

View file

@ -3,8 +3,14 @@ import {faArrowLeft} from '@fortawesome/free-solid-svg-icons';
import {Fa} from 'svelte-fa'; import {Fa} from 'svelte-fa';
import {AppRailAnchor} from '@skeletonlabs/skeleton'; import {AppRailAnchor} from '@skeletonlabs/skeleton';
export let popCount = 1; /**
let href; * @typedef {Object} Props
* @property {number} [popCount]
*/
/** @type {Props} */
let { popCount = 1 } = $props();
let href = $state();
let pathnameParts = window.location.pathname.split('/'); let pathnameParts = window.location.pathname.split('/');
for(let i = 0; i < popCount; i++) { for(let i = 0; i < popCount; i++) {

View file

@ -1,18 +1,20 @@
<script> <script>
import { preventDefault } from 'svelte/legacy';
import { showSearch, loading } from '$lib/stores'; import { showSearch, loading } from '$lib/stores';
import { lastSearch } from '$lib/settings'; import { lastSearch } from '$lib/settings';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
let searchValue = $lastSearch; let searchValue = $state($lastSearch);
</script> </script>
{#if $showSearch} {#if $showSearch}
<form class="flex flex-row gap-2" <form class="flex flex-row gap-2"
on:submit|preventDefault={() => { onsubmit={preventDefault(() => {
$loading = true; $loading = true;
goto('/') goto('/')
lastSearch.set(searchValue); lastSearch.set(searchValue);
}}> })}>
<input class="input py-1 px-2 w-96" type="text" placeholder="Search" bind:value={searchValue} /> <input class="input py-1 px-2 w-96" type="text" placeholder="Search" bind:value={searchValue} />
<button class="btn btn-md variant-filled" type="submit">Submit</button> <button class="btn btn-md variant-filled" type="submit">Submit</button>
</form> </form>

View file

@ -2,12 +2,12 @@
import { showSearch, loading } from '$lib/stores'; import { showSearch, loading } from '$lib/stores';
import { postGridPreviewQuality, imageGridColumns } from '$lib/settings'; import { postGridPreviewQuality, imageGridColumns } from '$lib/settings';
export let posts = []; let { posts = [] } = $props();
</script> </script>
<div class="grid gap-3" style={`grid-template-columns: repeat(${$imageGridColumns}, minmax(0, 1fr))`}> <div class="grid gap-3" style={`grid-template-columns: repeat(${$imageGridColumns}, minmax(0, 1fr))`}>
{#each posts as post} {#each posts as post}
<a href={`/posts/${post.id}`} class="card p-3 flex flex-col justify-end items-center" on:click={() => { <a href={`/posts/${post.id}`} class="card p-3 flex flex-col justify-end items-center" onclick={() => {
$loading = true; $loading = true;
$showSearch = false $showSearch = false
}}> }}>

View file

@ -1,7 +1,13 @@
<script> <script>
import { limitImageHeight } from '$lib/settings'; import { limitImageHeight } from '$lib/settings';
export let post = null; /**
* @typedef {Object} Props
* @property {any} [post]
*/
/** @type {Props} */
let { post = null } = $props();
</script> </script>
{#if post?.preview?.has} {#if post?.preview?.has}

View file

@ -1,4 +1,14 @@
<script>
/**
* @typedef {Object} Props
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props} */
let { children } = $props();
</script>
<div class="container mx-auto"> <div class="container mx-auto">
<p>Error</p> <p>Error</p>
<slot /> {@render children?.()}
</div> </div>

View file

@ -3,6 +3,13 @@
import { Drawer, initializeStores, Modal, ProgressRadial, Toast } from '@skeletonlabs/skeleton'; import { Drawer, initializeStores, Modal, ProgressRadial, Toast } from '@skeletonlabs/skeleton';
import { loading } from '$lib/stores'; import { loading } from '$lib/stores';
import { AppSettings } from '$lib/components'; import { AppSettings } from '$lib/components';
/**
* @typedef {Object} Props
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props} */
let { children } = $props();
initializeStores(); initializeStores();
</script> </script>
@ -20,5 +27,5 @@
<ProgressRadial width="w-8" height="h-8" stroke={120} strokeLinecap="butt" /> <ProgressRadial width="w-8" height="h-8" stroke={120} strokeLinecap="butt" />
</div> </div>
{/if} {/if}
<slot /> {@render children?.()}
</div> </div>

View file

@ -1 +0,0 @@
export const ssr = false;

View file

@ -2,7 +2,7 @@
import { Fa } from 'svelte-fa'; import { Fa } from 'svelte-fa';
import { GlobalSearch, ImageGrid, SearchAppRailAnchor} from '$lib/components'; import { GlobalSearch, ImageGrid, SearchAppRailAnchor} from '$lib/components';
import { faGear } from '@fortawesome/free-solid-svg-icons'; import { faGear } from '@fortawesome/free-solid-svg-icons';
import { invoke } from '@tauri-apps/api/tauri' import { invoke } from '@tauri-apps/api/core'
import { lastSearch, blacklist } from '$lib/settings'; import { lastSearch, blacklist } from '$lib/settings';
import { postSearchResults, loading, showSearch} from '$lib/stores'; import { postSearchResults, loading, showSearch} from '$lib/stores';
import { import {
@ -19,10 +19,10 @@
function onSearch() { function onSearch() {
$loading = true; $loading = true;
invoke('get_posts', { query: $lastSearch }).then((resPosts) => { /*invoke('get_posts', { query: $lastSearch }).then((resPosts) => {
postSearchResults.set(new PostList(resPosts).filterPosts($blacklist).getPosts()); postSearchResults.set(new PostList(resPosts).filterPosts($blacklist).getPosts());
requestAnimationFrame(() => $loading = false); requestAnimationFrame(() => $loading = false);
}); });*/
} }
@ -38,37 +38,47 @@
</script> </script>
<AppShell> <AppShell>
<svelte:fragment slot="pageHeader"> {#snippet pageHeader()}
{#if $showSearch}
<AppBar>
<GlobalSearch />
</AppBar>
{/if}
</svelte:fragment>
<svelte:fragment slot="sidebarLeft"> {#if $showSearch}
<div class="h-dvh"> <AppBar>
<AppRail> <GlobalSearch />
<svelte:fragment slot="lead"> </AppBar>
<SearchAppRailAnchor />
</svelte:fragment>
<svelte:fragment slot="trail">
<AppRailAnchor on:click={() => drawerStore.open()}>
<div class="flex flex-col gap-2 place-content-center">
<Fa size={'2x'} icon={faGear} />
<p>Settings</p>
</div>
</AppRailAnchor>
</svelte:fragment>
</AppRail>
</div>
</svelte:fragment>
<svelte:fragment>
<div class="overflow-scroll p-2">
{#if $postSearchResults}
<ImageGrid posts={$postSearchResults} />
{/if} {/if}
</div>
</svelte:fragment> {/snippet}
{#snippet sidebarLeft()}
<div class="h-dvh">
<AppRail>
{#snippet lead()}
<SearchAppRailAnchor />
{/snippet}
{#snippet trail()}
<AppRailAnchor on:click={() => drawerStore.open()}>
<div class="flex flex-col gap-2 place-content-center">
<Fa size={'2x'} icon={faGear} />
<p>Settings</p>
</div>
</AppRailAnchor>
{/snippet}
</AppRail>
</div>
{/snippet}
{#snippet children()}
<div class="overflow-scroll p-2">
{#if $postSearchResults}
<ImageGrid posts={$postSearchResults} />
{/if}
</div>
{/snippet}
</AppShell> </AppShell>

View file

@ -1,5 +1,3 @@
export const ssr = false;
/** @type {import('./$types').PageLoad} */ /** @type {import('./$types').PageLoad} */
export async function load({ params }) { export async function load({ params }) {
return { postId: params.post_id }; return { postId: params.post_id };

View file

@ -14,15 +14,15 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { loading, showSearch } from '$lib/stores'; import { loading, showSearch } from '$lib/stores';
import { TAG_TYPES } from '$lib/Tags'; import { TAG_TYPES } from '$lib/Tags';
import { invoke } from '@tauri-apps/api/tauri'; import { invoke } from '@tauri-apps/api/core';
export let data; let { data } = $props();
const drawerStore = getDrawerStore(); const drawerStore = getDrawerStore();
const tagTypes = Object.entries(TAG_TYPES); const tagTypes = Object.entries(TAG_TYPES);
const toastStore = getToastStore(); const toastStore = getToastStore();
/** @type any|null */ /** @type any|null */
let post = null; let post = $state(null);
onMount(async () => { onMount(async () => {
$loading = true; $loading = true;
@ -33,105 +33,117 @@
</script> </script>
<AppShell> <AppShell>
<svelte:fragment slot="pageHeader"> {#snippet pageHeader()}
{#if $showSearch}
<AppBar>
<GlobalSearch />
</AppBar>
{/if}
</svelte:fragment>
<svelte:fragment slot="sidebarRight"> {#if $showSearch}
{#if post} <AppBar>
<div class="p-2 h-dvh"> <GlobalSearch />
{#each tagTypes as [name, label]} </AppBar>
<div class="mb-2"> {/if}
<p class="font-bold">{label}</p>
{#each post.tags[name] as tag}
<p>{tag}</p>
{/each}
</div>
{/each}
</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="sidebarLeft"> {/snippet}
<div class="h-dvh">
<AppRail> {#snippet sidebarRight()}
<svelte:fragment slot="lead">
<BackAppRailAnchor popCount={2} />
<SearchAppRailAnchor />
</svelte:fragment>
<svelte:fragment slot="trail">
{#if post}
<AppRailAnchor>
<button class="w-full flex flex-col gap-2 place-content-center place-items-center" on:click={() => toastStore.trigger({message: 'Copied link'})} use:clipboard={`https://e621.net/posts/${post.id}`}>
<Fa size={'2x'} icon={faCopy} />
Copy link
</button>
</AppRailAnchor>
{/if}
<AppRailAnchor on:click={() => drawerStore.open()}>
<button class="w-full flex flex-col gap-2 place-content-center place-items-center">
<Fa size={'2x'} icon={faGear} />
Settings
</button>
</AppRailAnchor>
</svelte:fragment>
</AppRail>
</div>
</svelte:fragment>
<svelte:fragment slot="default">
<div class="container mx-auto flex flex-col items-center justify-center">
{#if post} {#if post}
<div class="card mb-4 flex flex-col"> <div class="p-2 h-dvh">
<section class="flex flex-col place-items-center"> {#each tagTypes as [name, label]}
<PostMedia {post} /> <div class="mb-2">
</section> <p class="font-bold">{label}</p>
<section class="flex flex-row justify-between bg-black"> {#each post.tags[name] as tag}
<div class="btn-group variant-filled rounded-none"> <p>{tag}</p>
<button> {/each}
<span class:text-primary-500={!post.is_favorited} class:text-secondary-500={post.is_favorited}>
<Fa size={'1x'} icon={faHeart} />
</span>
&nbsp;
<span>
{post.fav_count}
</span>
</button>
<button>
<span class="text-secondary-500">
<Fa size={'1x'} icon={faStar} />
</span>
&nbsp;
{post.score.total}
</button>
<button>
<Fa size={'1x'} icon={faCaretUp} />
</button>
<button>
<Fa size={'1x'} icon={faCaretDown} />
</button>
</div> </div>
<div class="btn-group variant-filled rounded-none"> {/each}
<button>
ID: {post.id}
</button>
</div>
</section>
<footer class="card-footer pt-4">
<h3 class="font-bold mb-2" class:line-through={post.description === ''} >Description</h3>
<p>{post.description}</p>
<!-- <pre class="break-all">
{JSON.stringify(post, null, 2)}
</pre> -->
</footer>
</div> </div>
{/if} {/if}
</div>
</svelte:fragment> {/snippet}
{#snippet sidebarLeft()}
<div class="h-dvh">
<AppRail>
{#snippet lead()}
<BackAppRailAnchor popCount={2} />
<SearchAppRailAnchor />
{/snippet}
{#snippet trail()}
{#if post}
<AppRailAnchor>
<button class="w-full flex flex-col gap-2 place-content-center place-items-center" onclick={() => toastStore.trigger({message: 'Copied link'})} use:clipboard={`https://e621.net/posts/${post.id}`}>
<Fa size={'2x'} icon={faCopy} />
Copy link
</button>
</AppRailAnchor>
{/if}
<AppRailAnchor on:click={() => drawerStore.open()}>
<button class="w-full flex flex-col gap-2 place-content-center place-items-center">
<Fa size={'2x'} icon={faGear} />
Settings
</button>
</AppRailAnchor>
{/snippet}
</AppRail>
</div>
{/snippet}
{#snippet default()}
<div class="container mx-auto flex flex-col items-center justify-center">
{#if post}
<div class="card mb-4 flex flex-col">
<section class="flex flex-col place-items-center">
<PostMedia {post} />
</section>
<section class="flex flex-row justify-between bg-black">
<div class="btn-group variant-filled rounded-none">
<button>
<span class:text-primary-500={!post.is_favorited} class:text-secondary-500={post.is_favorited}>
<Fa size={'1x'} icon={faHeart} />
</span>
&nbsp;
<span>
{post.fav_count}
</span>
</button>
<button>
<span class="text-secondary-500">
<Fa size={'1x'} icon={faStar} />
</span>
&nbsp;
{post.score.total}
</button>
<button>
<Fa size={'1x'} icon={faCaretUp} />
</button>
<button>
<Fa size={'1x'} icon={faCaretDown} />
</button>
</div>
<div class="btn-group variant-filled rounded-none">
<button>
ID: {post.id}
</button>
</div>
</section>
<footer class="card-footer pt-4">
<h3 class="font-bold mb-2" class:line-through={post.description === ''} >Description</h3>
<p>{post.description}</p>
<!-- <pre class="break-all">
{JSON.stringify(post, null, 2)}
</pre> -->
</footer>
</div>
{/if}
</div>
{/snippet}
</AppShell> </AppShell>
<style lang="postcss"></style> <style lang="postcss"></style>

View file

@ -7,6 +7,8 @@ export default {
preprocess: [vitePreprocess({})], preprocess: [vitePreprocess({})],
kit: { kit: {
adapter: adapter(), adapter: adapter({
fallback: 'index.html'
}),
}, },
}; };