code refactoring, added router, moved download api path
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,4 +13,5 @@ yt-dlp-webui
|
|||||||
session.dat
|
session.dat
|
||||||
config.yml
|
config.yml
|
||||||
cookies.txt
|
cookies.txt
|
||||||
__debug*
|
__debug*
|
||||||
|
app/
|
||||||
@@ -30,6 +30,8 @@ func ApplyRouter(db *sql.DB, mdb *internal.MemoryDB, mq *internal.MessageQueue)
|
|||||||
r.Post("/template", h.AddTemplate())
|
r.Post("/template", h.AddTemplate())
|
||||||
r.Get("/template/all", h.GetTemplates())
|
r.Get("/template/all", h.GetTemplates())
|
||||||
r.Delete("/template/{id}", h.DeleteTemplate())
|
r.Delete("/template/{id}", h.DeleteTemplate())
|
||||||
|
|
||||||
r.Get("/tree", h.DirectoryTree())
|
r.Get("/tree", h.DirectoryTree())
|
||||||
|
r.Get("/d/{id}", h.DownloadFile())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package rest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/internal"
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/internal"
|
||||||
@@ -173,3 +176,36 @@ func (h *Handler) DirectoryTree() http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) DownloadFile() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
id := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
path, err := h.service.DownloadFile(r.Context(), id)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Add(
|
||||||
|
"Content-Disposition",
|
||||||
|
"inline; filename="+filepath.Base(*path),
|
||||||
|
)
|
||||||
|
w.Header().Set(
|
||||||
|
"Content-Type",
|
||||||
|
"application/octet-stream",
|
||||||
|
)
|
||||||
|
|
||||||
|
fd, err := os.Open(*path)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(w, fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -123,3 +123,12 @@ func (s *Service) DeleteTemplate(ctx context.Context, id string) error {
|
|||||||
func (s *Service) DirectoryTree(ctx context.Context) (*internal.Stack[sys.FSNode], error) {
|
func (s *Service) DirectoryTree(ctx context.Context) (*internal.Stack[sys.FSNode], error) {
|
||||||
return sys.DirectoryTree()
|
return sys.DirectoryTree()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) DownloadFile(ctx context.Context, id string) (*string, error) {
|
||||||
|
p, err := s.mdb.Get(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &p.Output.Path, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/roboto": "^5.0.8",
|
"@fontsource/roboto": "^5.0.8",
|
||||||
"fp-ts": "^2.16.2",
|
"fp-ts": "^2.16.2",
|
||||||
"lucide-svelte": "^0.323.0"
|
"lucide-svelte": "^0.323.0",
|
||||||
|
"svelte-spa-router": "^4.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
ui/pnpm-lock.yaml
generated
14
ui/pnpm-lock.yaml
generated
@@ -14,6 +14,9 @@ dependencies:
|
|||||||
lucide-svelte:
|
lucide-svelte:
|
||||||
specifier: ^0.323.0
|
specifier: ^0.323.0
|
||||||
version: 0.323.0(svelte@4.2.10)
|
version: 0.323.0(svelte@4.2.10)
|
||||||
|
svelte-spa-router:
|
||||||
|
specifier: ^4.0.1
|
||||||
|
version: 4.0.1
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@sveltejs/vite-plugin-svelte':
|
'@sveltejs/vite-plugin-svelte':
|
||||||
@@ -1254,6 +1257,11 @@ packages:
|
|||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/regexparam@2.0.2:
|
||||||
|
resolution: {integrity: sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/resolve-from@4.0.0:
|
/resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -1498,6 +1506,12 @@ packages:
|
|||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/svelte-spa-router@4.0.1:
|
||||||
|
resolution: {integrity: sha512-2JkmUQ2f9jRluijL58LtdQBIpynSbem2eBGp4zXdi7aDY1znbR6yjw0KsonD0aq2QLwf4Yx4tBJQjxIjgjXHKg==}
|
||||||
|
dependencies:
|
||||||
|
regexparam: 2.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/svelte@4.2.10:
|
/svelte@4.2.10:
|
||||||
resolution: {integrity: sha512-Ep06yCaCdgG1Mafb/Rx8sJ1QS3RW2I2BxGp2Ui9LBHSZ2/tO/aGLc5WqPjgiAP6KAnLJGaIr/zzwQlOo1b8MxA==}
|
resolution: {integrity: sha512-Ep06yCaCdgG1Mafb/Rx8sJ1QS3RW2I2BxGp2Ui9LBHSZ2/tO/aGLc5WqPjgiAP6KAnLJGaIr/zzwQlOo1b8MxA==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { SvelteToast } from '@zerodevx/svelte-toast';
|
import { SvelteToast } from '@zerodevx/svelte-toast';
|
||||||
|
import Router from 'svelte-spa-router';
|
||||||
|
import { wrap } from 'svelte-spa-router/wrap';
|
||||||
import Footer from './lib/Footer.svelte';
|
import Footer from './lib/Footer.svelte';
|
||||||
import Home from './lib/Home.svelte';
|
import Home from './views/Home.svelte';
|
||||||
import Navbar from './lib/Navbar.svelte';
|
import Navbar from './lib/Navbar.svelte';
|
||||||
|
|
||||||
|
const routes = {
|
||||||
|
'/': Home,
|
||||||
|
'/settings': wrap({
|
||||||
|
asyncComponent: () => import('./views/SettingsView.svelte'),
|
||||||
|
}),
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main
|
<main
|
||||||
class="bg-neutral-50 dark:bg-neutral-900 h-screen text-neutral-950 dark:text-neutral-50"
|
class="bg-neutral-50 dark:bg-neutral-900 h-screen text-neutral-950 dark:text-neutral-50"
|
||||||
>
|
>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<Home />
|
<Router {routes} />
|
||||||
<Footer />
|
<Footer />
|
||||||
<SvelteToast />
|
<SvelteToast />
|
||||||
<!-- <FloatingAction /> -->
|
<!-- <FloatingAction /> -->
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class={`px-2.5 py-2 rounded-lg bg-orange-300 hover:bg-orange-400 hover:duration-150 text-sm font-semibold ${
|
class={`px-2.5 py-2 rounded-lg bg-blue-300 hover:bg-blue-400 hover:duration-150 text-sm font-semibold ${
|
||||||
disabled && 'bg-neutral-300 hover:bg-neutral-300'
|
disabled && 'bg-neutral-300 hover:bg-neutral-300'
|
||||||
} ${clazz}`}
|
} ${clazz}`}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-1.5 p-1 bg-orange-200 rounded-lg text-neutral-900"
|
class="flex items-center gap-1.5 p-1 bg-blue-200 rounded-lg text-neutral-900"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
{text}
|
{text}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import Chip from './Chip.svelte';
|
|
||||||
import type { RPCResult } from './types';
|
|
||||||
import { formatSpeedMiB, mapProcessStatus, roundMiB } from './utils';
|
|
||||||
import { rpcClient } from './store';
|
|
||||||
import Button from './Button.svelte';
|
import Button from './Button.svelte';
|
||||||
|
import Chip from './Chip.svelte';
|
||||||
|
import { rpcClient, serverApiEndpoint } from './store';
|
||||||
|
import type { RPCResult } from './types';
|
||||||
|
import { formatSpeedMiB, roundMiB } from './utils';
|
||||||
|
|
||||||
export let download: RPCResult;
|
export let download: RPCResult;
|
||||||
|
|
||||||
@@ -14,12 +14,12 @@
|
|||||||
<div
|
<div
|
||||||
class="flex gap-4
|
class="flex gap-4
|
||||||
bg-neutral-100 dark:bg-neutral-800
|
bg-neutral-100 dark:bg-neutral-800
|
||||||
pt-2 md:p-4
|
p-2 md:p-4
|
||||||
rounded-lg shadow-lg
|
rounded-lg shadow-lg
|
||||||
border dark:border-neutral-700"
|
border dark:border-neutral-700"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="h-full w-96 bg-cover bg-center rounded"
|
class="h-full hidden sm:block w-96 bg-cover bg-center rounded"
|
||||||
style="background-image: url({download.info.thumbnail})"
|
style="background-image: url({download.info.thumbnail})"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col justify-end gap-2 select-none flex-wrap">
|
<div class="flex flex-col justify-end gap-2 select-none flex-wrap">
|
||||||
<div class="flex items-center gap-2 text-sm">
|
<div class="hidden sm:flex items-center gap-2 text-sm">
|
||||||
{#if download.info.vcodec}
|
{#if download.info.vcodec}
|
||||||
<Chip text={download.info.vcodec} />
|
<Chip text={download.info.vcodec} />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -50,9 +50,9 @@
|
|||||||
{#if download.info.filesize_approx}
|
{#if download.info.filesize_approx}
|
||||||
<Chip text={roundMiB(download.info.filesize_approx)} />
|
<Chip text={roundMiB(download.info.filesize_approx)} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if download.progress.process_status}
|
<!-- {#if download.progress.process_status}
|
||||||
<Chip text={mapProcessStatus(download.progress.process_status)} />
|
<Chip text={mapProcessStatus(download.progress.process_status)} />
|
||||||
{/if}
|
{/if} -->
|
||||||
{#if download.progress.speed}
|
{#if download.progress.speed}
|
||||||
<Chip text={formatSpeedMiB(download.progress.speed)} />
|
<Chip text={formatSpeedMiB(download.progress.speed)} />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
<Button class="w-14" on:click={() => remove(download.id)}>Stop</Button>
|
<Button class="w-14" on:click={() => remove(download.id)}>Stop</Button>
|
||||||
{#if download.progress.process_status === 2}
|
{#if download.progress.process_status === 2}
|
||||||
<Button class="w-18">Download</Button>
|
<Button class="w-18">Download</Button>
|
||||||
|
<!-- <a href={`${$serverApiEndpoint}/api/v1/d/${download.id}`}>d</a> -->
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@
|
|||||||
class={`h-2 rounded-full ${
|
class={`h-2 rounded-full ${
|
||||||
download.progress.process_status === 2
|
download.progress.process_status === 2
|
||||||
? 'bg-green-600'
|
? 'bg-green-600'
|
||||||
: 'bg-orange-500'
|
: 'bg-blue-500'
|
||||||
}`}
|
}`}
|
||||||
style="width: {download.progress.percentage}"
|
style="width: {download.progress.percentage}"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,18 +5,18 @@
|
|||||||
<div class="absolute bottom-10 right-10">
|
<div class="absolute bottom-10 right-10">
|
||||||
<!-- <div class="relative mb-4 flex flex-col justify-center items-center gap-2">
|
<!-- <div class="relative mb-4 flex flex-col justify-center items-center gap-2">
|
||||||
<button
|
<button
|
||||||
class="relative flex items-center justify-center bg-orange-500 h-8 w-8 z-10 rounded-2xl shadow-xl text-neutral-100"
|
class="relative flex items-center justify-center bg-blue-500 h-8 w-8 z-10 rounded-2xl shadow-xl text-neutral-100"
|
||||||
>
|
>
|
||||||
<Plus size={18} />
|
<Plus size={18} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="relative flex items-center justify-center bg-orange-500 h-8 w-8 z-10 rounded-2xl shadow-xl text-neutral-100"
|
class="relative flex items-center justify-center bg-blue-500 h-8 w-8 z-10 rounded-2xl shadow-xl text-neutral-100"
|
||||||
>
|
>
|
||||||
<Plus size={18} />
|
<Plus size={18} />
|
||||||
</button>
|
</button>
|
||||||
</div> -->
|
</div> -->
|
||||||
<button
|
<button
|
||||||
class="relative bg-orange-500 p-5 z-10 rounded-2xl shadow-xl text-neutral-100"
|
class="relative bg-blue-500 p-5 z-10 rounded-2xl shadow-xl text-neutral-100"
|
||||||
>
|
>
|
||||||
<Plus />
|
<Plus />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ChevronUp, ChevronDown } from 'lucide-svelte';
|
import { ChevronDown, ChevronUp } from 'lucide-svelte';
|
||||||
import { cubicOut } from 'svelte/easing';
|
import { cubicOut } from 'svelte/easing';
|
||||||
import { tweened } from 'svelte/motion';
|
import { tweened } from 'svelte/motion';
|
||||||
import Settings from './Settings.svelte';
|
|
||||||
import NewDownload from './NewDownload.svelte';
|
import NewDownload from './NewDownload.svelte';
|
||||||
|
|
||||||
const height = tweened(52, {
|
const height = tweened(52, {
|
||||||
@@ -11,7 +10,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const minHeight = 52;
|
const minHeight = 52;
|
||||||
const maxHeight = window.innerHeight - 60;
|
const maxHeight = window.innerHeight / 1.5;
|
||||||
|
|
||||||
let open = false;
|
let open = false;
|
||||||
$: open = $height > minHeight;
|
$: open = $height > minHeight;
|
||||||
@@ -19,7 +18,7 @@
|
|||||||
|
|
||||||
<footer
|
<footer
|
||||||
class="
|
class="
|
||||||
absolute bottom-0
|
fixed bottom-0 z-10
|
||||||
w-full
|
w-full
|
||||||
p-2
|
p-2
|
||||||
bg-neutral-100 dark:bg-neutral-800
|
bg-neutral-100 dark:bg-neutral-800
|
||||||
@@ -44,7 +43,6 @@
|
|||||||
{#if $height > 100}
|
{#if $height > 100}
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<NewDownload />
|
<NewDownload />
|
||||||
<Settings />
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -24,16 +24,16 @@
|
|||||||
for="formats"
|
for="formats"
|
||||||
class="
|
class="
|
||||||
[&_p]:text-gray-900 [&_span]:text-gray-500
|
[&_p]:text-gray-900 [&_span]:text-gray-500
|
||||||
peer-checked:[&_p]:text-white peer-checked:[&_span]:text-orange-100
|
peer-checked:[&_p]:text-white peer-checked:[&_span]:text-blue-100
|
||||||
peer-focus:ring-2 peer-focus:ring-white
|
peer-focus:ring-2 peer-focus:ring-white
|
||||||
peer-focus:ring-opacity-60 peer-focus:ring-offset-2 peer-focus:ring-offset-orange-300
|
peer-focus:ring-opacity-60 peer-focus:ring-offset-2 peer-focus:ring-offset-blue-300
|
||||||
bg-white
|
bg-white
|
||||||
relative flex
|
relative flex
|
||||||
cursor-pointer
|
cursor-pointer
|
||||||
rounded-lg px-5 py-4
|
rounded-lg px-5 py-4
|
||||||
shadow-md
|
shadow-md
|
||||||
focus:outline-none
|
focus:outline-none
|
||||||
peer-checked:bg-orange-700/75
|
peer-checked:bg-blue-700/75
|
||||||
peer-checked:text-white"
|
peer-checked:text-white"
|
||||||
>
|
>
|
||||||
<div class="flex w-full items-center justify-between">
|
<div class="flex w-full items-center justify-between">
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ArrowDownUp, Github, HardDrive, Network } from 'lucide-svelte';
|
import {
|
||||||
|
ArrowDownUp,
|
||||||
|
Github,
|
||||||
|
HardDrive,
|
||||||
|
Network,
|
||||||
|
Settings,
|
||||||
|
} from 'lucide-svelte';
|
||||||
import { downloads, rpcClient, serverApiEndpoint } from './store';
|
import { downloads, rpcClient, serverApiEndpoint } from './store';
|
||||||
import { formatGiB, formatSpeedMiB } from './utils';
|
import { formatGiB, formatSpeedMiB } from './utils';
|
||||||
import * as O from 'fp-ts/Option';
|
import * as O from 'fp-ts/Option';
|
||||||
import { pipe } from 'fp-ts/lib/function';
|
import { pipe } from 'fp-ts/lib/function';
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
|
import { link } from 'svelte-spa-router';
|
||||||
|
|
||||||
let downloadSpeed = 0;
|
let downloadSpeed = 0;
|
||||||
|
|
||||||
@@ -34,13 +41,13 @@
|
|||||||
shadow-lg
|
shadow-lg
|
||||||
select-none"
|
select-none"
|
||||||
>
|
>
|
||||||
<div class="font-semibold text-lg">yt-dlp WebUI</div>
|
<a use:link={'/'} href="/" class="font-semibold text-lg">yt-dlp WebUI</a>
|
||||||
|
|
||||||
<div />
|
<div />
|
||||||
|
|
||||||
<div class="flex items-center gap-2 text-sm">
|
<div class="flex items-center gap-2 text-sm">
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-orange-200 rounded-lg"
|
class="hidden sm:flex items-center gap-1.5 p-1 text-neutral-900 bg-blue-200 rounded-lg"
|
||||||
>
|
>
|
||||||
<ArrowDownUp size={18} />
|
<ArrowDownUp size={18} />
|
||||||
<div>
|
<div>
|
||||||
@@ -50,7 +57,7 @@
|
|||||||
|
|
||||||
<div class="flex items-center gap-2 text-sm">
|
<div class="flex items-center gap-2 text-sm">
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-orange-200 rounded-lg"
|
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-blue-200 rounded-lg"
|
||||||
>
|
>
|
||||||
<HardDrive size={18} />
|
<HardDrive size={18} />
|
||||||
<div>
|
<div>
|
||||||
@@ -63,7 +70,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-orange-200 rounded-lg"
|
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-blue-200 rounded-lg"
|
||||||
>
|
>
|
||||||
<Network size={18} />
|
<Network size={18} />
|
||||||
<div>
|
<div>
|
||||||
@@ -73,10 +80,18 @@
|
|||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/marcopeocchi/yt-dlp-web-ui"
|
href="https://github.com/marcopeocchi/yt-dlp-web-ui"
|
||||||
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-orange-200 rounded-lg"
|
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-blue-200 rounded-lg"
|
||||||
>
|
>
|
||||||
<Github size={18} />
|
<Github size={18} />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
use:link={'/settings'}
|
||||||
|
href="/settings"
|
||||||
|
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-blue-200 rounded-lg"
|
||||||
|
>
|
||||||
|
<Settings size={18} />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
border rounded-lg
|
border rounded-lg
|
||||||
appearance-none
|
appearance-none
|
||||||
text-sm font-semibold
|
text-sm font-semibold
|
||||||
focus:outline-orange-300
|
focus:outline-blue-300
|
||||||
"
|
"
|
||||||
bind:value
|
bind:value
|
||||||
{disabled}
|
{disabled}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
const update = () => (loading = get(rpcClient).updateExecutable());
|
const update = () => (loading = get(rpcClient).updateExecutable());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full px-8 mt-8">
|
<div class="w-full">
|
||||||
<div class="font-semibold text-lg mb-4">Settings</div>
|
<div class="font-semibold text-lg mb-4">Settings</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div role="status">
|
<div role="status">
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-orange-400"
|
class="w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-blue-400"
|
||||||
viewBox="0 0 100 101"
|
viewBox="0 0 100 101"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -12,7 +12,13 @@
|
|||||||
<label for=""> {label} </label>
|
<label for=""> {label} </label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class={`p-2 bg-neutral-50 border rounded-lg focus:outline-orange-300 ${clazz}`}
|
class={`p-2
|
||||||
|
bg-neutral-50 border
|
||||||
|
rounded-lg
|
||||||
|
focus:outline-blue-300
|
||||||
|
dark:bg-neutral-700 dark:border-neutral-900
|
||||||
|
${clazz}
|
||||||
|
`}
|
||||||
on:keyup
|
on:keyup
|
||||||
bind:value
|
bind:value
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
import * as O from 'fp-ts/Option';
|
import * as O from 'fp-ts/Option';
|
||||||
import { pipe } from 'fp-ts/lib/function';
|
import { pipe } from 'fp-ts/lib/function';
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import DownloadCard from './DownloadCard.svelte';
|
import DownloadCard from '../lib/DownloadCard.svelte';
|
||||||
import Spinner from './Spinner.svelte';
|
import Spinner from '../lib/Spinner.svelte';
|
||||||
import { downloads, rpcClient } from './store';
|
import { downloads, rpcClient } from '../lib/store';
|
||||||
import { datetimeCompareFunc, isRPCResponse } from './utils';
|
import { datetimeCompareFunc, isRPCResponse } from '../lib/utils';
|
||||||
|
|
||||||
const unsubscribe = rpcClient.subscribe(($client) => {
|
const unsubscribe = rpcClient.subscribe(($client) => {
|
||||||
setInterval(() => $client.running(), 750);
|
setInterval(() => $client.running(), 750);
|
||||||
13
ui/src/views/SettingsView.svelte
Normal file
13
ui/src/views/SettingsView.svelte
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Settings from '../lib/Settings.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main
|
||||||
|
class="bg-neutral-100 dark:bg-neutral-800
|
||||||
|
rounded-xl
|
||||||
|
border dark:border-neutral-700
|
||||||
|
shadow-lg
|
||||||
|
m-8 p-4"
|
||||||
|
>
|
||||||
|
<Settings />
|
||||||
|
</main>
|
||||||
Reference in New Issue
Block a user