feat(shell,api,keys): firefly v1 shell over the api with cookie auth, period, palette and keymap
This commit is contained in:
1 parent
ae4de7911b
commit
c74dac3e00
37 files changed
+58163
-79
No files matched your search
@@ -1,10 +1,93 @@
|
||||
<script lang="ts">
|
||||
import "./layout.css";
|
||||
import { QueryClientProvider } from "@tanstack/svelte-query";
|
||||
import { ModeWatcher } from "mode-watcher";
|
||||
import { onMount, untrack } from "svelte";
|
||||
import { queryClient } from "#lib/api/lists.js";
|
||||
import QuickEntry from "#lib/components/QuickEntry.svelte";
|
||||
import Shell from "#lib/components/shell/Shell.svelte";
|
||||
import { keys } from "#lib/keys.svelte.js";
|
||||
import { period, rangeFromUrl } from "#lib/period.svelte.js";
|
||||
import { session } from "#lib/session.svelte.js";
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
let lastHref = "";
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const back = await session.boot();
|
||||
period.init(session.viewRange, page.url);
|
||||
if (back) {
|
||||
await goto(back, { replace: true });
|
||||
}
|
||||
} catch (error) {
|
||||
session.failed = error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!period.ready) {
|
||||
return;
|
||||
}
|
||||
const url = page.url;
|
||||
const start = period.start;
|
||||
const end = period.end;
|
||||
untrack(() => {
|
||||
const fromUrl = rangeFromUrl(url);
|
||||
if (url.href !== lastHref && fromUrl) {
|
||||
lastHref = url.href;
|
||||
period.set(fromUrl);
|
||||
return;
|
||||
}
|
||||
lastHref = url.href;
|
||||
if (fromUrl?.start === start && fromUrl?.end === end) {
|
||||
return;
|
||||
}
|
||||
const next = new URL(url.href);
|
||||
next.searchParams.set("start", start);
|
||||
next.searchParams.set("end", end);
|
||||
lastHref = next.href;
|
||||
goto(next, { replace: true, shallow: true, state: page.state });
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<ModeWatcher />
|
||||
<svelte:window onkeydown={keys.handle} />
|
||||
|
||||
{@render children()}
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{#if session.ready}
|
||||
<Shell>
|
||||
{@render children()}
|
||||
{#snippet quick()}
|
||||
<QuickEntry />
|
||||
{/snippet}
|
||||
</Shell>
|
||||
{:else if session.failed}
|
||||
<main class="grid min-h-dvh place-items-center p-6 text-center">
|
||||
<div class="flex max-w-md flex-col gap-3">
|
||||
<h1 class="font-semibold text-[18px]">Firefly III did not answer</h1>
|
||||
<p class="text-cladd-fg-soft">{session.failed}</p>
|
||||
<p class="flex justify-center gap-4">
|
||||
<a class="text-brand underline" href={page.url.pathname}>Try again</a>
|
||||
<a class="text-brand underline" data-sveltekit-reload href="/login"
|
||||
>Log in</a
|
||||
>
|
||||
<a
|
||||
class="text-brand underline"
|
||||
data-sveltekit-reload
|
||||
href={`${page.url.pathname}?ffui=old`}
|
||||
>Open the old interface</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
{:else}
|
||||
<main class="grid min-h-dvh place-items-center text-cladd-fg-soft">
|
||||
<span>Connecting to Firefly III</span>
|
||||
</main>
|
||||
{/if}
|
||||
</QueryClientProvider>
|
||||
+4
-12
@@ -1,13 +1,5 @@
|
||||
<svelte:head>
|
||||
<title>Firefly UI</title>
|
||||
</svelte:head>
|
||||
<script lang="ts">
|
||||
import PageHeader from "#lib/components/shell/PageHeader.svelte";
|
||||
</script>
|
||||
|
||||
<main
|
||||
class="flex min-h-dvh flex-col items-center justify-center gap-4 px-6 text-center bg-background text-foreground"
|
||||
>
|
||||
<h1 class="font-bold text-4xl tracking-tight sm:text-5xl">Firefly UI</h1>
|
||||
<p class="max-w-prose text-base text-muted-foreground">
|
||||
Edit
|
||||
<code class="font-mono">src/routes/+page.svelte</code>.
|
||||
</p>
|
||||
</main>
|
||||
<PageHeader subtitle="What's playing?" title="Dashboard" />
|
||||
@@ -0,0 +1,5 @@
|
||||
import { redirect } from "@sveltejs/kit";
|
||||
|
||||
export function load() {
|
||||
redirect(307, "/");
|
||||
}
|
||||
@@ -5,11 +5,111 @@
|
||||
@import "./claddcn.css";
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--ff-brand: oklch(0.57 0.105 240);
|
||||
--ff-brand-deep: oklch(0.5 0.1 240);
|
||||
--ff-on-brand: oklch(0.99 0.01 240);
|
||||
--ff-income: oklch(0.5 0.12 145);
|
||||
--ff-expense: oklch(0.52 0.15 28);
|
||||
--ff-transfer: oklch(0.5 0.085 225);
|
||||
--ff-warn: oklch(0.62 0.14 70);
|
||||
--ff-row-hover: oklch(0.96 0.012 240);
|
||||
--ff-row-cursor: oklch(0.93 0.03 240);
|
||||
--ff-row-selected: oklch(0.9 0.05 240);
|
||||
--ff-series-1: oklch(0.57 0.105 240);
|
||||
--ff-series-2: oklch(0.62 0.13 160);
|
||||
--ff-series-3: oklch(0.7 0.14 70);
|
||||
--ff-series-4: oklch(0.58 0.15 20);
|
||||
--ff-series-5: oklch(0.55 0.12 300);
|
||||
--ff-series-6: oklch(0.65 0.1 200);
|
||||
--ff-series-7: oklch(0.6 0.13 110);
|
||||
--ff-series-8: oklch(0.6 0.08 260);
|
||||
}
|
||||
|
||||
.cladd-color-brand {
|
||||
--cladd-theme: #3c8dbc;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--ff-brand: oklch(0.42 0.085 240);
|
||||
--ff-brand-deep: oklch(0.36 0.075 240);
|
||||
--ff-on-brand: oklch(0.97 0.01 240);
|
||||
--ff-income: oklch(0.78 0.15 140);
|
||||
--ff-expense: oklch(0.76 0.1 35);
|
||||
--ff-transfer: oklch(0.76 0.08 220);
|
||||
--ff-warn: oklch(0.8 0.13 80);
|
||||
--ff-row-hover: oklch(0.25 0.012 240);
|
||||
--ff-row-cursor: oklch(0.29 0.03 240);
|
||||
--ff-row-selected: oklch(0.33 0.06 240);
|
||||
--ff-series-1: oklch(0.7 0.1 240);
|
||||
--ff-series-2: oklch(0.72 0.12 160);
|
||||
--ff-series-3: oklch(0.8 0.13 75);
|
||||
--ff-series-4: oklch(0.7 0.13 25);
|
||||
--ff-series-5: oklch(0.7 0.11 300);
|
||||
--ff-series-6: oklch(0.75 0.09 200);
|
||||
--ff-series-7: oklch(0.75 0.12 115);
|
||||
--ff-series-8: oklch(0.72 0.07 260);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-brand: var(--ff-brand);
|
||||
--color-brand-deep: var(--ff-brand-deep);
|
||||
--color-on-brand: var(--ff-on-brand);
|
||||
--color-income: var(--ff-income);
|
||||
--color-expense: var(--ff-expense);
|
||||
--color-transfer: var(--ff-transfer);
|
||||
--color-warn: var(--ff-warn);
|
||||
--color-row-hover: var(--ff-row-hover);
|
||||
--color-row-cursor: var(--ff-row-cursor);
|
||||
--color-row-selected: var(--ff-row-selected);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
html {
|
||||
scrollbar-color: var(--cladd-fg-softest) transparent;
|
||||
}
|
||||
body {
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
::selection {
|
||||
background: color-mix(in oklch, var(--ff-brand) 28%, transparent);
|
||||
}
|
||||
:focus-visible {
|
||||
outline: 1.5px solid var(--ff-brand);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.dark :focus-visible {
|
||||
outline-color: var(--ff-series-1);
|
||||
}
|
||||
a {
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
@utility num {
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.01em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@utility kbd {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 1.35rem;
|
||||
height: 1.25rem;
|
||||
padding: 0 0.3rem;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
color: var(--cladd-fg-soft);
|
||||
background: var(--cladd-surface-cut);
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 -1px 0 var(--cladd-outline);
|
||||
}
|
||||
Reference in new issue
Block a user