Testing page generation
This commit is contained in:
15
package-lock.json
generated
15
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "z-reader",
|
"name": "z-reader",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@zip.js/zip.js": "^2.7.20",
|
||||||
"panzoom": "^9.4.3"
|
"panzoom": "^9.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -885,6 +886,15 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@zip.js/zip.js": {
|
||||||
|
"version": "2.7.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.20.tgz",
|
||||||
|
"integrity": "sha512-rh5cby/bBOYC+hcK9qElDzbiIDeL3nhHPUTIE6/FQZR8mhY7azpthPdYbSNMOYBfv0AM188RNJ2yjtXsUfbAuQ==",
|
||||||
|
"engines": {
|
||||||
|
"deno": ">=1.0.0",
|
||||||
|
"node": ">=16.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.10.0",
|
"version": "8.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
||||||
@@ -3698,6 +3708,11 @@
|
|||||||
"eslint-visitor-keys": "^3.3.0"
|
"eslint-visitor-keys": "^3.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@zip.js/zip.js": {
|
||||||
|
"version": "2.7.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.20.tgz",
|
||||||
|
"integrity": "sha512-rh5cby/bBOYC+hcK9qElDzbiIDeL3nhHPUTIE6/FQZR8mhY7azpthPdYbSNMOYBfv0AM188RNJ2yjtXsUfbAuQ=="
|
||||||
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "8.10.0",
|
"version": "8.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@zip.js/zip.js": "^2.7.20",
|
||||||
"panzoom": "^9.4.3"
|
"panzoom": "^9.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
button {
|
button {
|
||||||
border: none;
|
border: none;
|
||||||
|
font-family: var(--font-family);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/lib/assets/001.jpg
Normal file
BIN
src/lib/assets/001.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 233 KiB |
BIN
src/lib/assets/049.jpg
Normal file
BIN
src/lib/assets/049.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 296 KiB |
@@ -1,4 +1,5 @@
|
|||||||
import type { Manga } from "$lib/types/catalog";
|
import type { Manga, Volume } from "$lib/types/catalog";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
export const currentManga = writable<Manga | undefined>(undefined);
|
export const currentManga = writable<Manga | undefined>(undefined);
|
||||||
|
export const currentVolume = writable<Volume | undefined>(undefined);
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
style:--color={color}
|
style:--color={color}
|
||||||
style:--background-color={backgroundColor}
|
style:--background-color={backgroundColor}
|
||||||
style:--active={activeColor}
|
style:--active={activeColor}
|
||||||
|
{...$$restProps}
|
||||||
on:click
|
on:click
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -11,32 +11,92 @@
|
|||||||
{
|
{
|
||||||
title: 'Manga name',
|
title: 'Manga name',
|
||||||
cover: image1,
|
cover: image1,
|
||||||
currentPage: 26,
|
volumes: [
|
||||||
|
{
|
||||||
|
cover: image1,
|
||||||
|
title: 'Volume 1',
|
||||||
|
currentPage: 0,
|
||||||
totalPages: 100
|
totalPages: 100
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
cover: image1,
|
||||||
|
title: 'Volume 2',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Another',
|
title: 'Another',
|
||||||
cover: image2,
|
cover: image2,
|
||||||
|
volumes: [
|
||||||
|
{
|
||||||
|
cover: image2,
|
||||||
|
title: 'Volume 1',
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
totalPages: 120
|
totalPages: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cover: image2,
|
||||||
|
title: 'Volume 2',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Awooo',
|
title: 'Awooo',
|
||||||
cover: image3,
|
cover: image3,
|
||||||
currentPage: 69,
|
volumes: [
|
||||||
totalPages: 96
|
{
|
||||||
|
cover: image3,
|
||||||
|
title: 'Volume 1',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cover: image3,
|
||||||
|
title: 'Volume 2',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Title',
|
title: 'Title',
|
||||||
cover: image4,
|
cover: image4,
|
||||||
currentPage: 9,
|
volumes: [
|
||||||
totalPages: 59
|
{
|
||||||
|
cover: image4,
|
||||||
|
title: 'Volume 1',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cover: image4,
|
||||||
|
title: 'Volume 2',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'sdhfjksdh',
|
title: 'sdhfjksdh',
|
||||||
cover: image5,
|
cover: image5,
|
||||||
currentPage: 19,
|
volumes: [
|
||||||
totalPages: 200
|
{
|
||||||
|
cover: image5,
|
||||||
|
title: 'Volume 1',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cover: image5,
|
||||||
|
title: 'Volume 2',
|
||||||
|
currentPage: 0,
|
||||||
|
totalPages: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import { currentManga } from '$lib/catalog';
|
import { currentManga } from '$lib/catalog';
|
||||||
import type { Manga } from '$lib/types/catalog';
|
import type { Manga } from '$lib/types/catalog';
|
||||||
import Button from './Button.svelte';
|
import { navbarTitle } from './NavBar.svelte';
|
||||||
|
|
||||||
export let manga: Manga;
|
export let manga: Manga;
|
||||||
const { cover, currentPage, title, totalPages } = manga;
|
const { cover, title } = manga;
|
||||||
|
|
||||||
function onClick() {
|
function onClick() {
|
||||||
|
console.log('bruh');
|
||||||
|
|
||||||
|
navbarTitle.set(title);
|
||||||
currentManga.set(manga);
|
currentManga.set(manga);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -16,7 +17,6 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
{title}
|
{title}
|
||||||
<img src={cover} alt={title} />
|
<img src={cover} alt={title} />
|
||||||
{currentPage} / {totalPages}
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
56
src/lib/components/FileUpload.svelte
Normal file
56
src/lib/components/FileUpload.svelte
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Entry } from '@zip.js/zip.js';
|
||||||
|
import Button from './Button.svelte';
|
||||||
|
import { colors } from '$lib/theme';
|
||||||
|
|
||||||
|
export let accept: string | null | undefined = undefined;
|
||||||
|
export let files: FileList | null | undefined = undefined;
|
||||||
|
export let multiple: boolean | null | undefined = true;
|
||||||
|
export let onUpload: ((files: FileList) => void) | undefined = undefined;
|
||||||
|
|
||||||
|
let input: HTMLInputElement;
|
||||||
|
|
||||||
|
function handleChange() {
|
||||||
|
if (files && onUpload) {
|
||||||
|
onUpload(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClick() {
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrop(event: DragEvent) {
|
||||||
|
const items = event.dataTransfer?.items;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input type="file" {multiple} {accept} bind:files bind:this={input} on:change={handleChange} />
|
||||||
|
|
||||||
|
<button
|
||||||
|
on:click={onClick}
|
||||||
|
style:--border={colors.secondaryColor}
|
||||||
|
on:dragover|preventDefault
|
||||||
|
on:drop|preventDefault|stopPropagation={onDrop}
|
||||||
|
>
|
||||||
|
<p style:color={colors.secondaryColor}><slot>Upload</slot></p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 500px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 2px dashed var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,16 +1,51 @@
|
|||||||
<script lang="ts">
|
<script lang="ts" context="module">
|
||||||
import { colors } from '$lib/theme';
|
export let navbarTitle = writable<string | undefined>(undefined);
|
||||||
|
</script>
|
||||||
|
|
||||||
export let title: string | undefined = undefined;
|
<script lang="ts">
|
||||||
|
import { afterNavigate } from '$app/navigation';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
import { currentManga, currentVolume } from '$lib/catalog';
|
||||||
|
|
||||||
|
import { colors } from '$lib/theme';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
let title: string | undefined = 'Mokuro';
|
||||||
|
let back: string | undefined = undefined;
|
||||||
|
|
||||||
|
afterNavigate(() => {
|
||||||
|
console.log($page?.route.id);
|
||||||
|
window.document.body.classList.remove('reader');
|
||||||
|
|
||||||
|
switch ($page?.route.id) {
|
||||||
|
case '/[manga]':
|
||||||
|
title = $currentManga?.title;
|
||||||
|
back = '/';
|
||||||
|
break;
|
||||||
|
case '/[manga]/[volume]':
|
||||||
|
window.document.body.classList.add('reader');
|
||||||
|
title = $currentVolume?.title;
|
||||||
|
back = '/manga';
|
||||||
|
break;
|
||||||
|
case '/upload':
|
||||||
|
title = 'Upload';
|
||||||
|
back = '/';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
title = 'Mokuro';
|
||||||
|
back = undefined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div style:background-color={colors.primaryColor}>
|
<div style:background-color={colors.primaryColor}>
|
||||||
{#if title}
|
{#if back}
|
||||||
<a href="/"><h2>Back</h2></a>
|
<a href={back}><h2>Back</h2></a>
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
{:else}
|
{:else}
|
||||||
<a href="/"><h2>Mokuro</h2></a>
|
<a href="/"><h2>{title}</h2></a>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
195
src/lib/components/Reader/MangaPage.svelte
Normal file
195
src/lib/components/Reader/MangaPage.svelte
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import img from '$lib/assets/001.jpg';
|
||||||
|
|
||||||
|
type Block = {
|
||||||
|
box: number[];
|
||||||
|
vertical: boolean;
|
||||||
|
fontSize: number;
|
||||||
|
lines: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Page = {
|
||||||
|
version?: string;
|
||||||
|
imgWidth: number;
|
||||||
|
imgHeight: number;
|
||||||
|
blocks: Block[];
|
||||||
|
imgPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
let page: Page = {
|
||||||
|
version: '0.1.6',
|
||||||
|
imgWidth: 1078,
|
||||||
|
imgHeight: 1530,
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
box: [924, 167, 948, 380],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 21.0,
|
||||||
|
lines: ['私も頑張らなくちゃ!']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [584, 242, 610, 397],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 26.0,
|
||||||
|
lines: ['リョウ先輩!']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [895, 474, 943, 687],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 20.0,
|
||||||
|
lines: ['私のギターの練習', 'みてもらえませんか!']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [898, 787, 947, 979],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 21.0,
|
||||||
|
lines: ['ぼっちに教えて', 'もらってるじゃん?']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [708, 796, 783, 936],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 20.0,
|
||||||
|
lines: ['〜〜っ', 'もっと練習', 'したいんです!!']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [663, 832, 701, 971],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 20.0,
|
||||||
|
lines: ['後藤さんだって', '?の練習あるし...']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [568, 800, 616, 1011],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 20.0,
|
||||||
|
lines: ['ちょっと喜多ちゃん', '急にどうしちゃったの']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [874, 1122, 951, 1380],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 22.0,
|
||||||
|
lines: ['まさか結束パンドの', 'ギター同士で血で血を洗う', 'パート争いを...']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [636, 1135, 682, 1256],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 20.0,
|
||||||
|
lines: ['ちょっと', '結束してよ〜']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
box: [560, 1240, 590, 1381],
|
||||||
|
vertical: true,
|
||||||
|
fontSize: 27.0,
|
||||||
|
lines: ['違います!!']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
imgPath: '049.jpg'
|
||||||
|
};
|
||||||
|
|
||||||
|
let bold = false;
|
||||||
|
|
||||||
|
$: fontWeight = bold ? 'bold' : '400';
|
||||||
|
|
||||||
|
console.log(bold);
|
||||||
|
|
||||||
|
function clamp(x: number, min: number, max: number) {
|
||||||
|
return Math.min(Math.max(x, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { blocks, imgHeight, imgPath, imgWidth } = page;
|
||||||
|
const area = imgWidth * imgHeight;
|
||||||
|
|
||||||
|
const textBoxes = blocks.map((block) => {
|
||||||
|
const { box, fontSize, lines, vertical } = block;
|
||||||
|
let [_xmin, _ymin, _xmax, _ymax] = box;
|
||||||
|
|
||||||
|
const xmin = clamp(_xmin, 0, imgWidth);
|
||||||
|
const ymin = clamp(_ymin, 0, imgHeight);
|
||||||
|
const xmax = clamp(_xmax, 0, imgWidth);
|
||||||
|
const ymax = clamp(_ymax, 0, imgHeight);
|
||||||
|
|
||||||
|
const width = xmax - xmin;
|
||||||
|
const height = ymax - ymin;
|
||||||
|
|
||||||
|
const textBox = {
|
||||||
|
left: `${xmin}px`,
|
||||||
|
top: `${ymin}px`,
|
||||||
|
width: `${width}px`,
|
||||||
|
height: `${height}px`,
|
||||||
|
fontSize: `${fontSize}px`,
|
||||||
|
writingMode: vertical ? 'vertical-rl' : 'horizontal-tb',
|
||||||
|
lines
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(textBox);
|
||||||
|
|
||||||
|
return textBox;
|
||||||
|
});
|
||||||
|
let src = `/src/lib/assets/${imgPath}`;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div style:--bold={fontWeight}>
|
||||||
|
<div class="bold">
|
||||||
|
<label>Bold</label>
|
||||||
|
<input bind:checked={bold} type="checkbox" placeholder="????" />
|
||||||
|
</div>
|
||||||
|
<img draggable="false" {src} alt="Page 1" />
|
||||||
|
{#each textBoxes as { left, top, width, height, lines, fontSize, writingMode }}
|
||||||
|
<div
|
||||||
|
class="text-box"
|
||||||
|
style:width
|
||||||
|
style:height
|
||||||
|
style:left
|
||||||
|
style:top
|
||||||
|
style:font-size={fontSize}
|
||||||
|
style:writing-mode={writingMode}
|
||||||
|
>
|
||||||
|
{#each lines as line}
|
||||||
|
<p>{line}</p>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
position: absolute;
|
||||||
|
color: #000;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-box {
|
||||||
|
color: black;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
line-height: 1.1em;
|
||||||
|
font-size: 16pt;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
.text-box:hover {
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0);
|
||||||
|
z-index: 999 !important;
|
||||||
|
}
|
||||||
|
.text-box p {
|
||||||
|
display: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
line-height: 1.1em;
|
||||||
|
margin: 0;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
font-weight: var(--bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-box:hover p {
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
26
src/lib/components/Reader/Reader.svelte
Normal file
26
src/lib/components/Reader/Reader.svelte
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { currentManga, currentVolume } from '$lib/catalog';
|
||||||
|
import Button from '$lib/components/Button.svelte';
|
||||||
|
import { Panzoom, zoomOriginal } from '$lib/panzoom';
|
||||||
|
import MangaPage from './MangaPage.svelte';
|
||||||
|
|
||||||
|
const volume = $currentVolume;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if volume}
|
||||||
|
<div>
|
||||||
|
<Button on:click={zoomOriginal}>Reset Zoom</Button>
|
||||||
|
</div>
|
||||||
|
<Panzoom>
|
||||||
|
<MangaPage />
|
||||||
|
</Panzoom>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
36
src/lib/components/VolumeItem.svelte
Normal file
36
src/lib/components/VolumeItem.svelte
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { currentManga, currentVolume } from '$lib/catalog';
|
||||||
|
import type { Volume } from '$lib/types/catalog';
|
||||||
|
|
||||||
|
export let volume: Volume;
|
||||||
|
const { cover, title, currentPage, totalPages } = volume;
|
||||||
|
|
||||||
|
function onClick() {
|
||||||
|
currentVolume.set(volume);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a href={`${$page.params.manga}/${title}`} on:click={onClick}>
|
||||||
|
<div class="content">
|
||||||
|
{title}
|
||||||
|
<img src={cover} alt={title} />
|
||||||
|
{currentPage} / {totalPages}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
width: 250px;
|
||||||
|
height: 350px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -65,8 +65,6 @@ export function panAlign(alignX: PanX, alignY: PanY) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(x, y);
|
|
||||||
|
|
||||||
pz?.moveTo(x, y)
|
pz?.moveTo(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
export type Manga = {
|
export type Volume = {
|
||||||
title: string;
|
title: string;
|
||||||
cover: string;
|
cover: string;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Manga = {
|
||||||
|
title: string;
|
||||||
|
cover: string;
|
||||||
|
volumes: Volume[];
|
||||||
};
|
};
|
||||||
8
src/lib/upload/index.ts
Normal file
8
src/lib/upload/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { BlobReader, ZipReader } from "@zip.js/zip.js";
|
||||||
|
|
||||||
|
export async function unzipManga(file: File) {
|
||||||
|
const zipFileReader = new BlobReader(file);
|
||||||
|
const zipReader = new ZipReader(zipFileReader);
|
||||||
|
|
||||||
|
return await zipReader.getEntries()
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { afterNavigate } from '$app/navigation';
|
|
||||||
import { navigating } from '$app/stores';
|
|
||||||
import { currentManga } from '$lib/catalog';
|
|
||||||
import Button from '$lib/components/Button.svelte';
|
|
||||||
import NavBar from '$lib/components/NavBar.svelte';
|
|
||||||
import '../../app.css';
|
|
||||||
|
|
||||||
afterNavigate(({}) => {
|
|
||||||
window.document.body.classList.remove('reader');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<NavBar />
|
|
||||||
<div>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Catalog from '$lib/components/Catalog.svelte';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Catalog />
|
|
||||||
16
src/routes/+layout.svelte
Normal file
16
src/routes/+layout.svelte
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import NavBar from '$lib/components/NavBar.svelte';
|
||||||
|
import '../app.css';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<NavBar />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
12
src/routes/+page.svelte
Normal file
12
src/routes/+page.svelte
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import Button from '$lib/components/Button.svelte';
|
||||||
|
import Catalog from '$lib/components/Catalog.svelte';
|
||||||
|
|
||||||
|
function onClick() {
|
||||||
|
goto('/upload');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button on:click={onClick}>Upload</Button>
|
||||||
|
<Catalog />
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { currentManga } from '$lib/catalog';
|
|
||||||
import NavBar from '$lib/components/NavBar.svelte';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { afterNavigate, goto } from '$app/navigation';
|
|
||||||
import '../../app.css';
|
|
||||||
|
|
||||||
const manga = $currentManga;
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!manga) {
|
|
||||||
goto('/');
|
|
||||||
window.document.body.classList.remove('reader');
|
|
||||||
} else {
|
|
||||||
window.document.body.classList.add('reader');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<NavBar title={manga?.title} />
|
|
||||||
<slot />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:global(body.reader) {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -4,24 +4,30 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import Button from '$lib/components/Button.svelte';
|
import Button from '$lib/components/Button.svelte';
|
||||||
import { panAlign, Panzoom, zoomOriginal } from '$lib/panzoom';
|
import { panAlign, Panzoom, zoomOriginal } from '$lib/panzoom';
|
||||||
|
import VolumeItem from '$lib/components/VolumeItem.svelte';
|
||||||
|
|
||||||
const manga = $currentManga;
|
const manga = $currentManga;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!manga) {
|
||||||
|
goto('/');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if manga}
|
|
||||||
<div>
|
<div>
|
||||||
<Button on:click={zoomOriginal}>Reset Zoom</Button>
|
{#if manga}
|
||||||
</div>
|
{#each manga.volumes as volume}
|
||||||
<Panzoom>
|
<VolumeItem {volume} />
|
||||||
<img draggable="false" src={manga.cover} alt={manga.title} />
|
{/each}
|
||||||
</Panzoom>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
position: absolute;
|
display: flex;
|
||||||
bottom: 10px;
|
flex-direction: row;
|
||||||
left: 10px;
|
gap: 20px;
|
||||||
z-index: 1;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
25
src/routes/[manga]/[volume]/+layout.svelte
Normal file
25
src/routes/[manga]/[volume]/+layout.svelte
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { currentVolume } from '$lib/catalog';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
const volume = $currentVolume;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!volume) {
|
||||||
|
goto('/');
|
||||||
|
} else {
|
||||||
|
console.log('???');
|
||||||
|
|
||||||
|
window.document.body.classList.add('reader');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(body.reader) {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
5
src/routes/[manga]/[volume]/+page.svelte
Normal file
5
src/routes/[manga]/[volume]/+page.svelte
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Reader from '$lib/components/Reader/Reader.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Reader />
|
||||||
24
src/routes/upload/+page.svelte
Normal file
24
src/routes/upload/+page.svelte
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FileUpload from '$lib/components/FileUpload.svelte';
|
||||||
|
import { unzipManga } from '$lib/upload';
|
||||||
|
import type { Entry } from '@zip.js/zip.js';
|
||||||
|
|
||||||
|
let promise: Promise<Entry[]> | undefined;
|
||||||
|
|
||||||
|
async function onUpload(files: FileList) {
|
||||||
|
const [file] = files;
|
||||||
|
promise = unzipManga(file);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FileUpload {onUpload} accept=".mokuro,.zip,.cbz,.rar" />
|
||||||
|
|
||||||
|
{#if promise}
|
||||||
|
{#await promise}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:then entries}
|
||||||
|
{#each entries as entry}
|
||||||
|
<p>{entry.filename}</p>
|
||||||
|
{/each}
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
Reference in New Issue
Block a user