mirror of
https://github.com/danbulant/Mangades
synced 2026-05-24 12:22:10 +00:00
some small tweaks
This commit is contained in:
parent
dce011fcd3
commit
173d53b261
16 changed files with 365 additions and 194 deletions
2
.nvmrc
2
.nvmrc
|
|
@ -1 +1 @@
|
||||||
17
|
24
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
# Mangades
|
# Mangades
|
||||||
|
|
||||||
Mangadex viewer and downloader. Uses svelte+routify.
|
Mangadex viewer and downloader website, with read-only anilist integration.
|
||||||
|
|
||||||
Minimalistic.
|
See it in action [here](https://manga.danbulant.eu).
|
||||||
|
|
||||||
|
|
||||||
See it live [here at manga.danbulant.eu](https://manga.danbulant.eu).
|
|
||||||
|
|
@ -34,5 +34,6 @@
|
||||||
"svelte-local-storage-store": "^0.3.1",
|
"svelte-local-storage-store": "^0.3.1",
|
||||||
"svelte-markdown": "^0.2.3",
|
"svelte-markdown": "^0.2.3",
|
||||||
"swiper": "^8.3.2"
|
"swiper": "^8.3.2"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@9.5.0+sha1.8c155dc114e1689d18937974f6571e0ceee66f1d"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
shell.nix
Normal file
8
shell.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
nodejs-slim
|
||||||
|
corepack
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,23 +1,29 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "$app/navigation";
|
import { goto, preloadData } from "$app/navigation";
|
||||||
import { flip } from "svelte/animate";
|
import { flip } from "svelte/animate";
|
||||||
import { blur } from "svelte/transition";
|
import { blur, crossfade } from "svelte/transition";
|
||||||
import request from "../util/request";
|
import request, { imageproxy } from "../util/request";
|
||||||
import Item from "./item.svelte";
|
import Item from "./item.svelte";
|
||||||
import { showType } from "./showTypeChooser.svelte";
|
import { showType } from "./showTypeChooser.svelte";
|
||||||
|
import { quintOut } from "svelte/easing";
|
||||||
|
import { sleep } from "$lib/util/ratelimit";
|
||||||
|
import { logs } from "$lib/util/logs";
|
||||||
|
|
||||||
export var lists;
|
export var lists;
|
||||||
|
|
||||||
var isLoading = false;
|
let isLoading = false;
|
||||||
|
let selectedEntry;
|
||||||
async function find(entry) {
|
async function find(entry) {
|
||||||
|
selectedEntry = entry;
|
||||||
|
isLoading = true;
|
||||||
if(typeof localStorage !== "undefined") {
|
if(typeof localStorage !== "undefined") {
|
||||||
let cache = localStorage.getItem("anilist-mangadex-" + entry.media.id);
|
let cache = localStorage.getItem("anilist-mangadex-" + entry.media.id);
|
||||||
if(cache) {
|
if(cache) {
|
||||||
goto("./" + cache);
|
setTimeout(() => goto("./" + cache), 300);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isLoading = true;
|
let sleeping = sleep(350);
|
||||||
var query = new URLSearchParams();
|
var query = new URLSearchParams();
|
||||||
query.set("title", entry.media.title.romaji);
|
query.set("title", entry.media.title.romaji);
|
||||||
query.append("contentRating[]", "safe");
|
query.append("contentRating[]", "safe");
|
||||||
|
|
@ -32,8 +38,12 @@
|
||||||
result = await request("manga", query);
|
result = await request("manga", query);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
isLoading = false;
|
// $logs.push({ type: 'error', text: "Failed to search mangadex." });
|
||||||
|
// $logs = $logs;
|
||||||
|
// await sleeping;
|
||||||
|
// isLoading = false;
|
||||||
alert("Failed to search mangadex.");
|
alert("Failed to search mangadex.");
|
||||||
|
location.reload()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("anilist mangadex data", result.data);
|
console.log("anilist mangadex data", result.data);
|
||||||
|
|
@ -45,28 +55,76 @@
|
||||||
if(!item) item = result.data.find(t => t.attributes.altTitles.find(t => Object.values(t).find(t => Object.values(entry.media.title).filter(t => t).map(t => t.toLowerCase()).includes(t.toLowerCase()))));
|
if(!item) item = result.data.find(t => t.attributes.altTitles.find(t => Object.values(t).find(t => Object.values(entry.media.title).filter(t => t).map(t => t.toLowerCase()).includes(t.toLowerCase()))));
|
||||||
console.log("anilist mangadex item", item);
|
console.log("anilist mangadex item", item);
|
||||||
if(!item) {
|
if(!item) {
|
||||||
alert(`Couldn't find any mangadex entry.`);
|
// $logs.push({ type: 'error', text: `Couldn't find any mangadex entry.` });
|
||||||
isLoading = false;
|
// $logs = $logs;
|
||||||
|
// await sleeping;
|
||||||
|
// isLoading = false;
|
||||||
|
alert("Couldn't find any mangadex entry.");
|
||||||
|
location.reload()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(typeof localStorage !== "undefined") {
|
if(typeof localStorage !== "undefined") {
|
||||||
localStorage.setItem("anilist-mangadex-" + entry.media.id, item.id);
|
localStorage.setItem("anilist-mangadex-" + entry.media.id, item.id);
|
||||||
}
|
}
|
||||||
goto("./" + item.id);
|
try {
|
||||||
|
await preloadData('./' + item.id);
|
||||||
|
await sleeping;
|
||||||
|
await goto("./" + item.id);
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e)
|
||||||
|
alert("Failed to open, please try again later.");
|
||||||
}
|
}
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: console.log(selectedEntry)
|
||||||
|
|
||||||
|
const [send, receive] = crossfade({
|
||||||
|
duration: 300,
|
||||||
|
easing: quintOut
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<dialog open>
|
<dialog open>
|
||||||
Finding the manga
|
{#if selectedEntry.media.bannerImage}
|
||||||
</dialog>
|
<div class="banner-container">
|
||||||
|
<img class="banner" src={selectedEntry.media.bannerImage} alt="">
|
||||||
|
<div class="fader"></div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="items" class:list={$showType == "list"}>
|
<div class="infoflex">
|
||||||
|
{#if selectedEntry.media.coverImage.large}
|
||||||
|
<div class="cover-container" in:send={{ key: selectedEntry.media.id }}>
|
||||||
|
<img class="cover" class:r18={selectedEntry.media.isAdult} draggable="false" src="{selectedEntry.media.coverImage.large}" alt="" >
|
||||||
|
<img class="cover-backdrop" draggable="false" src="{selectedEntry.media.coverImage.large}" alt="">
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="info">
|
||||||
|
<h1>{selectedEntry.media.title.userPreferred}</h1>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
{#if selectedEntry.media.startDate?.year}
|
||||||
|
{selectedEntry.media.startDate.year} ·
|
||||||
|
{/if}
|
||||||
|
{#if selectedEntry?.status} {selectedEntry.status} · {/if}
|
||||||
|
{selectedEntry.media.isAdult ? 'adult' : 'safe/suggestive'}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="loading">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
{:else}
|
||||||
|
<!-- This has to be in `if` to trigger the out transition -->
|
||||||
|
<div class="items" class:list={$showType === "list"}>
|
||||||
{#each lists as list}
|
{#each lists as list}
|
||||||
<h2>{list.name}</h2>
|
<h2>{list.name}</h2>
|
||||||
{#each list.entries.sort((a, b) => a.priority - b.priority) as entry (entry.media.id)}
|
{#each list.entries.sort((a, b) => a.priority - b.priority) as entry (entry.media.id)}
|
||||||
<div class="h-full" animate:flip transition:blur>
|
<div class="h-full" animate:flip in:blur out:receive={{ key: entry.media.id }}>
|
||||||
<Item
|
<Item
|
||||||
r18={entry.media.isAdult}
|
r18={entry.media.isAdult}
|
||||||
cover={entry.media.coverImage.large}
|
cover={entry.media.coverImage.large}
|
||||||
|
|
@ -75,13 +133,14 @@
|
||||||
chapterProgress={entry.progress}
|
chapterProgress={entry.progress}
|
||||||
score={entry.score || "?"}
|
score={entry.score || "?"}
|
||||||
description={entry.notes}
|
description={entry.notes}
|
||||||
coverColor={entry.media.coverImage.color == "null" ? null : entry.media.coverImage.color}
|
coverColor={entry.media.coverImage.color === "null" ? null : entry.media.coverImage.color}
|
||||||
on:click={() => find(entry)}
|
on:click={() => find(entry)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
dialog {
|
dialog {
|
||||||
|
|
@ -93,6 +152,80 @@
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background: black;
|
background: black;
|
||||||
color: white;
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.infoflex {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: calc(5rem + 15px) calc(1rem + 15px);
|
||||||
|
justify-content: start;
|
||||||
|
gap: 1rem;
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.cover.r18 {
|
||||||
|
filter: blur(15px);
|
||||||
|
transition: filter .3s;
|
||||||
|
}
|
||||||
|
.cover.r18:hover {
|
||||||
|
filter: blur(0);
|
||||||
|
}
|
||||||
|
.banner-container {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.banner {
|
||||||
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center top;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.banner-container .fader {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
background: linear-gradient(180deg, rgba(0,0,0,0.1) 0%, rgba(0,0,0,1) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-container {
|
||||||
|
position: relative;
|
||||||
|
height: 20rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 15px;
|
||||||
|
transition: height .3s;
|
||||||
|
}
|
||||||
|
.cover {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 10px;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.cover-backdrop {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 4px);
|
||||||
|
height: calc(100% + 4px);
|
||||||
|
z-index: 0;
|
||||||
|
filter: blur(18px) saturate(100%);
|
||||||
|
transition: filter .3s;
|
||||||
|
}
|
||||||
|
.cover-container:hover .cover-backdrop {
|
||||||
|
filter: blur(30px) saturate(150%);
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin: 0 1rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { apm } from "./tracing";
|
// import { apm } from "./tracing";
|
||||||
|
|
||||||
var isLogedInCache: boolean | null = null;
|
var isLogedInCache: boolean | null = null;
|
||||||
var isLogedInCacheTime: number | null = null;
|
var isLogedInCacheTime: number | null = null;
|
||||||
|
|
@ -21,7 +21,7 @@ export function isLogedIn() {
|
||||||
export function getUserID() {
|
export function getUserID() {
|
||||||
const token = localStorage.getItem("token")!;
|
const token = localStorage.getItem("token")!;
|
||||||
let data = JSON.parse(atob(token.substring(token.indexOf(".") + 1, token.lastIndexOf("."))));
|
let data = JSON.parse(atob(token.substring(token.indexOf(".") + 1, token.lastIndexOf("."))));
|
||||||
apm.setUserContext({ id: data.sub });
|
// apm.setUserContext({ id: data.sub });
|
||||||
return data.sub;
|
return data.sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,6 +104,10 @@ export function getUserManga() {
|
||||||
status
|
status
|
||||||
chapters
|
chapters
|
||||||
volumes
|
volumes
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
}
|
||||||
|
bannerImage
|
||||||
coverImage {
|
coverImage {
|
||||||
large
|
large
|
||||||
medium
|
medium
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import { writable } from "svelte/store";
|
|
||||||
|
|
||||||
export const logs = writable([]);
|
|
||||||
3
src/lib/util/logs.ts
Normal file
3
src/lib/util/logs.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
export const logs = writable<{ type: string, text: string }[]>([]);
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
const ratelimits = new Map();
|
const ratelimits = new Map();
|
||||||
|
|
||||||
|
export function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
function callback({ func }) {
|
function callback({ func }) {
|
||||||
const params = ratelimits.get(func);
|
const params = ratelimits.get(func);
|
||||||
console.log("ratelimit", params, func, ratelimits);
|
console.log("ratelimit", params, func, ratelimits);
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
import { browser } from '$app/environment';
|
// import { browser } from '$app/environment';
|
||||||
import { ApmBase, init as initApm } from '@elastic/apm-rum'
|
// import { ApmBase, init as initApm } from '@elastic/apm-rum'
|
||||||
|
|
||||||
var apm_: ApmBase;
|
// var apm_: ApmBase;
|
||||||
|
//
|
||||||
if(browser) {
|
// if(browser) {
|
||||||
apm_ = initApm({
|
// apm_ = initApm({
|
||||||
// Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)
|
// // Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)
|
||||||
serviceName: 'mangades',
|
// serviceName: 'mangades',
|
||||||
|
//
|
||||||
// Set custom APM Server URL (default: http://localhost:8200)
|
// // Set custom APM Server URL (default: http://localhost:8200)
|
||||||
serverUrl: 'https://apm.elasticsearch.danbulant.cloud',
|
// serverUrl: 'https://apm.elasticsearch.danbulant.cloud',
|
||||||
|
//
|
||||||
// Set the service version (required for source map feature)
|
// // Set the service version (required for source map feature)
|
||||||
// serviceVersion: import.meta.env.VITE_SENTRY_RELEASE,
|
// // serviceVersion: import.meta.env.VITE_SENTRY_RELEASE,
|
||||||
|
//
|
||||||
// Set the service environment
|
// // Set the service environment
|
||||||
environment: import.meta.env.VITE_SENTRY_ENVIRONMENT || 'production'
|
// environment: import.meta.env.VITE_SENTRY_ENVIRONMENT || 'production'
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
export const apm = apm_;
|
// export const apm = apm_;
|
||||||
|
|
@ -2,14 +2,8 @@
|
||||||
import { afterNavigate } from "$app/navigation";
|
import { afterNavigate } from "$app/navigation";
|
||||||
import { logs } from "$lib/util/logs";
|
import { logs } from "$lib/util/logs";
|
||||||
import PageTransition from "./pageTransition.svelte";
|
import PageTransition from "./pageTransition.svelte";
|
||||||
import { browser } from '$app/environment';
|
|
||||||
import { apm } from "$lib/util/tracing";
|
|
||||||
import { page } from "$app/stores";
|
|
||||||
|
|
||||||
export var data;
|
export var data;
|
||||||
if(browser) {
|
|
||||||
apm.setInitialPageLoadName($page.route.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let last = typeof window !== "undefined" && window.location.pathname;
|
let last = typeof window !== "undefined" && window.location.pathname;
|
||||||
afterNavigate(page => {
|
afterNavigate(page => {
|
||||||
|
|
@ -39,10 +33,23 @@
|
||||||
} else {
|
} else {
|
||||||
document.body.classList.remove("dark");
|
document.body.classList.remove("dark");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let inc = 0;
|
||||||
|
let lastUrl = data.url;
|
||||||
|
function increase() {
|
||||||
|
if(lastUrl == data.url) return;
|
||||||
|
let pagesToTransitionTo = ['/', '/about', '/callback', '/random']
|
||||||
|
if(pagesToTransitionTo.includes(data.url) || pagesToTransitionTo.find(t => data.url.startsWith(t + '?'))) {
|
||||||
|
inc++
|
||||||
|
}
|
||||||
|
lastUrl = data.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: increase(data.url)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class:dark={darkmode} class="main">
|
<div class:dark={darkmode} class="main">
|
||||||
<PageTransition url={data.url}>
|
<PageTransition {inc}>
|
||||||
<slot />
|
<slot />
|
||||||
</PageTransition>
|
</PageTransition>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -91,7 +98,8 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: white;
|
background: black;
|
||||||
|
color: white;
|
||||||
border-radius: 5px 0 0 0;
|
border-radius: 5px 0 0 0;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
box-shadow: 0 0 2px 0 black;
|
box-shadow: 0 0 2px 0 black;
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,20 @@
|
||||||
import ratelimit from '$lib/util/ratelimit';
|
import ratelimit from '$lib/util/ratelimit';
|
||||||
import MangadexItems from '$lib/components/mangadexItems.svelte';
|
import MangadexItems from '$lib/components/mangadexItems.svelte';
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
|
|
||||||
import type { load } from "./+page";
|
|
||||||
import Navbar from "$lib/components/navbar.svelte";
|
import Navbar from "$lib/components/navbar.svelte";
|
||||||
import ShowNsfwChooser, { showNsfw } from "$lib/components/showNsfwChooser.svelte";
|
import ShowNsfwChooser, { showNsfw } from "$lib/components/showNsfwChooser.svelte";
|
||||||
|
|
||||||
|
import type { load } from "./+page";
|
||||||
export var data: Awaited<ReturnType<typeof load>>;
|
export var data: Awaited<ReturnType<typeof load>>;
|
||||||
|
|
||||||
var name: string = typeof window === "undefined" ? "" : data.url.searchParams.get("search") || "";
|
var name: string = typeof window === "undefined" ? "" : data.url.searchParams.get("search") || "";
|
||||||
$: if(typeof window !== "undefined") {
|
$: if(typeof window !== "undefined") {
|
||||||
const url = new URL(window.location.toString());
|
const url = new URL(window.location.toString());
|
||||||
|
if(name) {
|
||||||
url.searchParams.set("search", name || "");
|
url.searchParams.set("search", name || "");
|
||||||
|
} else {
|
||||||
|
url.searchParams.delete("search");
|
||||||
|
}
|
||||||
history.replaceState(history.state, "", url.toString());
|
history.replaceState(history.state, "", url.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +135,7 @@
|
||||||
<div class="nsfw">
|
<div class="nsfw">
|
||||||
<ShowNsfwChooser />
|
<ShowNsfwChooser />
|
||||||
</div>
|
</div>
|
||||||
<a href="https://discord.gg/XKPbz5xRuK">Made by TechmandanCZ#3372</a>
|
<span>Made by <a href="https://discord.gg/XKPbz5xRuK">techmandancz</a></span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="/random" class="button" style="width: 100%; margin-bottom: 0.4rem; display: inline-block;">Random</a>
|
<a href="/random" class="button" style="width: 100%; margin-bottom: 0.4rem; display: inline-block;">Random</a>
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,7 @@
|
||||||
var smallScreenMode = width < 700;
|
var smallScreenMode = width < 700;
|
||||||
$: smallScreenMode = width < 700;
|
$: smallScreenMode = width < 700;
|
||||||
|
|
||||||
var scrollY, innerHeight;
|
var scrollY = 0, innerHeight = 1;
|
||||||
|
|
||||||
let additionalImages = [];
|
let additionalImages = [];
|
||||||
let alReadProgress
|
let alReadProgress
|
||||||
|
|
@ -363,6 +363,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</ArtDialog>
|
</ArtDialog>
|
||||||
|
|
||||||
|
|
||||||
|
<main class:smallScreenMode>
|
||||||
|
<div class="header">
|
||||||
{#if anilistData} {#await anilistData then data}
|
{#if anilistData} {#await anilistData then data}
|
||||||
{#if data && data.bannerImage}
|
{#if data && data.bannerImage}
|
||||||
<div class="banner-container">
|
<div class="banner-container">
|
||||||
|
|
@ -372,7 +375,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/await} {/if}
|
{/await} {/if}
|
||||||
|
|
||||||
<main class:smallScreenMode>
|
|
||||||
<div class="flex infoflex">
|
<div class="flex infoflex">
|
||||||
{#if relationships.find(t => t.type === "cover_art")}
|
{#if relationships.find(t => t.type === "cover_art")}
|
||||||
<div class="cover-container">
|
<div class="cover-container">
|
||||||
|
|
@ -455,6 +457,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Tabs list={tabs} bind:selected={selectedTab} />
|
<Tabs list={tabs} bind:selected={selectedTab} />
|
||||||
|
|
||||||
|
|
@ -546,40 +549,40 @@
|
||||||
<h4>Links</h4>
|
<h4>Links</h4>
|
||||||
|
|
||||||
{#if manga.links.al}
|
{#if manga.links.al}
|
||||||
<a href="https://anilist.co/manga/{manga.links.al}"><Favicon url="https://anilist.co" /> Anilist</a> <br>
|
<a target="_blank" href="https://anilist.co/manga/{manga.links.al}"><Favicon url="https://anilist.co" /> Anilist</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.ap}
|
{#if manga.links.ap}
|
||||||
<a href="https://www.anime-planet.com/manga/{manga.links.ap}"><Favicon url="https://anime-planet.com" /> Animeplanet</a> <br>
|
<a target="_blank" href="https://www.anime-planet.com/manga/{manga.links.ap}"><Favicon url="https://anime-planet.com" /> Animeplanet</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.bw}
|
{#if manga.links.bw}
|
||||||
<a href="https://bookwalker.jp/{manga.links.bw}"><Favicon url="https://bookwalker.jp" /> Bookwalker</a> <br>
|
<a target="_blank" href="https://bookwalker.jp/{manga.links.bw}"><Favicon url="https://bookwalker.jp" /> Bookwalker</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.mu}
|
{#if manga.links.mu}
|
||||||
<a href="https://www.mangaupdates.com/series.html?id={manga.links.mu}"><Favicon url="https://www.mangaupdates.com" /> Manga updates</a> <br>
|
<a target="_blank" href="https://www.mangaupdates.com/series.html?id={manga.links.mu}"><Favicon url="https://www.mangaupdates.com" /> Manga updates</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.nu}
|
{#if manga.links.nu}
|
||||||
<a href="https://www.novelupdates.com/series/{manga.links.nu}"><Favicon url="https://www.novelupdates.com" /> Novel updates</a> <br>
|
<a target="_blank" href="https://www.novelupdates.com/series/{manga.links.nu}"><Favicon url="https://www.novelupdates.com" /> Novel updates</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.amz}
|
{#if manga.links.amz}
|
||||||
<a href={manga.links.amz}><Favicon url={manga.links.amz} /> Amazon</a> <br>
|
<a target="_blank" href={manga.links.amz}><Favicon url={manga.links.amz} /> Amazon</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.ebj}
|
{#if manga.links.ebj}
|
||||||
<a href={manga.links.ebj}><Favicon url={manga.links.ebj} /> Ebookjapan</a> <br>
|
<a target="_blank" href={manga.links.ebj}><Favicon url={manga.links.ebj} /> Ebookjapan</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.mal}
|
{#if manga.links.mal}
|
||||||
<a href="https://myanimelist.net/manga/{manga.links.mal}"><Favicon url="https://myanimelist.net" /> MyAnimeList</a> <br>
|
<a target="_blank" href="https://myanimelist.net/manga/{manga.links.mal}"><Favicon url="https://myanimelist.net" /> MyAnimeList</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.cdj}
|
{#if manga.links.cdj}
|
||||||
<a href="{manga.links.cdj}"><Favicon url={manga.links.cdj} /> CDJapan</a> <br>
|
<a target="_blank" href="{manga.links.cdj}"><Favicon url={manga.links.cdj} /> CDJapan</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.raw}
|
{#if manga.links.raw}
|
||||||
<a href="{manga.links.raw}"><Favicon url={manga.links.raw} /> RAW</a> <br>
|
<a target="_blank" href="{manga.links.raw}"><Favicon url={manga.links.raw} /> RAW</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
{#if manga.links.engtl}
|
{#if manga.links.engtl}
|
||||||
<a href="{manga.links.engtl}"><Favicon url={manga.links.engtl} /> engtl</a> <br>
|
<a target="_blank" href="{manga.links.engtl}"><Favicon url={manga.links.engtl} /> engtl</a> <br>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<a href="https://mangadex.org/title/{mangaId}"><Favicon url="https://mangadex.org"/> Mangadex.org</a>
|
<a target="_blank" href="https://mangadex.org/title/{mangaId}"><Favicon url="https://mangadex.org"/> Mangadex.org</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -613,17 +616,22 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.header {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
.art-list {
|
.art-list {
|
||||||
margin-top: 2rem;
|
margin: 1rem 2rem 1rem 1rem;
|
||||||
}
|
}
|
||||||
.chapter-list {
|
.chapter-list {
|
||||||
margin-top: 1rem;
|
margin: 1rem;
|
||||||
}
|
}
|
||||||
.more-info {
|
.more-info {
|
||||||
margin-top: 2rem;
|
margin: 1rem 2rem 1rem 1rem;
|
||||||
}
|
}
|
||||||
.characters {
|
.characters {
|
||||||
margin-top: 1rem;
|
margin: 1rem;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
grid-gap: 2rem;
|
grid-gap: 2rem;
|
||||||
|
|
@ -669,6 +677,9 @@
|
||||||
.tags {
|
.tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
margin: 0 1rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.tags.expanded {
|
.tags.expanded {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
@ -688,6 +699,8 @@
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.flex-wrapped {
|
.flex-wrapped {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -705,15 +718,17 @@
|
||||||
filter: blur(0);
|
filter: blur(0);
|
||||||
}
|
}
|
||||||
.banner-container {
|
.banner-container {
|
||||||
|
margin-top: -5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 40vh;
|
max-height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
.banner {
|
.banner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 40vh;
|
max-height: 100%;
|
||||||
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center top;
|
object-position: center top;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
@ -772,9 +787,6 @@
|
||||||
.block {
|
.block {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
|
|
@ -835,11 +847,10 @@
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
padding-bottom: 1rem;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
/* background: linear-gradient(to bottom, rgba(0,0,0,0.3) 0vh, rgba(0,0,0,1) 30vh); */
|
|
||||||
padding-top: 5rem;
|
padding: 0 0 1rem 0;
|
||||||
}
|
}
|
||||||
.no-wrap {
|
.no-wrap {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<Navbar title="About" />
|
<Navbar title="About" />
|
||||||
|
|
||||||
<main style="padding-top: 6rem;">
|
<main style="padding-top: 6rem;">
|
||||||
<a href="https://discord.gg/XKPbz5xRuK">Made by TechmandanCZ#3372</a>
|
Made by <a href="https://discord.gg/XKPbz5xRuK">techmandancz</a>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|
@ -34,6 +34,4 @@
|
||||||
<p>DISCLAIMER: This site isn't distributing any content and is using mangadex.org API. All of the site's requests are done client side, my servers aren't sharing any manga data. Website is open source, and I don't claim any copyright on the publications. <i>If you believe in good faith you're downloading copyrighted content, file a DMCA at yourself, your operating system (as it took a part in the download), your ISP, your browser and all the free libraries that are used in any of the previous (made by volunteers), and then here. /satire</i></p>
|
<p>DISCLAIMER: This site isn't distributing any content and is using mangadex.org API. All of the site's requests are done client side, my servers aren't sharing any manga data. Website is open source, and I don't claim any copyright on the publications. <i>If you believe in good faith you're downloading copyrighted content, file a DMCA at yourself, your operating system (as it took a part in the download), your ISP, your browser and all the free libraries that are used in any of the previous (made by volunteers), and then here. /satire</i></p>
|
||||||
|
|
||||||
<p>For DMCA requests, I recommend going to the mangadex page of the selected manga instead and reporting it there, as it will be taken down more quickly. You can of course report it to me and I'll block the page from viewing it, but it's trivial to remove that (1 required software installed to run this site, edit single line, run 2 more commands and you have bypassed the block), so I really recommend removing the source instead.</p>
|
<p>For DMCA requests, I recommend going to the mangadex page of the selected manga instead and reporting it there, as it will be taken down more quickly. You can of course report it to me and I'll block the page from viewing it, but it's trivial to remove that (1 required software installed to run this site, edit single line, run 2 more commands and you have bypassed the block), so I really recommend removing the source instead.</p>
|
||||||
|
|
||||||
<a href="/error">Crash</a>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
export let url = "";
|
export let inc;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key url}
|
{#key inc}
|
||||||
<div
|
<div
|
||||||
in:fade={{ opacity: 1, duration: 200, delay: 150 }}
|
in:fade={{ opacity: 1, duration: 200, delay: 150 }}
|
||||||
out:fade={{ opacity: 0, duration: 200 }}
|
out:fade={{ opacity: 0, duration: 200 }}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue