aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Announcement.svelte4
-rw-r--r--src/lib/Error/AnimeRateLimited.svelte11
-rw-r--r--src/lib/Error/DotDotDot.svelte12
-rw-r--r--src/lib/Error/LogInRestricted.svelte2
-rw-r--r--src/lib/Error/Notice.svelte12
-rw-r--r--src/lib/Error/RateLimited.svelte33
-rw-r--r--src/lib/Events/Event.svelte54
-rw-r--r--src/lib/Events/Group.svelte6
-rw-r--r--src/lib/Hololive/Lives.svelte25
-rw-r--r--src/lib/Hololive/Stream.svelte19
-rw-r--r--src/lib/Home/HeadTitle.svelte8
-rw-r--r--src/lib/Home/LastActivity.svelte8
-rw-r--r--src/lib/Home/Root.svelte13
-rw-r--r--src/lib/Image/FallbackImage.svelte33
-rw-r--r--src/lib/Image/ParallaxImage.svelte40
-rw-r--r--src/lib/Landing.svelte6
-rw-r--r--src/lib/Layout/Dropdown.svelte23
-rw-r--r--src/lib/Layout/NumberTicker.svelte25
-rw-r--r--src/lib/Layout/Popup.svelte40
-rw-r--r--src/lib/Layout/TextTransition.svelte27
-rw-r--r--src/lib/Layout/Username.svelte6
-rw-r--r--src/lib/Lazy.svelte38
-rw-r--r--src/lib/List/Anime/AnimeListTemplate.svelte43
-rw-r--r--src/lib/List/Anime/CleanAnimeList.svelte129
-rw-r--r--src/lib/List/Anime/CompletedAnimeList.svelte14
-rw-r--r--src/lib/List/Anime/DueAnimeList.svelte10
-rw-r--r--src/lib/List/Anime/DueIndexColumn.svelte8
-rw-r--r--src/lib/List/Anime/PlaceholderList.svelte8
-rw-r--r--src/lib/List/Anime/UpcomingAnimeList.svelte18
-rw-r--r--src/lib/List/CleanGrid.svelte12
-rw-r--r--src/lib/List/CleanList.svelte26
-rw-r--r--src/lib/List/ListTitle.svelte29
-rw-r--r--src/lib/List/Manga/CleanMangaList.svelte143
-rw-r--r--src/lib/List/Manga/MangaListTemplate.svelte43
-rw-r--r--src/lib/List/MediaTitleDisplay.svelte17
-rw-r--r--src/lib/Loading/Ellipsis.svelte8
-rw-r--r--src/lib/Loading/Grid.svelte8
-rw-r--r--src/lib/Loading/Message.svelte29
-rw-r--r--src/lib/Loading/Ripple.svelte10
-rw-r--r--src/lib/Loading/Skeleton.svelte35
-rw-r--r--src/lib/MarkdownLink.svelte8
-rw-r--r--src/lib/Media/Anime/Airing/AiringTime.svelte18
-rw-r--r--src/lib/Media/Cover/HoverCover.svelte8
-rw-r--r--src/lib/Notification/Notification.svelte24
-rw-r--r--src/lib/Reader/Chapters/MangaDex.svelte6
-rw-r--r--src/lib/Reader/Chapters/Rawkuma.svelte6
-rw-r--r--src/lib/Schedule/CoverBypass.svelte17
-rw-r--r--src/lib/Schedule/Crunchyroll.svelte6
-rw-r--r--src/lib/Schedule/Days.svelte19
-rw-r--r--src/lib/Settings/Categories/Attributions.svelte2
-rw-r--r--src/lib/Settings/Categories/Cache.svelte6
-rw-r--r--src/lib/Settings/Categories/Calculation.svelte2
-rw-r--r--src/lib/Settings/Categories/Debug.svelte10
-rw-r--r--src/lib/Settings/Categories/Display.svelte24
-rw-r--r--src/lib/Settings/Categories/RSSFeeds.svelte10
-rw-r--r--src/lib/Settings/Categories/SettingSync.svelte12
-rw-r--r--src/lib/Settings/Category.svelte23
-rw-r--r--src/lib/Settings/SettingCheckboxToggle.svelte56
-rw-r--r--src/lib/Settings/SettingHint.svelte9
-rw-r--r--src/lib/Settings/SettingToggle.svelte30
-rw-r--r--src/lib/Tools/ActivityHistory/Grid.svelte20
-rw-r--r--src/lib/Tools/ActivityHistory/Tool.svelte20
-rw-r--r--src/lib/Tools/Birthdays.svelte14
-rw-r--r--src/lib/Tools/DumpProfile.svelte6
-rw-r--r--src/lib/Tools/EpisodeDiscussionCollector.svelte4
-rw-r--r--src/lib/Tools/FollowFix.svelte16
-rw-r--r--src/lib/Tools/Hayai.svelte6
-rw-r--r--src/lib/Tools/HololiveBirthdays.svelte14
-rw-r--r--src/lib/Tools/InputTemplate.svelte50
-rw-r--r--src/lib/Tools/Likes.svelte12
-rw-r--r--src/lib/Tools/Picker.svelte8
-rw-r--r--src/lib/Tools/RandomFollower.svelte6
-rw-r--r--src/lib/Tools/SequelCatcher/List.svelte12
-rw-r--r--src/lib/Tools/SequelCatcher/Tool.svelte10
-rw-r--r--src/lib/Tools/SequelSpy/Prequels.svelte6
-rw-r--r--src/lib/Tools/SequelSpy/Tool.svelte24
-rw-r--r--src/lib/Tools/Tracker/Tool.svelte24
-rw-r--r--src/lib/Tools/UmaMusumeBirthdays.svelte12
-rw-r--r--src/lib/Tools/Wrapped/ActivityHistory.svelte17
-rw-r--r--src/lib/Tools/Wrapped/Media.svelte33
-rw-r--r--src/lib/Tools/Wrapped/MediaExtras.svelte21
-rw-r--r--src/lib/Tools/Wrapped/Tool.svelte312
-rw-r--r--src/lib/Tools/Wrapped/Top/Activity.svelte22
-rw-r--r--src/lib/Tools/Wrapped/Top/Anime.svelte10
-rw-r--r--src/lib/Tools/Wrapped/Top/Manga.svelte8
-rw-r--r--src/lib/Tooltip/LinkedTooltip.svelte57
-rw-r--r--src/lib/User/BadgeWall/AWC.svelte21
-rw-r--r--src/lib/User/BadgeWall/BadgePreview.svelte46
-rw-r--r--src/lib/User/BadgeWall/Badges.svelte31
-rw-r--r--src/lib/User/BadgeWall/FallbackBadge.svelte58
-rw-r--r--src/lib/Utility/Loading.svelte18
-rw-r--r--src/lib/Utility/oauth.ts2
92 files changed, 1429 insertions, 822 deletions
diff --git a/src/lib/Announcement.svelte b/src/lib/Announcement.svelte
index 7bcc9df7..0dc330c7 100644
--- a/src/lib/Announcement.svelte
+++ b/src/lib/Announcement.svelte
@@ -40,9 +40,9 @@
{line}<br />
{/each}
- <p />
+ <p></p>
- <button on:click={dismiss} class="dismiss">{dismissButton || 'Dismiss'}</button>
+ <button onclick={dismiss} class="dismiss">{dismissButton || 'Dismiss'}</button>
</Popup>
{/if}
diff --git a/src/lib/Error/AnimeRateLimited.svelte b/src/lib/Error/AnimeRateLimited.svelte
index 96df3ad5..4671d45b 100644
--- a/src/lib/Error/AnimeRateLimited.svelte
+++ b/src/lib/Error/AnimeRateLimited.svelte
@@ -1,15 +1,22 @@
<script>
import Popup from '$lib/Layout/Popup.svelte';
+ /**
+ * @typedef {Object} Props
+ * @property {import('svelte').Snippet} [children]
+ */
+
+ /** @type {Props} */
+ let { children } = $props();
</script>
<Popup locked fullscreen>
- <p><slot /></p>
+ <p>{@render children?.()}</p>
<span>It is likely that you have been rate-limited by AniList. Please try again later.</span>
{#await fetch('https://api.waifu.pics/sfw/cry') then response}
{#await response.json() then json}
- <p />
+ <p></p>
<a href={`https://trace.moe/?url=${encodeURIComponent(json.url)}`} target="_blank">
<img src={json.url} alt="" style="width: 30vw;" />
diff --git a/src/lib/Error/DotDotDot.svelte b/src/lib/Error/DotDotDot.svelte
index 73261eba..22cd895c 100644
--- a/src/lib/Error/DotDotDot.svelte
+++ b/src/lib/Error/DotDotDot.svelte
@@ -1,11 +1,15 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
- export let max: number | undefined = undefined;
- export let perMs = 1000;
- export let start = '';
+ interface Props {
+ max?: number | undefined;
+ perMs?: number;
+ start?: string;
+ }
- let dots = start;
+ let { max = undefined, perMs = 1000, start = '' }: Props = $props();
+
+ let dots = $state(start);
let interval: NodeJS.Timeout;
onMount(() => {
diff --git a/src/lib/Error/LogInRestricted.svelte b/src/lib/Error/LogInRestricted.svelte
index 07a9adec..d19dfd3d 100644
--- a/src/lib/Error/LogInRestricted.svelte
+++ b/src/lib/Error/LogInRestricted.svelte
@@ -7,7 +7,7 @@
<div class="message">
Please <a
href={`https://anilist.co/api/v2/oauth/authorize?client_id=${env.PUBLIC_ANILIST_CLIENT_ID}&redirect_uri=${env.PUBLIC_ANILIST_REDIRECT_URI}&response_type=code`}
- on:click={() => {
+ onclick={() => {
localStorage.setItem(
'redirect',
window.location.origin + window.location.pathname + window.location.search
diff --git a/src/lib/Error/Notice.svelte b/src/lib/Error/Notice.svelte
index d4cf96c5..6bdd7327 100644
--- a/src/lib/Error/Notice.svelte
+++ b/src/lib/Error/Notice.svelte
@@ -1,2 +1,12 @@
+<script>
+ /**
+ * @typedef {Object} Props
+ * @property {import('svelte').Snippet} [children]
+ */
+
+ /** @type {Props} */
+ let { children } = $props();
+</script>
+
<b>Notice:</b>
-<slot />
+{@render children?.()}
diff --git a/src/lib/Error/RateLimited.svelte b/src/lib/Error/RateLimited.svelte
index 973d75d9..15e779b3 100644
--- a/src/lib/Error/RateLimited.svelte
+++ b/src/lib/Error/RateLimited.svelte
@@ -1,10 +1,23 @@
<script lang="ts">
- export let type = 'Media';
- export let loginSessionError = true;
- export let contact = true;
- export let list = true;
- export let card = false;
- export let might = true;
+ interface Props {
+ type?: string;
+ loginSessionError?: boolean;
+ contact?: boolean;
+ list?: boolean;
+ card?: boolean;
+ might?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ type = 'Media',
+ loginSessionError = true,
+ contact = true,
+ list = true,
+ card = false,
+ might = true,
+ children
+ }: Props = $props();
</script>
<div class:card>
@@ -24,10 +37,10 @@
</p>
{/if}
- <slot />
+ {@render children?.()}
{#if contact}
- <p />
+ <p></p>
If the problem persists, please contact
<a href="https://anilist.co/user/fuwn" target="_blank">@fuwn</a> on AniList.
@@ -48,10 +61,10 @@
</p>
{/if}
- <slot />
+ {@render children?.()}
{#if contact}
- <p />
+ <p></p>
If the problem persists, please contact
<a href="https://anilist.co/user/fuwn" target="_blank">@fuwn</a> on AniList.
diff --git a/src/lib/Events/Event.svelte b/src/lib/Events/Event.svelte
index a2b1d2b5..f0a4f9f7 100644
--- a/src/lib/Events/Event.svelte
+++ b/src/lib/Events/Event.svelte
@@ -7,39 +7,39 @@
export let avatar = false;
</script>
-<a href={event.anilist_url} target="_blank">
- <div
- class="card"
- id="user-grid"
- style={`background-image: ${event.banner ? `url(${event.banner})` : 'none'}; padding: 0;`}
- >
- {#if event}
+<div
+ class="card"
+ id="user-grid"
+ style={`background-image: ${event.banner ? `url(${event.banner})` : 'none'}; padding: 0;`}
+>
+ {#if event}
+ <a href={event.anilist_url} target="_blank">
<img src={event.banner} alt="" id="cover-image" />
- {/if}
+ </a>
+ {/if}
- <div class="card" id="user-grid-content">
- {#if avatar}
- <div id="user-grid-avatar">
- <a href={root(`/events/group/${event.group.anilist_username}`)}>
- <img src={event.group.avatar} alt="" width="100vw" id="avatar" />
- </a>
- </div>
- {/if}
+ <div class="card" id="user-grid-content">
+ {#if avatar}
+ <div id="user-grid-avatar">
+ <a href={root(`/events/group/${event.group.anilist_username}`)}>
+ <img src={event.group.avatar} alt="" width="100vw" id="avatar" />
+ </a>
+ </div>
+ {/if}
- <div id="user-grid-content-text">
- <p>
- <a href={event.anilist_url} target="_blank" class="title-text">
- {event.title}
- </a>
- <br />
- {$locale().dateFormatter(new Date(event.created_at))}
- </p>
+ <div id="user-grid-content-text">
+ <p>
+ <a href={event.anilist_url} target="_blank" class="title-text">
+ {event.title}
+ </a>
+ <br />
+ {$locale().dateFormatter(new Date(event.created_at))}
+ </p>
- <p>{event.description}</p>
- </div>
+ <p>{event.description}</p>
</div>
</div>
-</a>
+</div>
<style>
#user-grid-content {
diff --git a/src/lib/Events/Group.svelte b/src/lib/Events/Group.svelte
index a3d24807..e88d9b2a 100644
--- a/src/lib/Events/Group.svelte
+++ b/src/lib/Events/Group.svelte
@@ -2,7 +2,11 @@
import type { Group } from '$lib/Database/SB/groups';
import tooltip from '$lib/Tooltip/tooltip';
- export let group: Group;
+ interface Props {
+ group: Group;
+ }
+
+ let { group }: Props = $props();
</script>
<div
diff --git a/src/lib/Hololive/Lives.svelte b/src/lib/Hololive/Lives.svelte
index 9e762df9..0f566df4 100644
--- a/src/lib/Hololive/Lives.svelte
+++ b/src/lib/Hololive/Lives.svelte
@@ -4,10 +4,19 @@
import type { Live, ParseResult } from './hololive';
import Stream from './Stream.svelte';
- export let schedule: ParseResult;
- export let pinnedStreams: string[];
- export let getPinnedStreams: () => void;
- export let filter: string | undefined;
+ interface Props {
+ schedule: ParseResult;
+ pinnedStreams: string[];
+ getPinnedStreams: () => void;
+ filter: string | undefined;
+ }
+
+ let {
+ schedule,
+ pinnedStreams,
+ getPinnedStreams,
+ filter
+ }: Props = $props();
const pinStream = (streamer: string) =>
fetch(root(`/api/preferences/pin?stream=${encodeURIComponent(streamer)}`), {
@@ -17,7 +26,7 @@
}
}).then(getPinnedStreams);
- $: categorisedStreams = schedule.lives
+ let categorisedStreams = $derived(schedule.lives
.filter((live) => (filter ? live.streamer === filter : true))
.sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime())
.sort((a, b) => {
@@ -55,7 +64,7 @@
return acc;
},
{ live: [], upcoming: [], ended: [] }
- );
+ ));
</script>
{#if schedule.lives.length === 0}
@@ -68,7 +77,7 @@
{/each}
</div>
-<p />
+<p></p>
<div class="container">
{#each categorisedStreams.upcoming as live}
@@ -76,7 +85,7 @@
{/each}
</div>
-<p />
+<p></p>
<div class="container">
{#each categorisedStreams.ended as live}
diff --git a/src/lib/Hololive/Stream.svelte b/src/lib/Hololive/Stream.svelte
index 12681acb..9f2b5e7b 100644
--- a/src/lib/Hololive/Stream.svelte
+++ b/src/lib/Hololive/Stream.svelte
@@ -5,10 +5,19 @@
import locale from '$stores/locale';
import Icon from '@iconify/svelte';
- export let live: any;
- export let pinStream: (streamer: string) => void;
- export let pinnedStreams: string[];
- export let icon: string;
+ interface Props {
+ live: any;
+ pinStream: (streamer: string) => void;
+ pinnedStreams: string[];
+ icon: string;
+ }
+
+ let {
+ live,
+ pinStream,
+ pinnedStreams,
+ icon
+ }: Props = $props();
</script>
<div class="stream card">
@@ -16,7 +25,7 @@
<div class="pin-icon">
<a
href={'#'}
- on:click={(e) => {
+ onclick={(e) => {
e.preventDefault();
pinStream(live.streamer);
}}
diff --git a/src/lib/Home/HeadTitle.svelte b/src/lib/Home/HeadTitle.svelte
index 39a65ccd..a87da69a 100644
--- a/src/lib/Home/HeadTitle.svelte
+++ b/src/lib/Home/HeadTitle.svelte
@@ -1,6 +1,10 @@
<script lang="ts">
- export let route: string | undefined = undefined;
- export let path = '/';
+ interface Props {
+ route?: string | undefined;
+ path?: string;
+ }
+
+ let { route = undefined, path = '/' }: Props = $props();
const title = (route ? `${route} • ` : '') + 'due.moe';
</script>
diff --git a/src/lib/Home/LastActivity.svelte b/src/lib/Home/LastActivity.svelte
index 000c7f7b..cdbf4d44 100644
--- a/src/lib/Home/LastActivity.svelte
+++ b/src/lib/Home/LastActivity.svelte
@@ -5,9 +5,13 @@
import { lastActivityDate } from '../Data/AniList/activity';
import settings from '$stores/settings';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let lastActivityWasToday = true;
+ let { user }: Props = $props();
+
+ let lastActivityWasToday = $state(true);
onMount(async () => {
if (user !== undefined && !$settings.displayDisableLastActivityWarning) {
diff --git a/src/lib/Home/Root.svelte b/src/lib/Home/Root.svelte
index bc1bdea9..e17f9a77 100644
--- a/src/lib/Home/Root.svelte
+++ b/src/lib/Home/Root.svelte
@@ -2,14 +2,19 @@
import settings from '$stores/settings';
import { fly } from 'svelte/transition';
- export let data: any;
- export let way: number;
+ interface Props {
+ data: any;
+ way: number;
+ children?: import('svelte').Snippet;
+ }
+
+ let { data, way, children }: Props = $props();
const animationDelay = 100;
</script>
{#if $settings.displayDisableAnimations}
- <slot />
+ {@render children?.()}
{:else}
{#key data.url}
<div
@@ -20,7 +25,7 @@
}}
out:fly={{ x: -way, duration: animationDelay }}
>
- <slot />
+ {@render children?.()}
</div>
{/key}
{/if}
diff --git a/src/lib/Image/FallbackImage.svelte b/src/lib/Image/FallbackImage.svelte
index 2029cf0e..1eafdd4c 100644
--- a/src/lib/Image/FallbackImage.svelte
+++ b/src/lib/Image/FallbackImage.svelte
@@ -1,14 +1,27 @@
<script lang="ts">
- export let source: string | undefined | null;
- export let alternative: string | undefined | null;
- export let fallback: string | undefined | null;
- export let maxReplaceCount = 1;
- export let replaceDelay = 1000;
- export let error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg';
- export let hideOnError = false;
- export let style = '';
+ interface Props {
+ source: string | undefined | null;
+ alternative: string | undefined | null;
+ fallback: string | undefined | null;
+ maxReplaceCount?: number;
+ replaceDelay?: number;
+ error?: string;
+ hideOnError?: boolean;
+ style?: string;
+ }
+
+ let {
+ source,
+ alternative,
+ fallback,
+ maxReplaceCount = 1,
+ replaceDelay = 1000,
+ error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg',
+ hideOnError = false,
+ style = ''
+ }: Props = $props();
- let replaceCount = 0;
+ let replaceCount = $state(0);
const delayedReplace = (event: Event, image: string | undefined | null) => {
if (replaceCount >= maxReplaceCount) return;
@@ -27,7 +40,7 @@
alt={alternative}
loading="lazy"
class="badge"
- on:error={(e) => delayedReplace(e, fallback)}
+ onerror={(e) => delayedReplace(e, fallback)}
{style}
/>
{:else if !hideOnError}
diff --git a/src/lib/Image/ParallaxImage.svelte b/src/lib/Image/ParallaxImage.svelte
index 48565ce1..390b9c92 100644
--- a/src/lib/Image/ParallaxImage.svelte
+++ b/src/lib/Image/ParallaxImage.svelte
@@ -2,17 +2,31 @@
import { cubicOut } from 'svelte/easing';
import { tweened } from 'svelte/motion';
- export let source: string;
- export let duration = 300 * 1.75;
- export let easing = cubicOut;
- export let alternativeText: string;
- export let classList = '';
- export let style = '';
- export let factor = 1.25;
- export let limit = 300 * 1.75;
- export let borderRadius = '8px';
-
- let badgeReference: HTMLImageElement;
+ interface Props {
+ source: string;
+ duration?: any;
+ easing?: any;
+ alternativeText: string;
+ classList?: string;
+ style?: string;
+ factor?: number;
+ limit?: any;
+ borderRadius?: string;
+ }
+
+ let {
+ source,
+ duration = 300 * 1.75,
+ easing = cubicOut,
+ alternativeText,
+ classList = '',
+ style = '',
+ factor = 1.25,
+ limit = 300 * 1.75,
+ borderRadius = '8px'
+ }: Props = $props();
+
+ let badgeReference: HTMLImageElement = $state();
const mouse = tweened({ x: 0, y: 0 }, { duration, easing });
const handleMouseMove = (event: MouseEvent) => {
@@ -40,7 +54,7 @@
-$mouse.x / 10
}deg); border-radius: ${borderRadius}; ${style}`}
alt={alternativeText}
- on:mousemove={handleMouseMove}
- on:mouseleave={handleMouseLeave}
+ onmousemove={handleMouseMove}
+ onmouseleave={handleMouseLeave}
class={classList}
/>
diff --git a/src/lib/Landing.svelte b/src/lib/Landing.svelte
index 29a341f1..bfe69de9 100644
--- a/src/lib/Landing.svelte
+++ b/src/lib/Landing.svelte
@@ -39,7 +39,7 @@
</div>
</div>
-<p />
+<p></p>
<div class="example-item card">
<div class="card item-description">
@@ -65,7 +65,7 @@
</div>
</div>
-<p />
+<p></p>
<div class="example-item card">
<div class="item-content">
@@ -105,7 +105,7 @@
<span class="big-text">
<a
href={`https://anilist.co/api/v2/oauth/authorize?client_id=${env.PUBLIC_ANILIST_CLIENT_ID}&redirect_uri=${env.PUBLIC_ANILIST_REDIRECT_URI}&response_type=code`}
- on:click={() => {
+ onclick={() => {
localStorage.setItem(
'redirect',
window.location.origin + window.location.pathname + window.location.search
diff --git a/src/lib/Layout/Dropdown.svelte b/src/lib/Layout/Dropdown.svelte
index fb270dfe..aca94975 100644
--- a/src/lib/Layout/Dropdown.svelte
+++ b/src/lib/Layout/Dropdown.svelte
@@ -6,12 +6,14 @@
preventDefault?: boolean;
}
- export let items: Item[] = [];
- export let title: string | undefined = undefined;
- export let header = true;
- export let center = false;
+ let {
+ items = [] as Item[],
+ title = undefined as string | undefined,
+ header = true,
+ center = false
+ } = $props();
- let open = false;
+ let open = $state(false);
const handleClickOutside = (event: any) => {
if (!event.target.closest('.dropdown')) open = false;
@@ -30,8 +32,8 @@
<span
class={`${header ? 'header-item' : ''} dropdown-toggle`}
id="dropdown-toggle"
- on:click|preventDefault={() => (open = !open)}
- on:keydown={() => {}}
+ onclick={() => (open = !open)}
+ onkeydown={() => {}}
role="button"
tabindex="0"
>
@@ -47,7 +49,7 @@
<a
href={item.url}
class="header-item"
- on:click={(e) => {
+ onclick={(e) => {
if (item.preventDefault) e.preventDefault();
if (item.onClick) item.onClick();
}}
@@ -89,7 +91,10 @@
transform: translateY(-20px);
visibility: hidden;
$delay: 0.25s;
- transition: opacity $delay ease, transform $delay ease, visibility 0s linear $delay;
+ transition:
+ opacity $delay ease,
+ transform $delay ease,
+ visibility 0s linear $delay;
left: var(--dropdown-left);
transform: var(--dropdown-transform);
z-index: 1;
diff --git a/src/lib/Layout/NumberTicker.svelte b/src/lib/Layout/NumberTicker.svelte
index b5e2f49c..3bd7e6c8 100644
--- a/src/lib/Layout/NumberTicker.svelte
+++ b/src/lib/Layout/NumberTicker.svelte
@@ -1,11 +1,24 @@
<script>
+ import { run } from 'svelte/legacy';
+
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
- export let start = 0;
- export let end = 100;
- export let duration = 2000;
- export let delay = 0;
+ /**
+ * @typedef {Object} Props
+ * @property {number} [start]
+ * @property {number} [end]
+ * @property {number} [duration]
+ * @property {number} [delay]
+ */
+
+ /** @type {Props} */
+ let {
+ start = 0,
+ end = 100,
+ duration = 2000,
+ delay = 0
+ } = $props();
const count = tweened(start, {
duration: duration,
@@ -13,9 +26,9 @@
delay: delay
});
- $: {
+ run(() => {
count.set(end);
- }
+ });
</script>
<span class="counter" class:visible={$count !== start}>
diff --git a/src/lib/Layout/Popup.svelte b/src/lib/Layout/Popup.svelte
index b82e86c2..45b719f8 100644
--- a/src/lib/Layout/Popup.svelte
+++ b/src/lib/Layout/Popup.svelte
@@ -1,16 +1,32 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { onMount } from 'svelte';
- export let onLeave = () => {
+ interface Props {
+ onLeave?: any;
+ card?: boolean;
+ smallCard?: boolean;
+ fullscreen?: boolean;
+ show?: boolean;
+ locked?: boolean;
+ center?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ onLeave = () => {
return;
- };
- export let card = true;
- export let smallCard = false;
- export let fullscreen = false;
- export let show = true;
- export let locked = false;
- export let center = false;
+ },
+ card = true,
+ smallCard = false,
+ fullscreen = false,
+ show = $bindable(true),
+ locked = false,
+ center = false,
+ children
+ }: Props = $props();
const handleClickOutside = (event: any) => {
if (!locked && event.target.classList.contains('popup')) {
@@ -24,24 +40,24 @@
if (browser) document.body.style.overflow = 'auto';
});
- $: {
+ run(() => {
if (browser) {
document.body.style.overflow = 'auto';
if (show) document.body.style.overflow = 'hidden';
else document.body.style.overflow = 'auto';
}
- }
+ });
</script>
-<svelte:window on:click={handleClickOutside} />
+<svelte:window onclick={handleClickOutside} />
{#if show}
<div class={`popup ${fullscreen ? 'popup-fullscreen' : ''}`}>
<span
class={`${card ? `card ${smallCard ? 'card-small' : ''}` : ''} ${center ? 'centered' : ''}`}
>
- <slot />
+ {@render children?.()}
</span>
</div>
{/if}
diff --git a/src/lib/Layout/TextTransition.svelte b/src/lib/Layout/TextTransition.svelte
index 35cfe1ea..5f5bd036 100644
--- a/src/lib/Layout/TextTransition.svelte
+++ b/src/lib/Layout/TextTransition.svelte
@@ -1,24 +1,37 @@
<script>
+ import { run } from 'svelte/legacy';
+
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
- export let text = '';
- export let opacityTransitionDuration = 50;
- export let blurTransitionDuration = opacityTransitionDuration;
- export let easing = cubicOut;
+ /**
+ * @typedef {Object} Props
+ * @property {string} [text]
+ * @property {number} [opacityTransitionDuration]
+ * @property {any} [blurTransitionDuration]
+ * @property {any} [easing]
+ */
+
+ /** @type {Props} */
+ let {
+ text = '',
+ opacityTransitionDuration = 50,
+ blurTransitionDuration = opacityTransitionDuration,
+ easing = cubicOut
+ } = $props();
- let previousValue = '';
+ let previousValue = $state('');
let opacity = tweened(1, { duration: opacityTransitionDuration, easing });
let blur = tweened(0, { duration: blurTransitionDuration, easing });
- $: {
+ run(() => {
if (text !== previousValue)
Promise.all([opacity.set(0), blur.set(10)]).then(() => {
previousValue = text;
Promise.all([opacity.set(1), blur.set(0)]);
});
- }
+ });
</script>
<span
diff --git a/src/lib/Layout/Username.svelte b/src/lib/Layout/Username.svelte
index 8b89b708..82be59d0 100644
--- a/src/lib/Layout/Username.svelte
+++ b/src/lib/Layout/Username.svelte
@@ -1,5 +1,9 @@
<script lang="ts">
- export let username: string;
+ interface Props {
+ username: string;
+ }
+
+ let { username }: Props = $props();
</script>
<a href={`https://anilist.co/user/${username}/`}>@{username}</a>
diff --git a/src/lib/Lazy.svelte b/src/lib/Lazy.svelte
index da5b09a9..8167711d 100644
--- a/src/lib/Lazy.svelte
+++ b/src/lib/Lazy.svelte
@@ -1,17 +1,31 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
- export let top = 0;
- export let bottom = 0;
- export let left = 0;
- export let right = 0;
- export let steps = 100;
- export let threshold = 0.01;
- export let once = false;
-
- let element: Element;
- let percent: number = 0;
- let visible = false;
+ interface Props {
+ top?: number;
+ bottom?: number;
+ left?: number;
+ right?: number;
+ steps?: number;
+ threshold?: number;
+ once?: boolean;
+ children?: import('svelte').Snippet<[any]>;
+ }
+
+ let {
+ top = 0,
+ bottom = 0,
+ left = 0,
+ right = 0,
+ steps = 100,
+ threshold = 0.01,
+ once = false,
+ children
+ }: Props = $props();
+
+ let element: Element = $state();
+ let percent: number = $state(0);
+ let visible = $state(false);
let observer: IntersectionObserver;
const intersectPercent = (
@@ -45,5 +59,5 @@
</script>
<div bind:this={element}>
- <slot {visible} {percent} />
+ {@render children?.({ visible, percent, })}
</div>
diff --git a/src/lib/List/Anime/AnimeListTemplate.svelte b/src/lib/List/Anime/AnimeListTemplate.svelte
index 08583d7c..8b105df5 100644
--- a/src/lib/List/Anime/AnimeListTemplate.svelte
+++ b/src/lib/List/Anime/AnimeListTemplate.svelte
@@ -14,26 +14,41 @@
import subsPlease from '$stores/subsPlease';
import identity from '$stores/identity';
- export let endTime: number;
- export let cleanMedia: (
+ interface Props {
+ endTime: number;
+ cleanMedia: (
media: Media[],
displayUnresolved: boolean,
subsPlease: SubsPlease | null,
plannedOnly?: boolean
) => Media[];
- export let animeLists: Promise<Media[]>;
- export let user: AniListAuthorisation;
- export let title: any;
- export let completed = false;
- export let plannedOnly = false;
- export let upcoming = false;
- export let notYetReleased = false;
- export let dummy = false;
+ animeLists: Promise<Media[]>;
+ user: AniListAuthorisation;
+ title: any;
+ completed?: boolean;
+ plannedOnly?: boolean;
+ upcoming?: boolean;
+ notYetReleased?: boolean;
+ dummy?: boolean;
+ }
- let lastUpdatedMedia = -1;
- let previousAnimeList: Media[];
- let pendingUpdate: number | null = null;
- let lastListSize = 8;
+ let {
+ endTime,
+ cleanMedia,
+ animeLists = $bindable(),
+ user,
+ title,
+ completed = false,
+ plannedOnly = false,
+ upcoming = false,
+ notYetReleased = false,
+ dummy = false
+ }: Props = $props();
+
+ let lastUpdatedMedia = $state(-1);
+ let previousAnimeList: Media[] = $state();
+ let pendingUpdate: number | null = $state(null);
+ let lastListSize = $state(8);
onMount(() => {
if (browser) {
diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte
index 0220a366..b088964e 100644
--- a/src/lib/List/Anime/CleanAnimeList.svelte
+++ b/src/lib/List/Anime/CleanAnimeList.svelte
@@ -16,18 +16,35 @@
import CleanGrid from '$lib/List/CleanGrid.svelte';
import CleanList from '../CleanList.svelte';
- export let media: Media[];
- export let title: any;
- export let animeLists: Promise<Media[]>;
- export let user: AniListAuthorisation;
- export let endTime: number;
- export let lastUpdatedMedia: number;
- export let completed = false;
- export let previousAnimeList: Media[];
- export let pendingUpdate: number | null;
- export let upcoming = false;
- export let notYetReleased = false;
- export let dummy = false;
+ interface Props {
+ media: Media[];
+ title: any;
+ animeLists: Promise<Media[]>;
+ user: AniListAuthorisation;
+ endTime: number;
+ lastUpdatedMedia: number;
+ completed?: boolean;
+ previousAnimeList: Media[];
+ pendingUpdate: number | null;
+ upcoming?: boolean;
+ notYetReleased?: boolean;
+ dummy?: boolean;
+ }
+
+ let {
+ media = $bindable(),
+ title,
+ animeLists = $bindable(),
+ user,
+ endTime,
+ lastUpdatedMedia = $bindable(),
+ completed = false,
+ previousAnimeList = $bindable(),
+ pendingUpdate = $bindable(),
+ upcoming = false,
+ notYetReleased = false,
+ dummy = false
+ }: Props = $props();
let keyCacher: NodeJS.Timeout;
let totalEpisodeDueCount = media
@@ -109,60 +126,64 @@
/>
{#if media.length === 0}
- No anime to display. <button on:click={() => (animeLists = cleanCache(user, $identity))}>
+ No anime to display. <button onclick={() => (animeLists = cleanCache(user, $identity))}>
Force refresh
</button>
{/if}
{#if $settings.displayCoverModeAnime}
<CleanGrid {media} {dummy} type="anime" {upcoming} {notYetReleased}>
- <div slot="title" let:title={anime} let:progress>
- {#if !upcoming && !notYetReleased}
- {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
- <button
- class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
- style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(anime, progress)}>+</button
- >
- {#if !completed || dummy}
- [{anime.nextAiringEpisode?.episode === -1
- ? '?'
- : (anime.nextAiringEpisode?.episode || 1) - 1}]
- <br />
- <AiringTime originalAnime={anime} />
+ {#snippet title({ title: anime, progress })}
+ <div >
+ {#if !upcoming && !notYetReleased}
+ {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
+ <button
+ class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
+ style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
+ onclick={() => increment(anime, progress)}>+</button
+ >
+ {#if !completed || dummy}
+ [{anime.nextAiringEpisode?.episode === -1
+ ? '?'
+ : (anime.nextAiringEpisode?.episode || 1) - 1}]
+ <br />
+ <AiringTime originalAnime={anime} />
+ {/if}
+ {:else}
+ <AiringTime originalAnime={anime} upcoming={true} />
{/if}
- {:else}
- <AiringTime originalAnime={anime} upcoming={true} />
- {/if}
- </div>
+ </div>
+ {/snippet}
</CleanGrid>
{:else}
<CleanList {media} type="anime" {upcoming} {notYetReleased} {lastUpdatedMedia}>
- <span slot="information" let:title={anime} let:progress>
- {#if !upcoming || notYetReleased || !$settings.displayCountdownRightAligned}
- <span class="opaque">|</span>
- {/if}
- {#if !upcoming || notYetReleased}
- <!-- {anime.mediaListEntry?.progress || 0}{@html totalEpisodes(anime)} -->
- {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
- <button
- class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
- style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(anime, progress)}>+</button
- >
- {#if !completed}
- [{anime.nextAiringEpisode?.episode === -1
- ? '?'
- : (anime.nextAiringEpisode?.episode || 1) - 1}]
+ {#snippet information({ title: anime, progress })}
+ <span >
+ {#if !upcoming || notYetReleased || !$settings.displayCountdownRightAligned}
+ <span class="opaque">|</span>
+ {/if}
+ {#if !upcoming || notYetReleased}
+ <!-- {anime.mediaListEntry?.progress || 0}{@html totalEpisodes(anime)} -->
+ {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
+ <button
+ class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
+ style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
+ onclick={() => increment(anime, progress)}>+</button
+ >
+ {#if !completed}
+ [{anime.nextAiringEpisode?.episode === -1
+ ? '?'
+ : (anime.nextAiringEpisode?.episode || 1) - 1}]
+ <span class:countdown={$settings.displayCountdownRightAligned}>
+ <AiringTime originalAnime={anime} />
+ </span>
+ {/if}
+ {:else}
<span class:countdown={$settings.displayCountdownRightAligned}>
- <AiringTime originalAnime={anime} />
+ <AiringTime originalAnime={anime} upcoming={true} />
</span>
{/if}
- {:else}
- <span class:countdown={$settings.displayCountdownRightAligned}>
- <AiringTime originalAnime={anime} upcoming={true} />
- </span>
- {/if}
- </span>
+ </span>
+ {/snippet}
</CleanList>
{/if}
diff --git a/src/lib/List/Anime/CompletedAnimeList.svelte b/src/lib/List/Anime/CompletedAnimeList.svelte
index 76ec7207..a8cb6fd1 100644
--- a/src/lib/List/Anime/CompletedAnimeList.svelte
+++ b/src/lib/List/Anime/CompletedAnimeList.svelte
@@ -11,18 +11,22 @@
import identity from '$stores/identity';
import sampleAnime from '$lib/Data/Static/SampleMedia/anime.json';
- export let user: AniListAuthorisation = {
+ interface Props {
+ user?: AniListAuthorisation;
+ dummy?: boolean;
+ }
+
+ let { user = {
accessToken: '',
refreshToken: '',
expiresIn: 0,
tokenType: ''
- };
- export let dummy = false;
+ }, dummy = false }: Props = $props();
const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
+ let animeLists: Promise<Media[]> = $state();
let startTime: number;
- let endTime: number;
+ let endTime: number = $state();
onMount(async () => {
startTime = performance.now();
diff --git a/src/lib/List/Anime/DueAnimeList.svelte b/src/lib/List/Anime/DueAnimeList.svelte
index 1453baeb..bbae319a 100644
--- a/src/lib/List/Anime/DueAnimeList.svelte
+++ b/src/lib/List/Anime/DueAnimeList.svelte
@@ -12,12 +12,16 @@
import locale from '$stores/locale';
import identity from '$stores/identity';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
+
+ let { user }: Props = $props();
const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
+ let animeLists: Promise<Media[]> = $state();
let startTime: number;
- let endTime: number;
+ let endTime: number = $state();
const keyCacher = setInterval(() => {
startTime = performance.now();
diff --git a/src/lib/List/Anime/DueIndexColumn.svelte b/src/lib/List/Anime/DueIndexColumn.svelte
index 61f2a178..0f20a2a1 100644
--- a/src/lib/List/Anime/DueIndexColumn.svelte
+++ b/src/lib/List/Anime/DueIndexColumn.svelte
@@ -6,8 +6,12 @@
import ListTitle from '../ListTitle.svelte';
import AnimeList from '$lib/List/Anime/DueAnimeList.svelte';
- export let userIdentity: { id: number };
- export let user: AniListAuthorisation;
+ interface Props {
+ userIdentity: { id: number };
+ user: AniListAuthorisation;
+ }
+
+ let { userIdentity, user }: Props = $props();
</script>
<details open={!$settings.displayAnimeCollapsed} class="list list-due">
diff --git a/src/lib/List/Anime/PlaceholderList.svelte b/src/lib/List/Anime/PlaceholderList.svelte
index 1f701d79..2c9baab5 100644
--- a/src/lib/List/Anime/PlaceholderList.svelte
+++ b/src/lib/List/Anime/PlaceholderList.svelte
@@ -4,8 +4,12 @@
import ListTitle from '../ListTitle.svelte';
import type { Title } from '../mediaTitle';
- export let title: Title;
- export let count = 8;
+ interface Props {
+ title: Title;
+ count?: number;
+ }
+
+ let { title, count = 8 }: Props = $props();
</script>
<ListTitle {title} />
diff --git a/src/lib/List/Anime/UpcomingAnimeList.svelte b/src/lib/List/Anime/UpcomingAnimeList.svelte
index 7b01af86..4957cd45 100644
--- a/src/lib/List/Anime/UpcomingAnimeList.svelte
+++ b/src/lib/List/Anime/UpcomingAnimeList.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media';
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import { onMount } from 'svelte';
@@ -13,12 +15,16 @@
import { injectAiringTime } from '$lib/Media/Anime/Airing/Subtitled/match';
import revalidateAnime from '$stores/revalidateAnime';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
+
+ let { user }: Props = $props();
const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
+ let animeLists: Promise<Media[]> = $state();
let startTime: number;
- let endTime: number;
+ let endTime: number = $state();
onMount(async () => {
startTime = performance.now();
@@ -74,7 +80,7 @@
return upcomingAnime;
};
- $: {
+ run(() => {
if ($revalidateAnime) {
$revalidateAnime = false;
$lastPruneTimes.anime = -1;
@@ -83,7 +89,7 @@
notificationType: 'Upcoming Episodes'
});
}
- }
+ });
</script>
<AnimeList
@@ -96,7 +102,7 @@
/>
{#if $settings.displayPlannedAnime}
- <p />
+ <p></p>
<AnimeList
{endTime}
diff --git a/src/lib/List/CleanGrid.svelte b/src/lib/List/CleanGrid.svelte
index ec93a685..774303aa 100644
--- a/src/lib/List/CleanGrid.svelte
+++ b/src/lib/List/CleanGrid.svelte
@@ -7,11 +7,13 @@
import { mediaTitle } from './mediaTitle';
import './covers.css';
- export let media: Media[];
- export let dummy = false;
- export let type: 'anime' | 'manga';
- export let upcoming = false;
- export let notYetReleased = false;
+ let {
+ media = [] as Media[],
+ dummy = false,
+ type = 'anime' as 'anime' | 'manga',
+ upcoming = false,
+ notYetReleased = false
+ } = $props();
let uniqueID = new Date().getTime();
</script>
diff --git a/src/lib/List/CleanList.svelte b/src/lib/List/CleanList.svelte
index 47811932..46946973 100644
--- a/src/lib/List/CleanList.svelte
+++ b/src/lib/List/CleanList.svelte
@@ -5,11 +5,23 @@
import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte';
import settings from '$stores/settings';
- export let media: Media[];
- export let type: 'anime' | 'manga';
- export let upcoming = false;
- export let notYetReleased = false;
- export let lastUpdatedMedia: number;
+ interface Props {
+ media: Media[];
+ type: 'anime' | 'manga';
+ upcoming?: boolean;
+ notYetReleased?: boolean;
+ lastUpdatedMedia: number;
+ information?: import('svelte').Snippet<[any]>;
+ }
+
+ let {
+ media,
+ type,
+ upcoming = false,
+ notYetReleased = false,
+ lastUpdatedMedia,
+ information
+ }: Props = $props();
</script>
<ul>
@@ -32,7 +44,7 @@
href={$settings.displayCopyMediaTitleNotLink
? '#'
: outboundLink(title, type, $settings.displayOutboundLinksTo)}
- on:click={(e) => {
+ onclick={(e) => {
if ($settings.displayCopyMediaTitleNotLink) {
e.preventDefault();
@@ -54,7 +66,7 @@
[<a href={`https://anilist.co/${type}/${title.id}/social`} target="_blank">S</a>]
{/if}
- <slot name="information" {progress} {title} />
+ {@render information?.({ progress, title, })}
</span>
</li>
{/if}
diff --git a/src/lib/List/ListTitle.svelte b/src/lib/List/ListTitle.svelte
index 21013b52..cccc5731 100644
--- a/src/lib/List/ListTitle.svelte
+++ b/src/lib/List/ListTitle.svelte
@@ -2,15 +2,28 @@
import tooltip from '$lib/Tooltip/tooltip';
import type { Title } from './mediaTitle';
- export let time: number | undefined = undefined;
- export let count = -1337;
- export let title: Title = {
+ interface Props {
+ time?: number | undefined;
+ count?: any;
+ title?: Title;
+ progress?: undefined | number;
+ hideTime?: boolean;
+ hideCount?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ time = undefined,
+ count = -1337,
+ title = {
title: 'Media List',
hint: 'This is a media list.'
- };
- export let progress: undefined | number = undefined;
- export let hideTime = false;
- export let hideCount = false;
+ },
+ progress = undefined,
+ hideTime = false,
+ hideCount = false,
+ children
+ }: Props = $props();
</script>
<summary>
@@ -23,7 +36,7 @@
{#if !hideTime}
<small class="opaque">{time ? time.toFixed(3) : '...'}s</small>
{/if}
- <slot />
+ {@render children?.()}
{#if progress !== undefined}
<button class="badge unclickable-button button-badge badge-info">
{progress.toFixed(0)}%
diff --git a/src/lib/List/Manga/CleanMangaList.svelte b/src/lib/List/Manga/CleanMangaList.svelte
index dfaa187c..ec1a3d83 100644
--- a/src/lib/List/Manga/CleanMangaList.svelte
+++ b/src/lib/List/Manga/CleanMangaList.svelte
@@ -14,22 +14,37 @@
import CleanGrid from '../CleanGrid.svelte';
import CleanList from '../CleanList.svelte';
- export let media: Media[];
- export let cleanCache: () => void;
- export let endTime: number;
- export let lastUpdatedMedia: number;
- export let updateMedia: (
+ interface Props {
+ media: Media[];
+ cleanCache: () => void;
+ endTime: number;
+ lastUpdatedMedia: number;
+ updateMedia: (
id: number,
progress: number | undefined,
media: Media[]
) => Promise<void>;
- export let pendingUpdate: number | null;
- export let due: boolean;
- export let rateLimited: boolean;
- export let authorised: boolean;
- export let dummy = false;
+ pendingUpdate: number | null;
+ due: boolean;
+ rateLimited: boolean;
+ authorised: boolean;
+ dummy?: boolean;
+ }
- let serviceStatusResponse: Promise<Response>;
+ let {
+ media,
+ cleanCache,
+ endTime,
+ lastUpdatedMedia,
+ updateMedia,
+ pendingUpdate,
+ due,
+ rateLimited,
+ authorised,
+ dummy = false
+ }: Props = $props();
+
+ let serviceStatusResponse: Promise<Response> = $state();
onMount(() => {
serviceStatusResponse = fetch(proxy('https://api.mangadex.org/ping'));
@@ -58,7 +73,7 @@
<button
class="small-button"
title="Force a full refresh"
- on:click={cleanCache}
+ onclick={cleanCache}
data-umami-event="Force Refresh Manga">Refresh</button
>
{/if}
@@ -90,18 +105,18 @@
{#if media.length === 0 && !rateLimited}
{#if rateLimited}
- <p />
+ <p></p>
{/if}
<p>
- No manga to display. <button on:click={cleanCache} data-umami-event="Force Refresh No Manga"
+ No manga to display. <button onclick={cleanCache} data-umami-event="Force Refresh No Manga"
>Force refresh</button
>
</p>
<span>
Don't read manga? <button
- on:click={() => ($settings.disableManga = true)}
+ onclick={() => ($settings.disableManga = true)}
data-umami-event="Disable No Manga">Hide the manga panel</button
>
You can re-enable it later in the <a href={root('/settings')}>Settings</a>.
@@ -110,57 +125,61 @@
{#if $settings.displayCoverModeManga || dummy}
<CleanGrid {media} {dummy} type="manga">
- <div slot="title" let:title={manga} let:progress>
- {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
- <span class="opaque">/{manga.chapters || '?'}</span>
- {/if}
- <button
- class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
- style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(manga)}
- >
- +
- </button>
- {#if due || Math.floor(manga.episodes) < manga.chapters}
- [{manga.episodes || '?'}]
- {#await volumeCount(manga) then volumes}
- {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
+ {#snippet title({ title: manga, progress })}
+ <div >
+ {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
+ <span class="opaque">/{manga.chapters || '?'}</span>
+ {/if}
+ <button
+ class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
+ style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
+ onclick={() => increment(manga)}
+ >
+ +
+ </button>
+ {#if due || Math.floor(manga.episodes) < manga.chapters}
+ [{manga.episodes || '?'}]
+ {#await volumeCount(manga) then volumes}
+ {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
- {#if volumes !== null && (volumeProgress || 0) < volumes}
- <span style="color: lightcoral;">
- Vol. {volumeProgress} &#8594; {volumes}
- </span>
- {/if}
- {/await}
- {/if}
- </div>
+ {#if volumes !== null && (volumeProgress || 0) < volumes}
+ <span style="color: lightcoral;">
+ Vol. {volumeProgress} &#8594; {volumes}
+ </span>
+ {/if}
+ {/await}
+ {/if}
+ </div>
+ {/snippet}
</CleanGrid>
{:else}
<CleanList {media} type="manga" {lastUpdatedMedia}>
- <span slot="information" let:title={manga} let:progress>
- <span class="opaque">|</span>
- {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
- <span class="opaque">/{manga.chapters || '?'}</span>
- {/if}
- <button
- class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
- style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(manga)}
- >
- +
- </button>
- {#if due || Math.floor(manga.episodes) < manga.chapters}
- [{manga.episodes || '?'}]
- {#await volumeCount(manga) then volumes}
- {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
+ {#snippet information({ title: manga, progress })}
+ <span >
+ <span class="opaque">|</span>
+ {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
+ <span class="opaque">/{manga.chapters || '?'}</span>
+ {/if}
+ <button
+ class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
+ style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
+ onclick={() => increment(manga)}
+ >
+ +
+ </button>
+ {#if due || Math.floor(manga.episodes) < manga.chapters}
+ [{manga.episodes || '?'}]
+ {#await volumeCount(manga) then volumes}
+ {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
- {#if volumes !== null && (volumeProgress || 0) < volumes}
- <span style="color: lightcoral;">
- Vol. {volumeProgress} &#8594; {volumes}
- </span>
- {/if}
- {/await}
- {/if}
- </span>
+ {#if volumes !== null && (volumeProgress || 0) < volumes}
+ <span style="color: lightcoral;">
+ Vol. {volumeProgress} &#8594; {volumes}
+ </span>
+ {/if}
+ {/await}
+ {/if}
+ </span>
+ {/snippet}
</CleanList>
{/if}
diff --git a/src/lib/List/Manga/MangaListTemplate.svelte b/src/lib/List/Manga/MangaListTemplate.svelte
index 1303419f..107ff47d 100644
--- a/src/lib/List/Manga/MangaListTemplate.svelte
+++ b/src/lib/List/Manga/MangaListTemplate.svelte
@@ -21,28 +21,37 @@
import { browser } from '$app/environment';
import identity from '$stores/identity';
- export let user: AniListAuthorisation = {
+ interface Props {
+ user?: AniListAuthorisation;
+ displayUnresolved: boolean;
+ due: boolean;
+ dummy?: any;
+ }
+
+ let {
+ user = {
accessToken: '',
refreshToken: '',
expiresIn: 0,
tokenType: ''
- };
- export let displayUnresolved: boolean;
- export let due: boolean;
- export let dummy = $settings.debugDummyLists || false;
+ },
+ displayUnresolved,
+ due,
+ dummy = $settings.debugDummyLists || false
+ }: Props = $props();
const { addNotification } = getNotificationsContext();
const authorised = authorisedJson.includes($identity.id);
- let mangaLists: Promise<Media[]>;
+ let mangaLists: Promise<Media[]> = $state();
let startTime: number;
- let endTime: number;
- let lastUpdatedMedia = -1;
- let previousMangaList: Media[];
- let pendingUpdate: number | null = null;
- let progress = 0;
- let rateLimited = false;
- let forceFlag = false;
- let lastListSize = 5;
+ let endTime: number = $state();
+ let lastUpdatedMedia = $state(-1);
+ let previousMangaList: Media[] = $state();
+ let pendingUpdate: number | null = $state(null);
+ let progress = $state(0);
+ let rateLimited = $state(false);
+ let forceFlag = $state(false);
+ let lastListSize = $state(5);
const keyCacher = setInterval(() => {
startTime = performance.now();
@@ -270,7 +279,7 @@
<button
data-umami-event="Force Refresh Manga"
title="Force a full refresh"
- on:click={() => {
+ onclick={() => {
cleanCache();
forceFlag = true;
@@ -317,7 +326,7 @@
<button
data-umami-event="Force Refresh Manga"
title="Force a full refresh"
- on:click={() => {
+ onclick={() => {
cleanCache();
forceFlag = true;
@@ -349,7 +358,7 @@
<button
data-umami-event="Force Refresh Manga"
title="Force a full refresh"
- on:click={() => {
+ onclick={() => {
cleanCache();
forceFlag = true;
diff --git a/src/lib/List/MediaTitleDisplay.svelte b/src/lib/List/MediaTitleDisplay.svelte
index 6a886704..0fd7cf17 100644
--- a/src/lib/List/MediaTitleDisplay.svelte
+++ b/src/lib/List/MediaTitleDisplay.svelte
@@ -6,10 +6,19 @@
import LZString from 'lz-string';
import * as wanakana from 'wanakana';
- export let title: MediaTitle;
- export let abbreviate = false;
- export let abbreviateTo = 20;
- export let tooltip = false;
+ interface Props {
+ title: MediaTitle;
+ abbreviate?: boolean;
+ abbreviateTo?: number;
+ tooltip?: boolean;
+ }
+
+ let {
+ title,
+ abbreviate = false,
+ abbreviateTo = 20,
+ tooltip = false
+ }: Props = $props();
const compressToBase64 = (string: string) => LZString.compressToBase64(string);
</script>
diff --git a/src/lib/Loading/Ellipsis.svelte b/src/lib/Loading/Ellipsis.svelte
index ba1f30b8..7ee9dcfb 100644
--- a/src/lib/Loading/Ellipsis.svelte
+++ b/src/lib/Loading/Ellipsis.svelte
@@ -1,10 +1,14 @@
<script lang="ts">
- export let colour = 'var(--fg)';
+ interface Props {
+ colour?: string;
+ }
+
+ let { colour = 'var(--fg)' }: Props = $props();
</script>
<div class="ellipsis" style={`--loader-colour: ${colour};`}>
{#each Array.from({ length: 4 }) as _}
- <div />
+ <div></div>
{/each}
</div>
diff --git a/src/lib/Loading/Grid.svelte b/src/lib/Loading/Grid.svelte
index 1a64b3e0..107b985b 100644
--- a/src/lib/Loading/Grid.svelte
+++ b/src/lib/Loading/Grid.svelte
@@ -1,10 +1,14 @@
<script lang="ts">
- export let colour = 'var(--fg)';
+ interface Props {
+ colour?: string;
+ }
+
+ let { colour = 'var(--fg)' }: Props = $props();
</script>
<div class="grid" style={`--loader-colour: ${colour};`}>
{#each Array.from({ length: 9 }) as _}
- <div />
+ <div></div>
{/each}
</div>
diff --git a/src/lib/Loading/Message.svelte b/src/lib/Loading/Message.svelte
index 5aabec9c..c940a452 100644
--- a/src/lib/Loading/Message.svelte
+++ b/src/lib/Loading/Message.svelte
@@ -4,12 +4,25 @@
import Grid from './Grid.svelte';
import Popup from '$lib/Layout/Popup.svelte';
- export let message: string | undefined = undefined;
- export let loader: 'ellipsis' | 'ripple' | 'grid' = 'ellipsis';
- export let colour = 'var(--fg)';
- export let slot = false;
- export let withReload = false;
- export let fullscreen = true;
+ interface Props {
+ message?: string | undefined;
+ loader?: 'ellipsis' | 'ripple' | 'grid';
+ colour?: string;
+ slot?: boolean;
+ withReload?: boolean;
+ fullscreen?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ message = undefined,
+ loader = 'ellipsis',
+ colour = 'var(--fg)',
+ slot = false,
+ withReload = false,
+ fullscreen = true,
+ children
+ }: Props = $props();
</script>
<Popup {fullscreen} locked>
@@ -29,10 +42,10 @@
{:else if slot}
<br />
- <slot />
+ {@render children?.()}
{#if withReload}
- Please <a href={'#'} on:click={() => location.reload()}>try again</a> later.
+ Please <a href={'#'} onclick={() => location.reload()}>try again</a> later.
{/if}
{/if}
</div>
diff --git a/src/lib/Loading/Ripple.svelte b/src/lib/Loading/Ripple.svelte
index 05d62bb5..20b1447a 100644
--- a/src/lib/Loading/Ripple.svelte
+++ b/src/lib/Loading/Ripple.svelte
@@ -1,10 +1,14 @@
<script lang="ts">
- export let colour = 'var(--fg)';
+ interface Props {
+ colour?: string;
+ }
+
+ let { colour = 'var(--fg)' }: Props = $props();
</script>
<div class="ripple" style={`--loader-colour: ${colour};`}>
- <div />
- <div />
+ <div></div>
+ <div></div>
</div>
<style lang="scss">
diff --git a/src/lib/Loading/Skeleton.svelte b/src/lib/Loading/Skeleton.svelte
index 3f39beec..5ca3789a 100644
--- a/src/lib/Loading/Skeleton.svelte
+++ b/src/lib/Loading/Skeleton.svelte
@@ -1,12 +1,25 @@
<script lang="ts">
- export let count = 3;
- export let width = '100%';
- export let height = '100px';
- export let card = true;
- export let grid = false;
- export let list = false;
- export let pad = false;
- export let bigCard = false;
+ interface Props {
+ count?: number;
+ width?: string;
+ height?: string;
+ card?: boolean;
+ grid?: boolean;
+ list?: boolean;
+ pad?: boolean;
+ bigCard?: boolean;
+ }
+
+ let {
+ count = 3,
+ width = '100%',
+ height = '100px',
+ card = true,
+ grid = false,
+ list = false,
+ pad = false,
+ bigCard = false
+ }: Props = $props();
</script>
{#if grid}
@@ -14,7 +27,7 @@
{#each Array(count) as _, i}
<div class={card ? `${bigCard ? 'card' : ''} card-small` : ''} style={`width: ${width};`}>
<div class="skeleton-container" style={`--i: ${i};`}>
- <div class="skeleton" style={`width: ${width}; height: ${height};`} />
+ <div class="skeleton" style={`width: ${width}; height: ${height};`}></div>
</div>
</div>
{/each}
@@ -26,12 +39,12 @@
style={`width: ${width}; ${list ? 'padding-top: .75em;' : ''}; --i: ${i};`}
>
<div class="skeleton-container">
- <div class="skeleton" style={`width: ${width}; height: ${height};`} />
+ <div class="skeleton" style={`width: ${width}; height: ${height};`}></div>
</div>
</div>
{#if !list && i < count - 1}
- <p />
+ <p></p>
{/if}
{/each}
{/if}
diff --git a/src/lib/MarkdownLink.svelte b/src/lib/MarkdownLink.svelte
index 731eb263..a4c62112 100644
--- a/src/lib/MarkdownLink.svelte
+++ b/src/lib/MarkdownLink.svelte
@@ -1,6 +1,10 @@
<script lang="ts">
- export let href: string;
- export let text: string;
+ interface Props {
+ href: string;
+ text: string;
+ }
+
+ let { href = $bindable(), text }: Props = $props();
try {
let url = new URL(href);
diff --git a/src/lib/Media/Anime/Airing/AiringTime.svelte b/src/lib/Media/Anime/Airing/AiringTime.svelte
index 53a39c39..b9b224d7 100644
--- a/src/lib/Media/Anime/Airing/AiringTime.svelte
+++ b/src/lib/Media/Anime/Airing/AiringTime.svelte
@@ -6,16 +6,20 @@
import tooltip from '$lib/Tooltip/tooltip';
import locale from '$stores/locale';
- export let originalAnime: Media;
- export let upcoming = false;
+ interface Props {
+ originalAnime: Media;
+ upcoming?: boolean;
+ }
+
+ let { originalAnime, upcoming = false }: Props = $props();
const anime = originalAnime;
- let opacity = 100;
- let timeFrame = '';
- let time = '';
+ let opacity = $state(100);
+ let timeFrame = $state('');
+ let time = $state('');
let nextEpisode = anime.nextAiringEpisode?.episode || 0;
- let few = true;
- let dateString = '';
+ let few = $state(true);
+ let dateString = $state('');
let updateInterval: NodeJS.Timeout;
onMount(() => {
diff --git a/src/lib/Media/Cover/HoverCover.svelte b/src/lib/Media/Cover/HoverCover.svelte
index 51cbf5d2..f27be993 100644
--- a/src/lib/Media/Cover/HoverCover.svelte
+++ b/src/lib/Media/Cover/HoverCover.svelte
@@ -2,8 +2,12 @@
import settings from '$stores/settings';
import type { HoverCoverResponse } from './hoverCover';
- export let options: HoverCoverResponse;
- export let width = 250;
+ interface Props {
+ options: HoverCoverResponse;
+ width?: number;
+ }
+
+ let { options, width = 250 }: Props = $props();
</script>
{#if options.hovering}
diff --git a/src/lib/Notification/Notification.svelte b/src/lib/Notification/Notification.svelte
index 6764f46e..f2b6ac44 100644
--- a/src/lib/Notification/Notification.svelte
+++ b/src/lib/Notification/Notification.svelte
@@ -1,12 +1,18 @@
<script lang="ts">
+ import { preventDefault } from 'svelte/legacy';
+
import settings from '$stores/settings';
import { onMount } from 'svelte';
- export let notification: { [key: string]: any } = {};
- export let onRemove: () => void = () => {
+ interface Props {
+ notification?: { [key: string]: any };
+ onRemove?: () => void;
+ removed?: boolean;
+ }
+
+ let { notification = {}, onRemove = () => {
return;
- };
- export let removed = false;
+ }, removed = $bindable(false) }: Props = $props();
onMount(() => setTimeout(remove, notification.duration));
@@ -21,20 +27,20 @@
<div
id="notification-container"
class={removed ? 'fade-out' : 'fade-in'}
- on:click={remove}
- on:keydown={() => {
+ onclick={remove}
+ onkeydown={() => {
return;
}}
role="button"
tabindex="0"
>
{#if notification.description}
- <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<details
open
id="notification"
- on:click|preventDefault={(event) => event.preventDefault()}
- on:keydown={() => {
+ onclick={preventDefault((event) => event.preventDefault())}
+ onkeydown={() => {
return;
}}
>
diff --git a/src/lib/Reader/Chapters/MangaDex.svelte b/src/lib/Reader/Chapters/MangaDex.svelte
index 44c0d8f6..4be69d02 100644
--- a/src/lib/Reader/Chapters/MangaDex.svelte
+++ b/src/lib/Reader/Chapters/MangaDex.svelte
@@ -1,5 +1,9 @@
<script lang="ts">
- export let data: any;
+ interface Props {
+ data: any;
+ }
+
+ let { data }: Props = $props();
</script>
<ul>
diff --git a/src/lib/Reader/Chapters/Rawkuma.svelte b/src/lib/Reader/Chapters/Rawkuma.svelte
index 720c65d5..671a0572 100644
--- a/src/lib/Reader/Chapters/Rawkuma.svelte
+++ b/src/lib/Reader/Chapters/Rawkuma.svelte
@@ -2,7 +2,11 @@
import { getChaptersFromText } from '$lib/Data/Manga/raw';
import { onMount } from 'svelte';
- export let data: string;
+ interface Props {
+ data: string;
+ }
+
+ let { data }: Props = $props();
onMount(() => {
console.log();
diff --git a/src/lib/Schedule/CoverBypass.svelte b/src/lib/Schedule/CoverBypass.svelte
index e94ddd40..72b84eb8 100644
--- a/src/lib/Schedule/CoverBypass.svelte
+++ b/src/lib/Schedule/CoverBypass.svelte
@@ -8,10 +8,19 @@
import { abbreviate } from '$lib/Utility/string';
import settings from '$stores/settings';
- export let media: Media | null;
- export let entry: SubsPleaseEpisode;
- export let cover = true;
- export let showTooltip = true;
+ interface Props {
+ media: Media | null;
+ entry: SubsPleaseEpisode;
+ cover?: boolean;
+ showTooltip?: boolean;
+ }
+
+ let {
+ media,
+ entry,
+ cover = true,
+ showTooltip = true
+ }: Props = $props();
const abbreviateTo = 40;
diff --git a/src/lib/Schedule/Crunchyroll.svelte b/src/lib/Schedule/Crunchyroll.svelte
index d22cff42..8fc0e95a 100644
--- a/src/lib/Schedule/Crunchyroll.svelte
+++ b/src/lib/Schedule/Crunchyroll.svelte
@@ -29,7 +29,7 @@
(media) => media.day === 'soon'
);
- $: columnCount = Math.ceil(Object.keys(days).length / 2);
+ let columnCount = $derived(Math.ceil(Object.keys(days).length / 2));
const ordinalSuffix = (i: number) => {
const j = i % 10;
@@ -61,7 +61,7 @@
</details>
</div>
- <p />
+ <p></p>
{/each}
<div class="card day">
@@ -76,7 +76,7 @@
</details>
</div>
- <p />
+ <p></p>
<div class="card day">
<details open class="details-unstyled">
diff --git a/src/lib/Schedule/Days.svelte b/src/lib/Schedule/Days.svelte
index f7a49029..51558240 100644
--- a/src/lib/Schedule/Days.svelte
+++ b/src/lib/Schedule/Days.svelte
@@ -18,15 +18,24 @@
import Skeleton from '$lib/Loading/Skeleton.svelte';
import Error from '$lib/Error/RateLimited.svelte';
- export let subsPlease: SubsPlease;
- export let scheduledMedia: Partial<Media[]>;
- export let forceListMode = false;
- export let user;
+ interface Props {
+ subsPlease: SubsPlease;
+ scheduledMedia: Partial<Media[]>;
+ forceListMode?: boolean;
+ user: any;
+ }
+
+ let {
+ subsPlease,
+ scheduledMedia,
+ forceListMode = false,
+ user
+ }: Props = $props();
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let day: string | null = parseOrDefault(urlParameters, 'day', null);
- let mediaListPromise: Promise<Media[]>;
+ let mediaListPromise: Promise<Media[]> = $state();
onMount(async () => {
if (user === undefined || $identity.id === -2) mediaListPromise = Promise.resolve([]);
diff --git a/src/lib/Settings/Categories/Attributions.svelte b/src/lib/Settings/Categories/Attributions.svelte
index 250b08c3..28f69a74 100644
--- a/src/lib/Settings/Categories/Attributions.svelte
+++ b/src/lib/Settings/Categories/Attributions.svelte
@@ -34,7 +34,7 @@
</li> -->
</ul>
-<p />
+<p></p>
<details open class="card-clear">
<summary>Outbound Link Disclaimer</summary>
diff --git a/src/lib/Settings/Categories/Cache.svelte b/src/lib/Settings/Categories/Cache.svelte
index e65b8081..df0165d5 100644
--- a/src/lib/Settings/Categories/Cache.svelte
+++ b/src/lib/Settings/Categories/Cache.svelte
@@ -11,7 +11,7 @@
<a href="https://due.moe">due.moe</a>'s site data will clear these caches too.
</small>
-<p />
+<p></p>
Re-cache AniList media lists every
<input
@@ -22,7 +22,7 @@ Re-cache AniList media lists every
max="60"
placeholder="30"
size="1"
- on:change={() =>
+ onchange={() =>
($settings.cacheMinutes < 1 && ($settings.cacheMinutes = 1)) ||
($settings.cacheMinutes > 60 && ($settings.cacheMinutes = 60))}
/>
@@ -39,7 +39,7 @@ Re-cache manga data every
max="1440"
placeholder="120"
size="2"
- on:change={() =>
+ onchange={() =>
($settings.cacheMangaMinutes < 5 && ($settings.cacheMangaMinutes = 5)) ||
($settings.cacheMangaMinutes > 1440 && ($settings.cacheMangaMinutes = 1440))}
/>
diff --git a/src/lib/Settings/Categories/Calculation.svelte b/src/lib/Settings/Categories/Calculation.svelte
index 4a202fff..1f6eeaa1 100644
--- a/src/lib/Settings/Categories/Calculation.svelte
+++ b/src/lib/Settings/Categories/Calculation.svelte
@@ -75,7 +75,7 @@
{#if !$settings.calculateGuessingDisabled}
<br />
- <select bind:value={$settings.calculateGuessMethod} on:change={pruneAllManga}>
+ <select bind:value={$settings.calculateGuessMethod} onchange={pruneAllManga}>
<option value="mode">Mode (fast, moderate to low accuracy)</option>
<option value="median">Median (moderate speed, high accuracy, recommended)</option>
<option value="iqr_median">Interquartile Range with Median (slower, high accuracy)</option>
diff --git a/src/lib/Settings/Categories/Debug.svelte b/src/lib/Settings/Categories/Debug.svelte
index 79e18c03..00c5852e 100644
--- a/src/lib/Settings/Categories/Debug.svelte
+++ b/src/lib/Settings/Categories/Debug.svelte
@@ -12,7 +12,7 @@
<SettingCheckboxToggle setting="debugDummyLists" text={$locale().debug.dummyLists} />
<button
- on:click={() => {
+ onclick={() => {
localStorage.removeItem('anime');
localStorage.removeItem('manga');
addNotification(
@@ -23,10 +23,10 @@
}}>{$locale().debug.clearCaches}</button
>
-<p />
+<p></p>
<button
- on:click={() => {
+ onclick={() => {
settings.reset();
addNotification(
options({
@@ -41,10 +41,10 @@
{$locale().debug.resetAllSettings.hint}
</SettingHint>
-<p />
+<p></p>
<button
- on:click={() => {
+ onclick={() => {
localStorage.clear();
addNotification(
options({
diff --git a/src/lib/Settings/Categories/Display.svelte b/src/lib/Settings/Categories/Display.svelte
index 3b572b7b..a83157fb 100644
--- a/src/lib/Settings/Categories/Display.svelte
+++ b/src/lib/Settings/Categories/Display.svelte
@@ -85,7 +85,7 @@
<SettingHint lineBreak>
Media where either the next episode's release date is unknown or the chapter count could not be
resolved is considered unresolved.
- <p />
+ <p></p>
<span>
Additionally, you hard exclude specific media from <a href={root('/')}>due.moe</a> on AniList.
To exclude any media from being included in <b>any</b> <a href={root('/')}>due.moe</a>
@@ -107,7 +107,7 @@
</span>
</SettingHint>
-<p />
+<p></p>
<b>{$locale().settings.display.categories.hidePanels}</b><br />
<SettingCheckboxToggle
@@ -160,7 +160,7 @@
}}
/>
-<p />
+<p></p>
<b>{$locale().settings.display.categories.collapsePanelsByDefault}</b><br />
<SettingCheckboxToggle
@@ -175,7 +175,7 @@
/>
<SettingCheckboxToggle setting="displayMangaCollapsed" text={$locale().settings.media.manga} />
-<p />
+<p></p>
<b>{$locale().settings.display.categories.motionAndAccessibility.title}</b><br />
<SettingCheckboxToggle
@@ -215,7 +215,7 @@
</button>
{/if}
-<p />
+<p></p>
<SettingCheckboxToggle
setting="displayAniListNotifications"
@@ -242,7 +242,7 @@
</SettingHint>
</SettingCheckboxToggle>
-<p />
+<p></p>
<b>{$locale().settings.display.categories.dateAndTime.title}</b><br />
<SettingCheckboxToggle
@@ -262,7 +262,7 @@
text={$locale().settings.display.categories.dateAndTime.fields.abbreviateCountdown}
/>
-<p />
+<p></p>
<SettingCheckboxToggle
setting="displayDisableLastActivityWarning"
@@ -277,7 +277,7 @@
</SettingHint>
</SettingCheckboxToggle>
-<p />
+<p></p>
<b>Show lists with media covers instead of text</b><br />
<SettingCheckboxToggle setting="displayCoverModeAnime" text="Anime" lineBreak={false} />
@@ -297,7 +297,7 @@
min="50"
placeholder="116.609"
size="3"
- on:change={() => {
+ onchange={() => {
if ($settings.displayCoverWidth === null) {
$settings.displayCoverWidth = 116.609;
@@ -313,7 +313,7 @@
<br />
{/if}
-<p />
+<p></p>
<b>{$locale().settings.display.categories.media.title}</b><br />
<SettingCheckboxToggle
@@ -357,7 +357,7 @@
<br />
{/if}
-<p />
+<p></p>
<select bind:value={$settings.displayOutboundLinksTo}>
<option value="anilist">AniList</option>
@@ -390,7 +390,7 @@
<br />
-<select bind:value={$settings.displayAoButa} on:change={onHelperChange}>
+<select bind:value={$settings.displayAoButa} onchange={onHelperChange}>
<option value="mai">{$locale().settings.display.categories.helper.options.mai}</option>
<option value="mai_2">{$locale().settings.display.categories.helper.options.mai} #2</option>
<option value="nodoka">{$locale().settings.display.categories.helper.options.nodoka}</option>
diff --git a/src/lib/Settings/Categories/RSSFeeds.svelte b/src/lib/Settings/Categories/RSSFeeds.svelte
index a225b137..9ced917b 100644
--- a/src/lib/Settings/Categories/RSSFeeds.svelte
+++ b/src/lib/Settings/Categories/RSSFeeds.svelte
@@ -6,13 +6,17 @@
import SettingHint from '../SettingHint.svelte';
import tooltip from '$lib/Tooltip/tooltip';
- export let user: any;
+ interface Props {
+ user: any;
+ }
+
+ let { user }: Props = $props();
const { addNotification } = getNotificationsContext();
</script>
<button
- on:click={() => {
+ onclick={() => {
addNotification(
options({
heading: 'RSS feed URL copied to clipboard'
@@ -31,7 +35,7 @@ Your AniList notifications RSS feed URL
<SettingHint lineBreak>
This <a
href={'#'}
- on:click={(e) => e.preventDefault()}
+ onclick={(e) => e.preventDefault()}
target="_blank"
title={$locale().settings.rssFeeds.tooltips.rss}
use:tooltip
diff --git a/src/lib/Settings/Categories/SettingSync.svelte b/src/lib/Settings/Categories/SettingSync.svelte
index 39e62954..24a35c79 100644
--- a/src/lib/Settings/Categories/SettingSync.svelte
+++ b/src/lib/Settings/Categories/SettingSync.svelte
@@ -13,7 +13,7 @@
{#if !$settings.settingsSync}
<button
- on:click={() => {
+ onclick={() => {
$settings.settingsSync = true;
fetch(root(`/api/configuration?id=${$identity.id}`)).then((response) => {
@@ -50,9 +50,9 @@
<SettingHint lineBreak>
{$locale().settings.settingsSync.buttons.pull.hint}
</SettingHint>
- <p />
+ <p></p>
<button
- on:click={() => {
+ onclick={() => {
$settings.settingsSync = true;
fetch(root(`/api/configuration`), {
@@ -76,7 +76,7 @@
</SettingHint>
{:else}
<button
- on:click={() => {
+ onclick={() => {
$settings.settingsSync = false;
addNotification(
@@ -89,7 +89,7 @@
{$locale().settings.settingsSync.buttons.disable}
</button>
<button
- on:click={() => {
+ onclick={() => {
fetch(root(`/api/configuration?id=${$identity.id}`), {
method: 'DELETE'
}).then((response) => {
@@ -108,7 +108,7 @@
{$locale().settings.settingsSync.buttons.delete}
</button>
- <p />
+ <p></p>
<b>Last Push</b>: {$locale().dateFormatter($settingsSyncTimes.lastPush)}
<br />
diff --git a/src/lib/Settings/Category.svelte b/src/lib/Settings/Category.svelte
index a5b3e211..aef86d77 100644
--- a/src/lib/Settings/Category.svelte
+++ b/src/lib/Settings/Category.svelte
@@ -1,8 +1,19 @@
<script lang="ts">
- export let title = '';
- export let id = title.toLowerCase();
- export let open = true;
- export let newLine = true;
+ interface Props {
+ title?: string;
+ id?: any;
+ open?: boolean;
+ newLine?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ title = '',
+ id = title.toLowerCase(),
+ open = true,
+ newLine = true,
+ children
+ }: Props = $props();
</script>
<details {open} {id}>
@@ -10,9 +21,9 @@
<summary>{title}</summary>
{/if}
- <slot />
+ {@render children?.()}
</details>
{#if newLine}
- <p />
+ <p></p>
{/if}
diff --git a/src/lib/Settings/SettingCheckboxToggle.svelte b/src/lib/Settings/SettingCheckboxToggle.svelte
index 6a16edec..85dbfcdc 100644
--- a/src/lib/Settings/SettingCheckboxToggle.svelte
+++ b/src/lib/Settings/SettingCheckboxToggle.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import settings, { type Settings } from '$stores/settings';
type BooleanSettingsKeys<T> = {
@@ -6,19 +8,37 @@
};
type SettingsBooleanKeys = BooleanSettingsKeys<Settings>;
- export let sectionBreak = false;
- export let disabled = false;
- export let text: string | (() => string);
- export let setting: SettingsBooleanKeys[keyof SettingsBooleanKeys];
- export let lineBreak = true;
- export let onChange: () => void = () => {
- return;
- };
- export let invert = false;
- export let id: string | null = null;
-
- $: checked = setting ? (invert ? !$settings[setting] : $settings[setting]) : false;
- $: field = text instanceof Function ? text() : text;
+ interface Props {
+ sectionBreak?: boolean;
+ disabled?: boolean;
+ text: string | (() => string);
+ setting: SettingsBooleanKeys[keyof SettingsBooleanKeys];
+ lineBreak?: boolean;
+ onChange?: () => void;
+ invert?: boolean;
+ id?: string | null;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ sectionBreak = false,
+ disabled = false,
+ text,
+ setting,
+ lineBreak = true,
+ onChange = () => {
+ return;
+ },
+ invert = false,
+ id = null,
+ children
+ }: Props = $props();
+
+ let checked = $state(false);
+ run(() => {
+ checked = setting ? (invert ? !$settings[setting] : $settings[setting]) : false;
+ });
+ let field = $derived(text instanceof Function ? text() : text);
// const toggler = (key: keyof Settings) => [
// () =>
@@ -53,11 +73,11 @@
};
</script>
-<input type="checkbox" on:change={check} bind:checked {id} />
+<input type="checkbox" onchange={check} bind:checked {id} />
<span
- on:click={flip}
- on:keydown={() => {
+ onclick={flip}
+ onkeydown={() => {
return;
}}
role="button"
@@ -72,12 +92,12 @@
{/if}
</span>
-<slot />
+{@render children?.()}
{#if lineBreak}
<br />
{/if}
{#if sectionBreak}
- <p />
+ <p></p>
{/if}
diff --git a/src/lib/Settings/SettingHint.svelte b/src/lib/Settings/SettingHint.svelte
index f82f061c..c5a9072f 100644
--- a/src/lib/Settings/SettingHint.svelte
+++ b/src/lib/Settings/SettingHint.svelte
@@ -1,5 +1,10 @@
<script lang="ts">
- export let lineBreak = false;
+ interface Props {
+ lineBreak?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let { lineBreak = false, children }: Props = $props();
</script>
{#if lineBreak}
@@ -7,5 +12,5 @@
{/if}
<small style="opacity: 80%;">
- <slot />
+ {@render children?.()}
</small>
diff --git a/src/lib/Settings/SettingToggle.svelte b/src/lib/Settings/SettingToggle.svelte
index 0d177b50..0d3b893a 100644
--- a/src/lib/Settings/SettingToggle.svelte
+++ b/src/lib/Settings/SettingToggle.svelte
@@ -1,16 +1,28 @@
<script lang="ts">
import settings, { type Settings } from '$stores/settings';
- export let setting: keyof Settings;
- export let on = '';
- export let off = '';
- export let sectionBreak = false;
- export let disabled = false;
+ interface Props {
+ setting: keyof Settings;
+ on?: string;
+ off?: string;
+ sectionBreak?: boolean;
+ disabled?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ setting,
+ on = '',
+ off = '',
+ sectionBreak = false,
+ disabled = false,
+ children
+ }: Props = $props();
</script>
<a
href={'#'}
- on:click={() =>
+ onclick={() =>
disabled
? {}
: $settings[setting]
@@ -20,16 +32,16 @@
{#if disabled}
<strike>
{$settings[setting] ? on : off}
- <slot />
+ {@render children?.()}
</strike>
{:else}
{$settings[setting] ? on : off}
- <slot />
+ {@render children?.()}
{/if}
</a>
<br />
{#if sectionBreak}
- <p />
+ <p></p>
{/if}
diff --git a/src/lib/Tools/ActivityHistory/Grid.svelte b/src/lib/Tools/ActivityHistory/Grid.svelte
index db9f3839..8789a786 100644
--- a/src/lib/Tools/ActivityHistory/Grid.svelte
+++ b/src/lib/Tools/ActivityHistory/Grid.svelte
@@ -12,12 +12,16 @@
import tooltip from '$lib/Tooltip/tooltip';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
- export let activityData: ActivityHistoryEntry[] | null = null;
- export let currentYear = new Date().getFullYear();
+ interface Props {
+ user: AniListAuthorisation;
+ activityData?: ActivityHistoryEntry[] | null;
+ currentYear?: any;
+ }
+
+ let { user, activityData = null, currentYear = new Date().getFullYear() }: Props = $props();
- let activityHistoryData: ActivityHistoryEntry[];
- let baseHue = Math.floor(Math.random() * 360);
+ let activityHistoryData: ActivityHistoryEntry[] = $state();
+ let baseHue = $state(Math.floor(Math.random() * 360));
onMount(async () => {
clearAllParameters();
@@ -45,8 +49,8 @@
<div
class="grid-item"
style="background-color: {gradientColour(activity.amount, highestActivity, baseHue)}"
- on:click={() => (baseHue = Math.floor(Math.random() * 360))}
- on:keydown={() => {
+ onclick={() => (baseHue = Math.floor(Math.random() * 360))}
+ onkeydown={() => {
return;
}}
role="button"
@@ -55,7 +59,7 @@
title={`Date: ${new Date(activity.date * 1000).toLocaleDateString()}\nAmount: ${
activity.amount
}`}
- />
+></div>
{/each}
</div>
{/if}
diff --git a/src/lib/Tools/ActivityHistory/Tool.svelte b/src/lib/Tools/ActivityHistory/Tool.svelte
index b6e66a5e..07b2096a 100644
--- a/src/lib/Tools/ActivityHistory/Tool.svelte
+++ b/src/lib/Tools/ActivityHistory/Tool.svelte
@@ -14,10 +14,14 @@
import Skeleton from '$lib/Loading/Skeleton.svelte';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let activityHistoryData: Promise<ActivityHistoryEntry[]>;
- let generated = false;
+ let { user }: Props = $props();
+
+ let activityHistoryData: Promise<ActivityHistoryEntry[]> = $state();
+ let generated = $state(false);
onMount(async () => {
clearAllParameters();
@@ -79,18 +83,18 @@
<div class="card">
<ActivityHistoryGrid {user} />
- <p />
+ <p></p>
- <div id="grid-final" />
+ <div id="grid-final"></div>
{#if generated}
- <p />
+ <p></p>
{/if}
- <button on:click={screenshot}>Generate grid image</button>
+ <button onclick={screenshot}>Generate grid image</button>
</div>
- <p />
+ <p></p>
<details open>
<summary>Days in risk of developing an activity history hole</summary>
diff --git a/src/lib/Tools/Birthdays.svelte b/src/lib/Tools/Birthdays.svelte
index 97ff40d8..17c91709 100644
--- a/src/lib/Tools/Birthdays.svelte
+++ b/src/lib/Tools/Birthdays.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { ACDBBirthdays, type ACDBBirthday } from '$lib/Data/Birthday/secondary';
@@ -18,12 +20,12 @@
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let date = new Date();
- let month = parseOrDefault(urlParameters, 'month', date.getMonth() + 1);
- let day = parseOrDefault(urlParameters, 'day', date.getDate());
- let anisearchBirthdays: Promise<aniSearchBirthday[]>;
- let acdbBirthdays: Promise<ACDBBirthday[]>;
+ let month = $state(parseOrDefault(urlParameters, 'month', date.getMonth() + 1));
+ let day = $state(parseOrDefault(urlParameters, 'day', date.getDate()));
+ let anisearchBirthdays: Promise<aniSearchBirthday[]> = $state();
+ let acdbBirthdays: Promise<ACDBBirthday[]> = $state();
- $: {
+ run(() => {
month = Math.min(month, 12);
month = Math.max(month, 1);
day = Math.min(day, new Date(new Date().getFullYear(), month, 0).getDate());
@@ -39,7 +41,7 @@
clearAllParameters(['month', 'day']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => clearAllParameters(['month', 'day']));
diff --git a/src/lib/Tools/DumpProfile.svelte b/src/lib/Tools/DumpProfile.svelte
index 45d4ffc9..ac0184ae 100644
--- a/src/lib/Tools/DumpProfile.svelte
+++ b/src/lib/Tools/DumpProfile.svelte
@@ -5,7 +5,7 @@
import InputTemplate from './InputTemplate.svelte';
import LZString from 'lz-string';
- let submission = '';
+ let submission = $state('');
// Credit: @hoh
const decodeJSON = (about: string): JSON | null => {
@@ -26,7 +26,7 @@
};
</script>
-<!-- svelte-ignore missing-declaration -->
+<!-- svelte-ignore missing_declaration -->
<InputTemplate field="Username" bind:submission event="Dump User" submitText="Dump">
{#await dumpUser(submission)}
<Skeleton card={false} count={1} height="500px" />
@@ -36,7 +36,7 @@
<pre>{JSON.stringify(dump, null, 2)}</pre>
{#if decoded && (dump.about || '').includes('[](json')}
- <p />
+ <p></p>
<pre>{JSON.stringify(decoded, null, 2).replaceAll(/\\n/g, '\n')}</pre>
{/if}
diff --git a/src/lib/Tools/EpisodeDiscussionCollector.svelte b/src/lib/Tools/EpisodeDiscussionCollector.svelte
index 4c61f3cf..746dff22 100644
--- a/src/lib/Tools/EpisodeDiscussionCollector.svelte
+++ b/src/lib/Tools/EpisodeDiscussionCollector.svelte
@@ -6,7 +6,7 @@
import InputTemplate from './InputTemplate.svelte';
import tooltip from '$lib/Tooltip/tooltip';
- let submission = '';
+ let submission = $state('');
onMount(clearAllParameters);
</script>
@@ -54,7 +54,7 @@
</p>
{/await}
{:else}
- <p />
+ <p></p>
Enter a username to search for to continue.
{/if}
diff --git a/src/lib/Tools/FollowFix.svelte b/src/lib/Tools/FollowFix.svelte
index b9fddeff..411b1a92 100644
--- a/src/lib/Tools/FollowFix.svelte
+++ b/src/lib/Tools/FollowFix.svelte
@@ -3,23 +3,27 @@
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let input = '';
- let submit = '';
+ let { user }: Props = $props();
+
+ let input = $state('');
+ let submit = $state('');
</script>
{#if user === undefined}
<LogInRestricted />
{:else}
<p>
- <!-- svelte-ignore missing-declaration -->
+ <!-- svelte-ignore missing_declaration -->
<input
type="text"
minlength="1"
placeholder="Username"
bind:value={input}
- on:keypress={(e) => {
+ onkeypress={(e) => {
if (e.key === 'Enter') {
submit = input;
@@ -28,7 +32,7 @@
}
}}
/>
- <a href={'#'} on:click={() => (submit = input)}>
+ <a href={'#'} onclick={() => (submit = input)}>
Toggle follow for {input.length === 0 ? '...' : input}
</a>
</p>
diff --git a/src/lib/Tools/Hayai.svelte b/src/lib/Tools/Hayai.svelte
index 1790af53..127c07b9 100644
--- a/src/lib/Tools/Hayai.svelte
+++ b/src/lib/Tools/Hayai.svelte
@@ -90,13 +90,13 @@
)}
</small>
- <p />
+ <p></p>
{@html applyBionicReadingToString(
`After selecting an EPUB file, 早い will apply a bionic reading filter over any and all words, and return the newly created "bionic" EPUB file.`
)}
- <p />
+ <p></p>
- <input type="file" id="epub-file" accept=".epub" on:change={handleFileUpload} />
+ <input type="file" id="epub-file" accept=".epub" onchange={handleFileUpload} />
</div>
diff --git a/src/lib/Tools/HololiveBirthdays.svelte b/src/lib/Tools/HololiveBirthdays.svelte
index 68a591de..769f5d6f 100644
--- a/src/lib/Tools/HololiveBirthdays.svelte
+++ b/src/lib/Tools/HololiveBirthdays.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { onMount } from 'svelte';
@@ -9,14 +11,14 @@
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let date = new Date();
- let month = parseOrDefault(urlParameters, 'month', date.getMonth() + 1);
- let day = parseOrDefault(urlParameters, 'day', date.getDate());
+ let month = $state(parseOrDefault(urlParameters, 'month', date.getMonth() + 1));
+ let day = $state(parseOrDefault(urlParameters, 'day', date.getDate()));
- $: todaysBirthdays = birthdays.filter(
+ let todaysBirthdays = $derived(birthdays.filter(
(birthday) => birthday.month === month && birthday.day === day
- );
+ ));
- $: {
+ run(() => {
month = Math.min(month, 12);
month = Math.max(month, 1);
day = Math.min(day, new Date(new Date().getFullYear(), month, 0).getDate());
@@ -28,7 +30,7 @@
clearAllParameters(['month', 'day']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => clearAllParameters(['month', 'day']));
</script>
diff --git a/src/lib/Tools/InputTemplate.svelte b/src/lib/Tools/InputTemplate.svelte
index 72e2f807..52b9fd46 100644
--- a/src/lib/Tools/InputTemplate.svelte
+++ b/src/lib/Tools/InputTemplate.svelte
@@ -3,33 +3,49 @@
import { onMount } from 'svelte';
import SettingHint from '$lib/Settings/SettingHint.svelte';
- export let field: string;
- export let submission: string;
- export let event: string | undefined = undefined;
- export let submitText: string;
- export let saveParameters: string[] = [];
- export let onSubmit = () => {
+ interface Props {
+ field: string;
+ submission: string;
+ event?: string | undefined;
+ submitText: string;
+ saveParameters?: string[];
+ onSubmit?: any;
+ preserveCase?: boolean;
+ prompt?: any;
+ hint?: string | undefined;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ field,
+ submission = $bindable(),
+ event = undefined,
+ submitText,
+ saveParameters = [],
+ onSubmit = () => {
return;
- };
- export let preserveCase = false;
- export let prompt = `Enter a ${
+ },
+ preserveCase = false,
+ prompt = `Enter a ${
preserveCase ? field : field.toLowerCase()
- } to search for to continue.`;
- export let hint: string | undefined = undefined;
+ } to search for to continue.`,
+ hint = undefined,
+ children
+ }: Props = $props();
- let input = '';
+ let input = $state('');
onMount(() => clearAllParameters(saveParameters));
</script>
<div class="card">
<p>
- <!-- svelte-ignore missing-declaration -->
+ <!-- svelte-ignore missing_declaration -->
<input
type="text"
placeholder={field}
bind:value={input}
- on:keypress={(e) => {
+ onkeypress={(e) => {
if (e.key === 'Enter') {
submission = input;
@@ -42,7 +58,7 @@
/>
<button
class="button-lined"
- on:click={() => {
+ onclick={() => {
submission = input;
onSubmit();
@@ -62,9 +78,9 @@
</p>
{#if submission !== ''}
- <slot />
+ {@render children?.()}
{:else}
- <p />
+ <p></p>
{prompt}
{/if}
diff --git a/src/lib/Tools/Likes.svelte b/src/lib/Tools/Likes.svelte
index 7b626c94..d86c5e12 100644
--- a/src/lib/Tools/Likes.svelte
+++ b/src/lib/Tools/Likes.svelte
@@ -7,16 +7,16 @@
import settings from '$stores/settings';
import InputTemplate from './InputTemplate.svelte';
- let submission = '';
+ let submission = $state('');
- $: normalisedSubmission = submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$2');
- $: submissionType = submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$1');
- $: likesPromise =
- submissionType === 'activity'
+ let normalisedSubmission = $derived(submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$2'));
+ let submissionType = $derived(submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$1'));
+ let likesPromise =
+ $derived(submissionType === 'activity'
? activityLikes(Number(normalisedSubmission))
: submissionType === 'thread'
? threadLikes(Number(normalisedSubmission))
- : Promise.resolve(null);
+ : Promise.resolve(null));
</script>
<InputTemplate
diff --git a/src/lib/Tools/Picker.svelte b/src/lib/Tools/Picker.svelte
index 3f20300f..8628f757 100644
--- a/src/lib/Tools/Picker.svelte
+++ b/src/lib/Tools/Picker.svelte
@@ -4,13 +4,17 @@
import root from '$lib/Utility/root';
import { tools } from './tools';
- export let tool: string;
+ interface Props {
+ tool: string;
+ }
+
+ let { tool = $bindable() }: Props = $props();
</script>
<blockquote>
<select
bind:value={tool}
- on:change={() => {
+ onchange={() => {
if (browser) goto(root(`/tools/${tool}`));
}}
>
diff --git a/src/lib/Tools/RandomFollower.svelte b/src/lib/Tools/RandomFollower.svelte
index acb5a33a..30b58470 100644
--- a/src/lib/Tools/RandomFollower.svelte
+++ b/src/lib/Tools/RandomFollower.svelte
@@ -5,8 +5,8 @@
import TextSwap from '$lib/Layout/TextTransition.svelte';
import InputTemplate from './InputTemplate.svelte';
- let submission = '';
- let randomSeed = 0;
+ let submission = $state('');
+ let randomSeed = $state(0);
</script>
<InputTemplate
@@ -21,7 +21,7 @@
{:then users}
{@const user = users[Math.floor(randomSeed * users.length)]}
- <p />
+ <p></p>
<a href={`https://anilist.co/user/${user.id}`} target="_blank">
<TextSwap text={user.name} />
diff --git a/src/lib/Tools/SequelCatcher/List.svelte b/src/lib/Tools/SequelCatcher/List.svelte
index 009df219..788e8e01 100644
--- a/src/lib/Tools/SequelCatcher/List.svelte
+++ b/src/lib/Tools/SequelCatcher/List.svelte
@@ -4,10 +4,14 @@
import { outboundLink } from '$lib/Media/links';
import settings from '$stores/settings';
- export let mediaListUnchecked: Media[];
+ interface Props {
+ mediaListUnchecked: Media[];
+ }
+
+ let { mediaListUnchecked }: Props = $props();
- let includeCurrent = false;
- let includeSideStories = false;
+ let includeCurrent = $state(false);
+ let includeSideStories = $state(false);
const matchCheck = (media: Media | undefined, swap = false) =>
(media &&
@@ -30,7 +34,7 @@ paused)
<input type="checkbox" bind:checked={includeSideStories} /> Include side stories (e.g., OVAs,
specials, etc.)
-<p />
+<p></p>
<ol class="media-list">
{#each filterRelations( mediaListUnchecked.filter((media) => media.mediaListEntry?.status === 'COMPLETED'), includeSideStories ) as { media, unwatchedRelations }}
diff --git a/src/lib/Tools/SequelCatcher/Tool.svelte b/src/lib/Tools/SequelCatcher/Tool.svelte
index a954b4d7..547903b7 100644
--- a/src/lib/Tools/SequelCatcher/Tool.svelte
+++ b/src/lib/Tools/SequelCatcher/Tool.svelte
@@ -12,9 +12,13 @@
import Skeleton from '$lib/Loading/Skeleton.svelte';
import Username from '$lib/Layout/Username.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let mediaList: Promise<Media[]>;
+ let { user }: Props = $props();
+
+ let mediaList: Promise<Media[]> = $state();
onMount(async () => {
if (user === undefined || $identity.id === -2) return;
@@ -71,7 +75,7 @@
<Message message="" loader="ripple" slot withReload fullscreen>Error fetching media.</Message>
{/await}
- <p />
+ <p></p>
<blockquote style="margin: 0 0 0 1.5rem;">
Thanks to <Username username="sevengirl" /> and <Username username="esthereae" /> for the idea!
diff --git a/src/lib/Tools/SequelSpy/Prequels.svelte b/src/lib/Tools/SequelSpy/Prequels.svelte
index b22db3af..0b01b646 100644
--- a/src/lib/Tools/SequelSpy/Prequels.svelte
+++ b/src/lib/Tools/SequelSpy/Prequels.svelte
@@ -6,7 +6,11 @@
import settings from '$stores/settings';
import type { Media } from '$lib/Data/AniList/media';
- export let currentPrequels: MediaPrequel[];
+ interface Props {
+ currentPrequels: MediaPrequel[];
+ }
+
+ let { currentPrequels }: Props = $props();
const prequelAiringTime = (prequel: MediaPrequel) =>
airingTime(prequel as unknown as Media, null, false, true);
diff --git a/src/lib/Tools/SequelSpy/Tool.svelte b/src/lib/Tools/SequelSpy/Tool.svelte
index caec4a46..b50e2f84 100644
--- a/src/lib/Tools/SequelSpy/Tool.svelte
+++ b/src/lib/Tools/SequelSpy/Tool.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import { prequels, type MediaPrequel } from '$lib/Data/AniList/prequels';
import { onMount } from 'svelte';
@@ -11,25 +13,29 @@
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
import Prequels from './Prequels.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
+
+ let { user }: Props = $props();
- let currentPrequels: Promise<MediaPrequel[]> = Promise.resolve([]) as Promise<MediaPrequel[]>;
+ let currentPrequels: Promise<MediaPrequel[]> = $state(Promise.resolve([]) as Promise<MediaPrequel[]>);
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
- let year = parseOrDefault(urlParameters, 'year', new Date().getFullYear());
- let season = parseOrDefault(urlParameters, 'season', getSeason());
+ let year = $state(parseOrDefault(urlParameters, 'year', new Date().getFullYear()));
+ let season = $state(parseOrDefault(urlParameters, 'season', getSeason()));
- $: {
+ run(() => {
if (year.toString().length === 4 && $identity.id !== -2 && user)
currentPrequels = prequels(user, year, season);
- }
- $: {
+ });
+ run(() => {
if (browser) {
$page.url.searchParams.set('year', year.toString());
$page.url.searchParams.set('season', season.toString());
clearAllParameters(['year', 'season']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => clearAllParameters(['year', 'season']));
</script>
@@ -54,7 +60,7 @@
<Prequels {currentPrequels} />
{/await}
- <p />
+ <p></p>
The count ratio is the number of episodes you've seen of any direct prequels, and the total
number of episodes of all direct prequels.
diff --git a/src/lib/Tools/Tracker/Tool.svelte b/src/lib/Tools/Tracker/Tool.svelte
index 8906e72d..2a1b38d7 100644
--- a/src/lib/Tools/Tracker/Tool.svelte
+++ b/src/lib/Tools/Tracker/Tool.svelte
@@ -4,14 +4,14 @@
import { onMount } from 'svelte';
import Message from '$lib/Loading/Message.svelte';
- let url = '';
- let title = '';
- let progress = 0;
- let error = '';
- let masterList: TrackerEntry[] | null = null;
+ let url = $state('');
+ let title = $state('');
+ let progress = $state(0);
+ let error = $state('');
+ let masterList: TrackerEntry[] | null = $state(null);
let confirmDelete = 0;
- $: listAccess = masterList || [];
+ let listAccess = $derived(masterList || []);
onMount(async () => {
masterList = await database.entries.toArray();
@@ -74,9 +74,9 @@
<input type="url" placeholder="URL" bind:value={url} />
<input type="text" placeholder="Title" bind:value={title} />
<input type="number" placeholder="Progress (defaults to 0)" bind:value={progress} />
- <button class="button-lined" on:click={() => addEntry(url, title, progress)}> Add </button>
+ <button class="button-lined" onclick={() => addEntry(url, title, progress)}> Add </button>
- <p />
+ <p></p>
{#if masterList === null}
<Message message="Loading entries ..." />
@@ -95,7 +95,7 @@
type="number"
value={entry.progress}
size={3}
- on:change={(e) =>
+ onchange={(e) =>
adjustEntry(entry.id, e.target ? e.target.value || entry.progress : entry.progress)}
/>
@@ -103,17 +103,17 @@
<span class="opaque">|</span>
<button
class="button-square button-action"
- on:click={() => adjustEntry(entry.id, entry.progress - 1)}
+ onclick={() => adjustEntry(entry.id, entry.progress - 1)}
>-
</button>
<button
class="button-square button-action"
- on:click={() => adjustEntry(entry.id, entry.progress + 1)}
+ onclick={() => adjustEntry(entry.id, entry.progress + 1)}
>
+
</button>
<span class="opaque">|</span>
- <button on:click={() => deleteEntry(entry.id)}>Remove</button>
+ <button onclick={() => deleteEntry(entry.id)}>Remove</button>
</span>
</div>
</li>
diff --git a/src/lib/Tools/UmaMusumeBirthdays.svelte b/src/lib/Tools/UmaMusumeBirthdays.svelte
index 29b1faa6..c9b03287 100644
--- a/src/lib/Tools/UmaMusumeBirthdays.svelte
+++ b/src/lib/Tools/UmaMusumeBirthdays.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { page } from '$app/stores';
import Error from '$lib/Error/RateLimited.svelte';
@@ -23,11 +25,11 @@
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let date = new Date();
- let month = parseOrDefault(urlParameters, 'month', date.getMonth() + 1);
- let day = parseOrDefault(urlParameters, 'day', date.getDate());
- let umapyoi: Promise<Birthday[]>;
+ let month = $state(parseOrDefault(urlParameters, 'month', date.getMonth() + 1));
+ let day = $state(parseOrDefault(urlParameters, 'day', date.getDate()));
+ let umapyoi: Promise<Birthday[]> = $state();
- $: {
+ run(() => {
month = Math.min(month, 12);
month = Math.max(month, 1);
day = Math.min(day, new Date(new Date().getFullYear(), month, 0).getDate());
@@ -39,7 +41,7 @@
clearAllParameters(['month', 'day']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => {
clearAllParameters(['month', 'day']);
diff --git a/src/lib/Tools/Wrapped/ActivityHistory.svelte b/src/lib/Tools/Wrapped/ActivityHistory.svelte
index 3da401d4..e652a487 100644
--- a/src/lib/Tools/Wrapped/ActivityHistory.svelte
+++ b/src/lib/Tools/Wrapped/ActivityHistory.svelte
@@ -3,10 +3,19 @@
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import ActivityHistoryGrid from '../ActivityHistory/Grid.svelte';
- export let user: AniListAuthorisation;
- export let activities: ActivityHistoryEntry[];
- export let year: number;
- export let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL';
+ interface Props {
+ user: AniListAuthorisation;
+ activities: ActivityHistoryEntry[];
+ year: number;
+ activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL';
+ }
+
+ let {
+ user,
+ activities,
+ year,
+ activityHistoryPosition
+ }: Props = $props();
</script>
<div
diff --git a/src/lib/Tools/Wrapped/Media.svelte b/src/lib/Tools/Wrapped/Media.svelte
index ea8a989b..7f8d4f66 100644
--- a/src/lib/Tools/Wrapped/Media.svelte
+++ b/src/lib/Tools/Wrapped/Media.svelte
@@ -4,14 +4,27 @@
import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte';
import proxy from '$lib/Utility/proxy';
- export let animeList: Media[] | undefined;
- export let mangaList: Media[] | undefined;
- export let wrapped: Wrapped;
- export let updateWidth: () => void;
- export let highestRatedMediaPercentage: boolean;
- export let highestRatedCount: number;
- export let animeMostTitle: string;
- export let mangaMostTitle: string;
+ interface Props {
+ animeList: Media[] | undefined;
+ mangaList: Media[] | undefined;
+ wrapped: Wrapped;
+ updateWidth: () => void;
+ highestRatedMediaPercentage: boolean;
+ highestRatedCount: number;
+ animeMostTitle: string;
+ mangaMostTitle: string;
+ }
+
+ let {
+ animeList,
+ mangaList,
+ wrapped,
+ updateWidth,
+ highestRatedMediaPercentage,
+ highestRatedCount,
+ animeMostTitle,
+ mangaMostTitle
+ }: Props = $props();
</script>
{#if animeList !== undefined || mangaList !== undefined}
@@ -28,7 +41,7 @@
)}
alt="Highest Rated Anime Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
@@ -67,7 +80,7 @@
)}
alt="Highest Rated Manga Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
diff --git a/src/lib/Tools/Wrapped/MediaExtras.svelte b/src/lib/Tools/Wrapped/MediaExtras.svelte
index 9e755ea5..3e083c0b 100644
--- a/src/lib/Tools/Wrapped/MediaExtras.svelte
+++ b/src/lib/Tools/Wrapped/MediaExtras.svelte
@@ -2,10 +2,19 @@
import type { TopMedia } from '$lib/Data/AniList/wrapped';
import proxy from '$lib/Utility/proxy';
- export let topMedia: TopMedia;
- export let updateWidth: () => void;
- export let highestRatedGenreTagPercentage: boolean;
- export let genreTagTitle: string;
+ interface Props {
+ topMedia: TopMedia;
+ updateWidth: () => void;
+ highestRatedGenreTagPercentage: boolean;
+ genreTagTitle: string;
+ }
+
+ let {
+ topMedia,
+ updateWidth,
+ highestRatedGenreTagPercentage,
+ genreTagTitle
+ }: Props = $props();
</script>
<div class="categories-grid" style="padding-top: 0;">
@@ -22,7 +31,7 @@
src={proxy(topMedia.topGenreMedia.coverImage.extraLarge)}
alt="Highest Rated Genre Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
@@ -53,7 +62,7 @@
src={proxy(topMedia.topTagMedia.coverImage.extraLarge)}
alt="Highest Rated Tag Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
diff --git a/src/lib/Tools/Wrapped/Tool.svelte b/src/lib/Tools/Wrapped/Tool.svelte
index 1484ab5c..af817051 100644
--- a/src/lib/Tools/Wrapped/Tool.svelte
+++ b/src/lib/Tools/Wrapped/Tool.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import userIdentity from '$stores/identity';
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import { onMount } from 'svelte';
@@ -30,153 +32,50 @@
import tooltip from '$lib/Tooltip/tooltip';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
-
- const currentYear = new Date(Date.now()).getFullYear();
- let selectedYear = new Date(Date.now()).getFullYear();
- let episodes = 0;
- let chapters = 0;
- let minutesWatched = 0;
- let animeList: Media[] | undefined = undefined;
- let mangaList: Media[] | undefined = undefined;
- let calculatedAnimeList: Media[] | undefined = undefined;
- let calculatedMangaList: Media[] | undefined = undefined;
- let originalAnimeList: Media[] | undefined = undefined;
- let originalMangaList: Media[] | undefined = undefined;
- let transparency = false;
- let lightTheme = true;
- let watermark = false;
- let includeMusic = false;
- let includeSpecials = true;
- let includeRepeats = false;
- let width = 1920;
- let lightMode = false;
- let highestRatedCount = 5;
- let genreTagCount = 5;
- let mounted = false;
- let generated = false;
- let disableActivityHistory = true;
- let excludedKeywordsInput = '';
- let excludedKeywords: string[] = [];
- let useFullActivityHistory = false;
- let topGenresTags = true;
- let topMedia: TopMedia;
- let highestRatedMediaPercentage = true;
- let highestRatedGenreTagPercentage = true;
- let genreTagsSort = SortOptions.SCORE;
- let mediaSort = SortOptions.SCORE;
- let includeMovies = true;
- let includeOVAs = true;
- let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL' = 'ORIGINAL';
- let includeOngoingMediaFromPreviousYears = false;
-
- $: {
- if (browser && mounted) {
- $page.url.searchParams.set('transparency', transparency.toString());
- $page.url.searchParams.set('lightTheme', lightTheme.toString());
- $page.url.searchParams.set('watermark', watermark.toString());
- $page.url.searchParams.set('includeMusic', includeMusic.toString());
- $page.url.searchParams.set('includeSpecials', includeSpecials.toString());
- $page.url.searchParams.set('includeRepeats', includeRepeats.toString());
- $page.url.searchParams.set('lightMode', lightMode.toString());
- $page.url.searchParams.set('highestRatedCount', highestRatedCount.toString());
- $page.url.searchParams.set('genreTagCount', genreTagCount.toString());
- $page.url.searchParams.set('disableActivityHistory', disableActivityHistory.toString());
- $page.url.searchParams.set(
- 'highestRatedMediaPercentage',
- highestRatedMediaPercentage.toString()
- );
- $page.url.searchParams.set(
- 'highestRatedGenreTagPercentage',
- highestRatedGenreTagPercentage.toString()
- );
- $page.url.searchParams.set('genreTagsSort', genreTagsSort.toString());
- $page.url.searchParams.set('mediaSort', mediaSort.toString());
- $page.url.searchParams.set('includeMovies', includeMovies.toString());
- $page.url.searchParams.set('includeOVAs', includeOVAs.toString());
-
- history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
- }
- }
- $: {
- includeMusic = includeMusic;
- includeSpecials = includeSpecials;
- includeRepeats = includeRepeats;
- disableActivityHistory = disableActivityHistory;
- highestRatedMediaPercentage = highestRatedMediaPercentage;
- highestRatedGenreTagPercentage = highestRatedGenreTagPercentage;
- topGenresTags = topGenresTags;
- genreTagsSort = genreTagsSort;
- mediaSort = mediaSort;
- includeMovies = includeMovies;
- includeOVAs = includeOVAs;
- selectedYear = selectedYear;
- includeOngoingMediaFromPreviousYears = includeOngoingMediaFromPreviousYears;
-
- update().then(updateWidth).catch(updateWidth);
- }
- $: {
- animeList = animeList;
- mangaList = mangaList;
- highestRatedCount = highestRatedCount;
-
- new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
+ interface Props {
+ user: AniListAuthorisation;
}
- $: {
- genreTagCount = genreTagCount;
-
- if (animeList && mangaList)
- topMedia = tops(
- [...(animeList || []), ...(mangaList || [])],
- genreTagCount,
- genreTagsSort,
- excludedKeywords
- );
- new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
- }
- $: {
- excludedKeywords = excludedKeywords;
+ let { user }: Props = $props();
- if (excludedKeywords.length > 0 && animeList !== undefined && mangaList !== undefined) {
- animeList = originalAnimeList;
- mangaList = originalMangaList;
- animeList = excludeKeywords(animeList as Media[]);
- mangaList = excludeKeywords(mangaList as Media[]);
- }
+ const currentYear = new Date(Date.now()).getFullYear();
+ let selectedYear = $state(new Date(Date.now()).getFullYear());
+ let episodes = $state(0);
+ let chapters = $state(0);
+ let minutesWatched = $state(0);
+ let animeList: Media[] | undefined = $state(undefined);
+ let mangaList: Media[] | undefined = $state(undefined);
+ let calculatedAnimeList: Media[] | undefined = $state(undefined);
+ let calculatedMangaList: Media[] | undefined = $state(undefined);
+ let originalAnimeList: Media[] | undefined = $state(undefined);
+ let originalMangaList: Media[] | undefined = $state(undefined);
+ let transparency = $state(false);
+ let lightTheme = $state(true);
+ let watermark = $state(false);
+ let includeMusic = $state(false);
+ let includeSpecials = $state(true);
+ let includeRepeats = $state(false);
+ let width = $state(1920);
+ let lightMode = $state(false);
+ let highestRatedCount = $state(5);
+ let genreTagCount = $state(5);
+ let mounted = $state(false);
+ let generated = $state(false);
+ let disableActivityHistory = $state(true);
+ let excludedKeywordsInput = $state('');
+ let excludedKeywords: string[] = $state([]);
+ let useFullActivityHistory = $state(false);
+ let topGenresTags = $state(true);
+ let topMedia: TopMedia = $state();
+ let highestRatedMediaPercentage = $state(true);
+ let highestRatedGenreTagPercentage = $state(true);
+ let genreTagsSort = $state(SortOptions.SCORE);
+ let mediaSort = $state(SortOptions.SCORE);
+ let includeMovies = $state(true);
+ let includeOVAs = $state(true);
+ let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL' = $state('ORIGINAL');
+ let includeOngoingMediaFromPreviousYears = $state(false);
- updateWidth();
- }
- $: genreTagTitle = (() => {
- switch (genreTagsSort) {
- case SortOptions.SCORE:
- return 'Highest Rated';
- case SortOptions.MINUTES_WATCHED:
- return 'Most Watched';
- case SortOptions.COUNT:
- return 'Most Common';
- }
- })();
- $: animeMostTitle = (() => {
- switch (mediaSort) {
- case SortOptions.SCORE:
- return 'Highest Rated';
- case SortOptions.MINUTES_WATCHED:
- return 'Most Watched';
- case SortOptions.COUNT:
- return 'Most Common';
- }
- })();
- $: mangaMostTitle = (() => {
- switch (mediaSort) {
- case SortOptions.SCORE:
- return 'Highest Rated';
- case SortOptions.MINUTES_WATCHED:
- return 'Most Read';
- case SortOptions.COUNT:
- return 'Most Common';
- }
- })();
const updateWidth = () => {
if (!browser) return;
@@ -562,6 +461,113 @@
// return mediaCover(top[Math.floor(Math.random() * top.length)].mediaIds[0]);
// };
+ run(() => {
+ includeMusic = includeMusic;
+ includeSpecials = includeSpecials;
+ includeRepeats = includeRepeats;
+ disableActivityHistory = disableActivityHistory;
+ highestRatedMediaPercentage = highestRatedMediaPercentage;
+ highestRatedGenreTagPercentage = highestRatedGenreTagPercentage;
+ topGenresTags = topGenresTags;
+ genreTagsSort = genreTagsSort;
+ mediaSort = mediaSort;
+ includeMovies = includeMovies;
+ includeOVAs = includeOVAs;
+ selectedYear = selectedYear;
+ includeOngoingMediaFromPreviousYears = includeOngoingMediaFromPreviousYears;
+
+ update().then(updateWidth).catch(updateWidth);
+ });
+ run(() => {
+ animeList = animeList;
+ mangaList = mangaList;
+ highestRatedCount = highestRatedCount;
+
+ new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
+ });
+ run(() => {
+ excludedKeywords = excludedKeywords;
+
+ if (excludedKeywords.length > 0 && animeList !== undefined && mangaList !== undefined) {
+ animeList = originalAnimeList;
+ mangaList = originalMangaList;
+ animeList = excludeKeywords(animeList as Media[]);
+ mangaList = excludeKeywords(mangaList as Media[]);
+ }
+
+ updateWidth();
+ });
+ run(() => {
+ genreTagCount = genreTagCount;
+
+ if (animeList && mangaList)
+ topMedia = tops(
+ [...(animeList || []), ...(mangaList || [])],
+ genreTagCount,
+ genreTagsSort,
+ excludedKeywords
+ );
+
+ new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
+ });
+ run(() => {
+ if (browser && mounted) {
+ $page.url.searchParams.set('transparency', transparency.toString());
+ $page.url.searchParams.set('lightTheme', lightTheme.toString());
+ $page.url.searchParams.set('watermark', watermark.toString());
+ $page.url.searchParams.set('includeMusic', includeMusic.toString());
+ $page.url.searchParams.set('includeSpecials', includeSpecials.toString());
+ $page.url.searchParams.set('includeRepeats', includeRepeats.toString());
+ $page.url.searchParams.set('lightMode', lightMode.toString());
+ $page.url.searchParams.set('highestRatedCount', highestRatedCount.toString());
+ $page.url.searchParams.set('genreTagCount', genreTagCount.toString());
+ $page.url.searchParams.set('disableActivityHistory', disableActivityHistory.toString());
+ $page.url.searchParams.set(
+ 'highestRatedMediaPercentage',
+ highestRatedMediaPercentage.toString()
+ );
+ $page.url.searchParams.set(
+ 'highestRatedGenreTagPercentage',
+ highestRatedGenreTagPercentage.toString()
+ );
+ $page.url.searchParams.set('genreTagsSort', genreTagsSort.toString());
+ $page.url.searchParams.set('mediaSort', mediaSort.toString());
+ $page.url.searchParams.set('includeMovies', includeMovies.toString());
+ $page.url.searchParams.set('includeOVAs', includeOVAs.toString());
+
+ history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
+ }
+ });
+ let genreTagTitle = $derived((() => {
+ switch (genreTagsSort) {
+ case SortOptions.SCORE:
+ return 'Highest Rated';
+ case SortOptions.MINUTES_WATCHED:
+ return 'Most Watched';
+ case SortOptions.COUNT:
+ return 'Most Common';
+ }
+ })());
+ let animeMostTitle = $derived((() => {
+ switch (mediaSort) {
+ case SortOptions.SCORE:
+ return 'Highest Rated';
+ case SortOptions.MINUTES_WATCHED:
+ return 'Most Watched';
+ case SortOptions.COUNT:
+ return 'Most Common';
+ }
+ })());
+ let mangaMostTitle = $derived((() => {
+ switch (mediaSort) {
+ case SortOptions.SCORE:
+ return 'Highest Rated';
+ case SortOptions.MINUTES_WATCHED:
+ return 'Most Read';
+ case SortOptions.COUNT:
+ return 'Most Common';
+ }
+ })());
</script>
{#if $userIdentity.id === -2 || user === undefined}
@@ -630,10 +636,10 @@
</div>
<div class="list">
<div class:card={generated}>
- <div id="wrapped-final" />
+ <div id="wrapped-final"></div>
{#if generated}
- <p />
+ <p></p>
<blockquote style="margin: 0 0 0 1.5rem;">
Click on the image to download, or right click and select "Save Image As...".
@@ -642,11 +648,11 @@
</div>
{#if generated}
- <p />
+ <p></p>
{/if}
<div id="options" class="card">
- <button on:click={screenshot} data-umami-event="Generate Wrapped">
+ <button onclick={screenshot} data-umami-event="Generate Wrapped">
Generate image
</button>
@@ -690,9 +696,9 @@
{/each}
</select>
Highest genre and tag count<br />
- <button on:click={updateWidth}>Find best fit</button>
- <button on:click={() => (width -= 25)}>-25px</button>
- <button on:click={() => (width += 25)}>+25px</button>
+ <button onclick={updateWidth}>Find best fit</button>
+ <button onclick={() => (width -= 25)}>-25px</button>
+ <button onclick={() => (width += 25)}>+25px</button>
Width adjustment<br />
</details>
@@ -700,7 +706,7 @@
<summary>Calculation</summary>
<input type="checkbox" bind:checked={useFullActivityHistory} />
- Enable full-year activity<button class="smaller-button" on:click={pruneFullYear}
+ Enable full-year activity<button class="smaller-button" onclick={pruneFullYear}
>Refresh data</button
>
<br />
@@ -732,12 +738,12 @@
<input
type="text"
bind:value={excludedKeywordsInput}
- on:keypress={(e) => {
+ onkeypress={(e) => {
e.key === 'Enter' && submitExcludedKeywords();
}}
/>
Excluded keywords
- <button on:click={submitExcludedKeywords} title="Or click your Enter key" use:tooltip
+ <button onclick={submitExcludedKeywords} title="Or click your Enter key" use:tooltip
>Submit</button
>
<br />
diff --git a/src/lib/Tools/Wrapped/Top/Activity.svelte b/src/lib/Tools/Wrapped/Top/Activity.svelte
index a91bedfb..27dea6a2 100644
--- a/src/lib/Tools/Wrapped/Top/Activity.svelte
+++ b/src/lib/Tools/Wrapped/Top/Activity.svelte
@@ -4,18 +4,28 @@
import type { Wrapped } from '$lib/Data/AniList/wrapped';
import proxy from '$lib/Utility/proxy';
- export let wrapped: Wrapped;
- export let year: number;
- export let activities: ActivityHistoryEntry[];
- export let useFullActivityHistory: boolean;
- export let updateWidth: () => void;
+ interface Props {
+ wrapped: Wrapped;
+ year: number;
+ activities: ActivityHistoryEntry[];
+ useFullActivityHistory: boolean;
+ updateWidth: () => void;
+ }
+
+ let {
+ wrapped,
+ year,
+ activities,
+ useFullActivityHistory,
+ updateWidth
+ }: Props = $props();
const currentYear = new Date(Date.now()).getFullYear();
</script>
<div class="grid-item image-grid avatar-grid category top-category">
<a href={`https://anilist.co/user/${$identity.name}`} target="_blank">
- <img src={proxy(wrapped.avatar.large)} alt="User Avatar" on:load={updateWidth} />
+ <img src={proxy(wrapped.avatar.large)} alt="User Avatar" onload={updateWidth} />
</a>
<div>
<div>
diff --git a/src/lib/Tools/Wrapped/Top/Anime.svelte b/src/lib/Tools/Wrapped/Top/Anime.svelte
index 08df7fd3..275adadf 100644
--- a/src/lib/Tools/Wrapped/Top/Anime.svelte
+++ b/src/lib/Tools/Wrapped/Top/Anime.svelte
@@ -1,9 +1,13 @@
<script lang="ts">
import type { Media } from '$lib/Data/AniList/media';
- export let minutesWatched: number;
- export let animeList: Media[] | undefined;
- export let episodes: number;
+ interface Props {
+ minutesWatched: number;
+ animeList: Media[] | undefined;
+ episodes: number;
+ }
+
+ let { minutesWatched, animeList, episodes }: Props = $props();
</script>
<div class="category-grid pure-category category top-category">
diff --git a/src/lib/Tools/Wrapped/Top/Manga.svelte b/src/lib/Tools/Wrapped/Top/Manga.svelte
index a36f7724..a49d1067 100644
--- a/src/lib/Tools/Wrapped/Top/Manga.svelte
+++ b/src/lib/Tools/Wrapped/Top/Manga.svelte
@@ -2,8 +2,12 @@
import type { Media } from '$lib/Data/AniList/media';
import { estimatedDayReading } from '$lib/Media/Manga/time';
- export let mangaList: Media[] | undefined;
- export let chapters: number;
+ interface Props {
+ mangaList: Media[] | undefined;
+ chapters: number;
+ }
+
+ let { mangaList, chapters }: Props = $props();
</script>
<div class="category-grid pure-category category top-category">
diff --git a/src/lib/Tooltip/LinkedTooltip.svelte b/src/lib/Tooltip/LinkedTooltip.svelte
index 6e468cd6..f4546d4c 100644
--- a/src/lib/Tooltip/LinkedTooltip.svelte
+++ b/src/lib/Tooltip/LinkedTooltip.svelte
@@ -2,23 +2,42 @@
import tooltipPosition from '$stores/tooltipPosition';
import { fade } from 'svelte/transition';
- export let id: string | undefined = undefined;
- export let pin: string | undefined = undefined;
- export let content: string;
- export let disable: boolean = false;
- export let pinPosition: 'top' | 'bottom' | 'left' | 'right' = 'top';
- export let offset = 10;
- export let tooltipTransitionTime = 200;
- export let tooltipHideDelay = 10;
- export let debounceDelay = 100;
- export let tooltipOpacityTransitionTime = 200;
- export let relative = false;
- export let ignoreAnchorStyling = false;
-
- let tooltipDiv: HTMLDivElement | null = null;
+ interface Props {
+ id?: string | undefined;
+ pin?: string | undefined;
+ content: string;
+ disable?: boolean;
+ pinPosition?: 'top' | 'bottom' | 'left' | 'right';
+ offset?: number;
+ tooltipTransitionTime?: number;
+ tooltipHideDelay?: number;
+ debounceDelay?: number;
+ tooltipOpacityTransitionTime?: number;
+ relative?: boolean;
+ ignoreAnchorStyling?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ id = undefined,
+ pin = undefined,
+ content,
+ disable = false,
+ pinPosition = 'top',
+ offset = 10,
+ tooltipTransitionTime = 200,
+ tooltipHideDelay = 10,
+ debounceDelay = 100,
+ tooltipOpacityTransitionTime = 200,
+ relative = false,
+ ignoreAnchorStyling = false,
+ children
+ }: Props = $props();
+
+ let tooltipDiv: HTMLDivElement | null = $state(null);
let hideTimeout: number | null = null;
let debounceTimer: number | null = null;
- let opacity = 0;
+ let opacity = $state(0);
const createTooltip = () => {
if (!tooltipDiv) {
@@ -202,12 +221,12 @@
<span
{id}
- on:mouseenter={handleMouseEnter}
- on:mousemove={handleMouseMove}
- on:mouseleave={handleMouseLeave}
+ onmouseenter={handleMouseEnter}
+ onmousemove={handleMouseMove}
+ onmouseleave={handleMouseLeave}
role="tooltip"
>
- <slot />
+ {@render children?.()}
</span>
{#if tooltipDiv}
diff --git a/src/lib/User/BadgeWall/AWC.svelte b/src/lib/User/BadgeWall/AWC.svelte
index 1cf82a1b..2bd21001 100644
--- a/src/lib/User/BadgeWall/AWC.svelte
+++ b/src/lib/User/BadgeWall/AWC.svelte
@@ -5,10 +5,19 @@
import FallbackBadge from './FallbackBadge.svelte';
import './badges.css';
- export let awcPromise: Promise<Response>;
- export let categoryFilter: string | null;
- export let isOwner: boolean;
- export let preferences: Preferences;
+ interface Props {
+ awcPromise: Promise<Response>;
+ categoryFilter: string | null;
+ isOwner: boolean;
+ preferences: Preferences;
+ }
+
+ let {
+ awcPromise,
+ categoryFilter,
+ isOwner,
+ preferences
+ }: Props = $props();
const awcBadgesGrouped = (awcResponse: string): AWCBadgesGroup[] => {
return Array.from(
@@ -72,7 +81,7 @@
{group.group}
</summary>
- <p />
+ <p></p>
<div class="badges">
{#each group.badges as badge, index}
@@ -92,7 +101,7 @@
</div>
</details>
- <p />
+ <p></p>
{/each}
{/if}
{/await}
diff --git a/src/lib/User/BadgeWall/BadgePreview.svelte b/src/lib/User/BadgeWall/BadgePreview.svelte
index 7a54cbc4..5daccdd2 100644
--- a/src/lib/User/BadgeWall/BadgePreview.svelte
+++ b/src/lib/User/BadgeWall/BadgePreview.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { thumbnail } from '$lib/Utility/image';
import type { Badge } from '$lib/Database/SB/User/badges';
import { cdn } from '$lib/Utility/image';
@@ -8,16 +10,26 @@
import root from '$lib/Utility/root';
import ParallaxImage from '$lib/Image/ParallaxImage.svelte';
- export let selectedBadge: Badge | undefined;
- export let onNext: () => void = () => {};
- export let onPrevious: () => void = () => {};
- export let hasNext: boolean;
- export let hasPrevious: boolean;
+ interface Props {
+ selectedBadge: Badge | undefined;
+ onNext?: () => void;
+ onPrevious?: () => void;
+ hasNext: boolean;
+ hasPrevious: boolean;
+ }
+
+ let {
+ selectedBadge = $bindable(),
+ onNext = () => {},
+ onPrevious = () => {},
+ hasNext,
+ hasPrevious
+ }: Props = $props();
- let source = cdn(thumbnail(selectedBadge?.image || '')) || '';
+ let source = $state(cdn(thumbnail(selectedBadge?.image || '')) || '');
let badgeReference: HTMLImageElement;
- $: {
+ run(() => {
if (selectedBadge && selectedBadge.image) {
const image = new Image();
@@ -26,14 +38,14 @@
source = image.src;
};
}
- }
+ });
- $: {
+ run(() => {
if (selectedBadge)
fetch(root(`/api/badges?incrementClickCount=${selectedBadge.id}`), {
method: 'PUT'
});
- }
+ });
onMount(() => {
badgeReference = document.querySelector('.badge-container-image') as HTMLImageElement;
@@ -105,7 +117,7 @@
<div class="badge-preview-badge">
{#if selectedBadge.image}
<div role="img" class="badge-container">
- <a href={'#'} on:click={onClick} class="badge-container-image">
+ <a href={'#'} onclick={onClick} class="badge-container-image">
<ParallaxImage
{source}
alternativeText="selectedBadge.description"
@@ -115,7 +127,7 @@
</a>
</div>
- <p />
+ <p></p>
{/if}
</div>
@@ -124,7 +136,7 @@
{$locale().dateFormatter(databaseTimeToDate(selectedBadge.time))}
{#if (selectedBadge.designer || selectedBadge.source || selectedBadge.post) && !selectedBadge.description}
- <p />
+ <p></p>
{:else if selectedBadge.description}
<br />
{/if}
@@ -133,7 +145,7 @@
{#if selectedBadge.description}
{selectedBadge.description}
- <p />
+ <p></p>
{/if}
{#if selectedBadge.designer}
@@ -185,7 +197,7 @@
<a
href={`?category=${selectedBadge.category}`}
- on:click={() => (selectedBadge = undefined)}
+ onclick={() => (selectedBadge = undefined)}
>
{selectedBadge.category}
</a>
@@ -200,11 +212,11 @@
<div class="badge-preview-seek">
{#if hasPrevious}
- <button on:click={onPrevious}>Previous</button>
+ <button onclick={onPrevious}>Previous</button>
{/if}
{#if hasNext}
- <button on:click={onNext} style="float: right;">Next</button>
+ <button onclick={onNext} style="float: right;">Next</button>
{/if}
</div>
</div>
diff --git a/src/lib/User/BadgeWall/Badges.svelte b/src/lib/User/BadgeWall/Badges.svelte
index b233d0c3..1acd4cce 100644
--- a/src/lib/User/BadgeWall/Badges.svelte
+++ b/src/lib/User/BadgeWall/Badges.svelte
@@ -9,19 +9,30 @@
import type { Preferences } from '../../../graphql/$types';
import type { IndexedBadge } from './badge';
- export let ungroupedBadges: IndexedBadge[];
- export let groupedBadges: [string, IndexedBadge[]][];
- export let categoryFilter: string | null;
- export let editMode: boolean;
- export let preferences: Preferences | undefined;
- export let selectedBadge: IndexedBadge | undefined = undefined;
+ interface Props {
+ ungroupedBadges: IndexedBadge[];
+ groupedBadges: [string, IndexedBadge[]][];
+ categoryFilter: string | null;
+ editMode: boolean;
+ preferences: Preferences | undefined;
+ selectedBadge?: IndexedBadge | undefined;
+ }
+
+ let {
+ ungroupedBadges,
+ groupedBadges,
+ categoryFilter,
+ editMode,
+ preferences,
+ selectedBadge = $bindable(undefined)
+ }: Props = $props();
</script>
{#if ungroupedBadges.length === 0}
<div class="card">
No due.moe registered badges found for this user. <a
href={'#'}
- on:click={(e) => e.preventDefault()}
+ onclick={(e) => e.preventDefault()}
title="This alert does not include AWC badges."
use:tooltip>?</a
>
@@ -36,7 +47,7 @@
<details open={categoryFilter ? categoryFilter === category : true}>
<summary>{category}</summary>
- <p />
+ <p></p>
<div class="badges">
{#each badges as badge}
@@ -54,7 +65,7 @@
>
<a
href={`#`}
- on:click={() => {
+ onclick={() => {
selectedBadge = badge;
const hiddenInput = document.querySelector('input[name="hidden"]');
@@ -93,6 +104,6 @@
</details>
{#if groupedBadges[groupedBadges.length - 1][0] !== category}
- <p />
+ <p></p>
{/if}
{/each}
diff --git a/src/lib/User/BadgeWall/FallbackBadge.svelte b/src/lib/User/BadgeWall/FallbackBadge.svelte
index 35a50a7d..68667031 100644
--- a/src/lib/User/BadgeWall/FallbackBadge.svelte
+++ b/src/lib/User/BadgeWall/FallbackBadge.svelte
@@ -9,22 +9,40 @@
import { dev } from '$app/environment';
import type { Preferences } from '../../../graphql/$types';
- export let source: string | null | undefined;
- export let alternative: string | null | undefined;
- export let fallback: string | null | undefined;
- export let maxReplaceCount = 1;
- export let replaceDelay = 1000;
- export let error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg';
- export let hideOnError = false;
- export let badge: Badge;
- export let style = '';
- export let selectedBadge: Badge | null = null;
- export let awc = false;
- export let index: number | null = null;
- export let preferences: Preferences | undefined;
-
- let replaceCount = 0;
- let badgeReference: HTMLImageElement;
+ interface Props {
+ source: string | null | undefined;
+ alternative: string | null | undefined;
+ fallback: string | null | undefined;
+ maxReplaceCount?: number;
+ replaceDelay?: number;
+ error?: string;
+ hideOnError?: boolean;
+ badge: Badge;
+ style?: string;
+ selectedBadge?: Badge | null;
+ awc?: boolean;
+ index?: number | null;
+ preferences: Preferences | undefined;
+ }
+
+ let {
+ source,
+ alternative,
+ fallback,
+ maxReplaceCount = 1,
+ replaceDelay = 1000,
+ error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg',
+ hideOnError = false,
+ badge,
+ style = '',
+ selectedBadge = $bindable(null),
+ awc = false,
+ index = null,
+ preferences
+ }: Props = $props();
+
+ let replaceCount = $state(0);
+ let badgeReference: HTMLImageElement = $state();
const mouse = tweened(
{ x: 0, y: 0 },
{
@@ -82,9 +100,9 @@
href={awc ? badgeToAny(badge).link : '#'}
target="_blank"
class="badge-container badge"
- on:mousemove={handleMouseMove}
- on:mouseleave={handleMouseLeave}
- on:click={(e) => {
+ onmousemove={handleMouseMove}
+ onmouseleave={handleMouseLeave}
+ onclick={(e) => {
if (!awc) {
e.preventDefault();
@@ -100,7 +118,7 @@
bind:this={badgeReference}
style="transform: perspective(1000px) rotateX({$mouse.y / 10}deg) rotateY({-$mouse.x /
10}deg); ${style}"
- on:error={(e) => delayedReplace(e, fallback)}
+ onerror={(e) => delayedReplace(e, fallback)}
/>
</a>
</Tooltip>
diff --git a/src/lib/Utility/Loading.svelte b/src/lib/Utility/Loading.svelte
index 92cbc1ac..3d1eeec6 100644
--- a/src/lib/Utility/Loading.svelte
+++ b/src/lib/Utility/Loading.svelte
@@ -1,13 +1,23 @@
<script lang="ts">
- export let type: string | undefined = undefined;
- export let percent: number | undefined = undefined;
- export let card = true;
+ interface Props {
+ type?: string | undefined;
+ percent?: number | undefined;
+ card?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ type = undefined,
+ percent = undefined,
+ card = true,
+ children
+ }: Props = $props();
</script>
<div class:card>
{#if type}
Loading {type} ...{percent ? ` ${percent}%` : ''}
{:else}
- <slot />
+ {@render children?.()}
{/if}
</div>
diff --git a/src/lib/Utility/oauth.ts b/src/lib/Utility/oauth.ts
index 78f52bfa..bd824b75 100644
--- a/src/lib/Utility/oauth.ts
+++ b/src/lib/Utility/oauth.ts
@@ -47,5 +47,5 @@ export const callback = async (options: CallbackOptions) => {
}
);
- throw redirect(303, options.redirect ?? '/');
+ redirect(303, options.redirect ?? '/');
};