Chore: Restructure Repository for SvelteKit

This commit is contained in:
Youwes09
2026-05-22 04:04:59 -05:00
parent bf071dcfc7
commit 8cef74bb98
266 changed files with 5093 additions and 396 deletions
+10
View File
@@ -0,0 +1,10 @@
export type AppStatus = 'booting' | 'auth' | 'ready' | 'error'
export const appState = $state({
status: 'booting' as AppStatus,
error: null as string | null,
serverUrl: '',
authenticated: false,
platform: 'web' as 'web' | 'tauri' | 'capacitor',
version: '',
})
+16
View File
@@ -0,0 +1,16 @@
import type { DownloadItem } from '$lib/server-adapters/types'
export const downloadsState = $state({
items: [] as DownloadItem[],
error: null as string | null,
})
export const activeDownloads = $derived(
downloadsState.items.filter(d => d.state === 'downloading')
)
export const queuedDownloads = $derived(
downloadsState.items.filter(d => d.state === 'queued')
)
export const downloadCount = $derived(downloadsState.items.length)
+36
View File
@@ -0,0 +1,36 @@
import type { Extension, Source, Manga } from '$lib/types'
export const extensionsState = $state({
items: [] as Extension[],
sources: [] as Source[],
loading: false,
error: null as string | null,
filter: {
query: '',
installed: false,
language: 'all',
},
browseResults: [] as Manga[],
browseLoading: false,
browseError: null as string | null,
browseHasMore: false,
})
export const filteredExtensions = $derived.by(() => {
let result = extensionsState.items
if (extensionsState.filter.installed) {
result = result.filter(e => e.installed)
}
if (extensionsState.filter.language !== 'all') {
result = result.filter(e => e.lang === extensionsState.filter.language)
}
if (extensionsState.filter.query) {
const q = extensionsState.filter.query.toLowerCase()
result = result.filter(e => e.name.toLowerCase().includes(q))
}
return result
})
+53
View File
@@ -0,0 +1,53 @@
import type { Manga } from '$lib/types'
import type { MangaStatus } from '$lib/server-adapters/types'
export type LibrarySortOption = 'alphabetical' | 'unread' | 'lastRead' | 'dateAdded'
export const libraryState = $state({
items: [] as Manga[],
searchResults: [] as Manga[],
loading: false,
error: null as string | null,
filter: {
status: 'all' as MangaStatus | 'all',
tags: [] as string[],
unread: false,
query: '',
},
sort: 'alphabetical' as LibrarySortOption,
sortDesc: false,
view: 'grid' as 'grid' | 'list',
selected: new Set<string>(),
})
export const filteredItems = $derived.by(() => {
let result = libraryState.items
if (libraryState.filter.unread) {
result = result.filter(m => m.unreadCount > 0)
}
if (libraryState.filter.status !== 'all') {
result = result.filter(m => m.status === libraryState.filter.status)
}
if (libraryState.filter.tags.length > 0) {
result = result.filter(m =>
libraryState.filter.tags.every(tag => m.tags?.includes(tag))
)
}
if (libraryState.filter.query) {
const q = libraryState.filter.query.toLowerCase()
result = result.filter(m => m.title.toLowerCase().includes(q))
}
const sorted = [...result].sort((a, b) => {
switch (libraryState.sort) {
case 'unread': return (b.unreadCount ?? 0) - (a.unreadCount ?? 0)
case 'lastRead': return (b.lastReadAt ?? 0) - (a.lastReadAt ?? 0)
case 'dateAdded': return (b.addedAt ?? 0) - (a.addedAt ?? 0)
case 'alphabetical':
default: return a.title.localeCompare(b.title)
}
})
return libraryState.sortDesc ? sorted.reverse() : sorted
})
+25
View File
@@ -0,0 +1,25 @@
export type ToastKind = 'info' | 'success' | 'error' | 'download'
export interface Toast {
id: string
kind: ToastKind
message: string
detail?: string
duration?: number
}
export const notificationsState = $state({
toasts: [] as Toast[],
})
export function toast(kind: ToastKind, message: string, detail?: string, duration = 4000) {
const id = crypto.randomUUID()
notificationsState.toasts.push({ id, kind, message, detail, duration })
if (duration > 0) {
setTimeout(() => dismissToast(id), duration)
}
}
export function dismissToast(id: string) {
notificationsState.toasts = notificationsState.toasts.filter(t => t.id !== id)
}
+41
View File
@@ -0,0 +1,41 @@
import type { Manga, Chapter } from '$lib/types'
import type { Page } from '$lib/server-adapters/types'
export type ReadMode = 'single' | 'strip'
export type FitMode = 'width' | 'height' | 'original'
export type ReadDirection = 'ltr' | 'rtl'
export const readerState = $state({
manga: null as Manga | null,
chapter: null as Chapter | null,
chapters: [] as Chapter[],
pages: [] as Page[],
pagesLoading: false,
pagesError: null as string | null,
currentPage: 0,
mode: 'single' as ReadMode,
fit: 'width' as FitMode,
direction: 'ltr' as ReadDirection,
zoom: 1,
showControls: false,
showSettings: false,
fullscreen: false,
})
export const currentPageData = $derived(
readerState.pages[readerState.currentPage] ?? null
)
export const progress = $derived(
readerState.pages.length > 0
? (readerState.currentPage + 1) / readerState.pages.length
: 0
)
export const hasPrev = $derived(readerState.currentPage > 0)
export const hasNext = $derived(
readerState.currentPage < readerState.pages.length - 1
)
+36
View File
@@ -0,0 +1,36 @@
import type { Manga, Chapter } from '$lib/types'
export const seriesState = $state({
current: null as Manga | null,
loading: false,
error: null as string | null,
chapters: [] as Chapter[],
chaptersLoading: false,
chaptersError: null as string | null,
chapterFilter: {
unread: false,
downloaded: false,
query: '',
},
chapterSortDesc: true,
})
export const filteredChapters = $derived.by(() => {
let result = seriesState.chapters
if (seriesState.chapterFilter.unread) {
result = result.filter(c => !c.read)
}
if (seriesState.chapterFilter.downloaded) {
result = result.filter(c => c.downloaded)
}
if (seriesState.chapterFilter.query) {
const q = seriesState.chapterFilter.query.toLowerCase()
result = result.filter(c => c.name.toLowerCase().includes(q))
}
const sorted = [...result].sort((a, b) => a.chapterNumber - b.chapterNumber)
return seriesState.chapterSortDesc ? sorted.reverse() : sorted
})
+8
View File
@@ -0,0 +1,8 @@
import type { Tracker } from '$lib/types'
export const trackingState = $state({
trackers: [] as Tracker[],
loading: false,
error: null as string | null,
syncing: false,
})