feat(render): enhance article rendering error handling & implement smooth theme transition

This commit is contained in:
ZhymabekRoman 2024-12-16 20:26:13 +05:00
parent 39b539a31c
commit 5ff68c22e6
9 changed files with 165 additions and 51 deletions

View file

@ -148,7 +148,35 @@ console.log('Hello, world!');
async def render_page(service_name: str):
return JSONResponse(content={"text": TEXT, "service_name": service_name})
return JSONResponse(
content={
"text": TEXT,
"service_name": service_name,
"article": {
"title": "UploadThing is 5x Faster",
"date": "2024-09-13T12:00:00Z",
"author": {
"name": "Theo Browne",
"role": "CEO @ Ping Labs",
"avatar": "https://picsum.photos/seed/post1/400/300",
},
"postImage": "https://picsum.photos/seed/postimage/1200/600",
"tableOfContents": [
{"id": "v7-is-here", "title": "V7 Is Here!"},
{"id": "benchmarks", "title": "Benchmarks"},
{"id": "the-road-to-v7", "title": "The Road To V7"},
{
"id": "uploadthing-has-served",
"title": "UploadThing Has Served...",
},
{
"id": "and-were-just-getting-started",
"title": "...and we're just getting started",
},
],
},
}
)
def register_render_router(router: APIRouter) -> None:

View file

@ -2,11 +2,6 @@
@tailwind components;
@tailwind utilities;
body,
html {
scroll-behavior: smooth;
}
@layer base {
:root {
--background: 0, 0%, 97%;
@ -71,6 +66,41 @@ html {
--ring: 240 4.9% 83.9%;
}
body,
html {
scroll-behavior: smooth;
}
html {
@apply transition-colors duration-200;
}
html.dark {
@apply text-gray-100 bg-gray-900;
}
html:not(.dark) {
@apply text-gray-900 bg-white;
}
* {
@apply transition-colors duration-200 border-border;
}
html,
body,
:root {
@apply transition-all duration-200;
}
/* Update existing html dark/light modes to include transition */
html.dark {
@apply text-gray-100 transition-all duration-200 bg-gray-900;
}
html:not(.dark) {
@apply text-gray-900 transition-all duration-200 bg-white;
}
}
@layer base {

View file

@ -1,4 +1,4 @@
<div class="w-full bg-yellow-400 text-center py-1 px-4">
<div class="w-full bg-yellow-400 text-center py-1 px-4 relative">
<p class="text-yellow-900">
Advertise here and support our project! Reach out to us at admin@freedium.cfd
</p>

View file

@ -11,12 +11,37 @@
import { Button } from '$lib/components/ui/button/index.js';
let isNavOpen = false;
let isSearchOpen = false;
let isHeaderVisible = true;
let lastScrollY = 0;
// Handle scroll events
function handleScroll() {
const currentScrollY = window.scrollY;
const documentHeight = document.documentElement.scrollHeight - window.innerHeight;
const scrollPercentage = (currentScrollY / documentHeight) * 100;
if (scrollPercentage > 15) {
isHeaderVisible = lastScrollY > currentScrollY;
} else {
isHeaderVisible = true;
}
lastScrollY = currentScrollY;
}
// Bind scroll event when component mounts
import { onMount } from 'svelte';
onMount(() => {
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
});
const toggleNav = () => {
isNavOpen = !isNavOpen;
};
let isSearchOpen = false;
const toggleSearch = () => {
isSearchOpen = !isSearchOpen;
};
@ -27,7 +52,8 @@
<nav
id="header"
class="sticky top-0 z-20 w-full border-b shadow-sm bg-white/95 backdrop-blur-sm dark:bg-zinc-900/95 border-zinc-200 dark:border-zinc-800"
class="sticky top-0 z-20 w-full transition-transform duration-300 border-b shadow-sm bg-white/95 backdrop-blur-sm dark:bg-zinc-900/95 border-zinc-200 dark:border-zinc-800"
style="transform: translateY({isHeaderVisible ? '0' : '-100%'})"
>
<ProgressLine />
@ -119,7 +145,12 @@
</div>
{/if}
<Advertise />
<div
class="transition-transform duration-300"
style="transform: translateY({isHeaderVisible ? '0' : '-100%'})"
>
<Advertise />
</div>
</nav>
<SearchDialog bind:open={isSearchOpen} />

View file

@ -3,7 +3,7 @@
import { fade } from 'svelte/transition';
import { writable } from 'svelte/store';
const MIN_DURATION = 400;
const MIN_DURATION = 200;
const isVisible = writable(false);
$: {

View file

@ -4,6 +4,8 @@
import ProgressOverlay from '$lib/elements/ProgressOverlay.svelte';
</script>
<ProgressOverlay />
<Toaster position="top-right" expand={true} />
<slot />
<div class="transition-all duration-200 ease-in-out">
<ProgressOverlay />
<Toaster position="top-right" expand={true} />
<slot />
</div>

View file

@ -137,6 +137,19 @@ export async function load({ params }) {
transformed = await render("medium");
} catch (err) {
console.error("Failed to render article:", err);
return {
slug: params.slug,
loading: false,
content: null,
article: null,
error: {
status: 500,
message: "Failed to render article",
code: ErrorCodes.RENDER_ERROR,
details:
process.env.NODE_ENV === "development" ? err.message : undefined,
},
};
}
if (!transformed) {
@ -164,7 +177,7 @@ export async function load({ params }) {
slug: params.slug,
loading: false,
content: code,
article: MOCK_ARTICLE,
article: transformed.article,
error: null,
};
} catch (compileError) {

View file

@ -2,7 +2,13 @@ import apiFetch from "@/api";
async function render(serviceName) {
const response = await apiFetch(`/services/${serviceName}/render`);
return response;
if (!response) {
throw new Error("Failed to fetch article");
}
return {
text: response.text,
article: response.article,
};
}
export { render };

View file

@ -1,66 +1,70 @@
import { fontFamily } from 'tailwindcss/defaultTheme';
import { addDynamicIconSelectors } from '@iconify/tailwind';
import { fontFamily } from "tailwindcss/defaultTheme";
import { addDynamicIconSelectors } from "@iconify/tailwind";
/** @type {import('tailwindcss').Config} */
const config = {
darkMode: ['class'],
content: ['./src/**/*.{html,js,svelte,ts}'],
safelist: ['dark'],
darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"],
safelist: ["dark"],
theme: {
container: {
center: true,
padding: '2rem',
padding: "2rem",
screens: {
'2xl': '1400px'
}
"2xl": "1400px",
},
},
extend: {
colors: {
border: 'hsla(var(--border))',
input: 'hsla(var(--input))',
ring: 'hsla(var(--ring))',
background: 'hsla(var(--background))',
foreground: 'hsla(var(--foreground))',
transitionProperty: {
colors:
"color, background-color, border-color, text-decoration-color, fill, stroke",
},
border: "hsla(var(--border))",
input: "hsla(var(--input))",
ring: "hsla(var(--ring))",
background: "hsla(var(--background))",
foreground: "hsla(var(--foreground))",
primary: {
DEFAULT: 'hsla(var(--primary))',
foreground: 'hsla(var(--primary-foreground))'
DEFAULT: "hsla(var(--primary))",
foreground: "hsla(var(--primary-foreground))",
},
secondary: {
DEFAULT: 'hsla(var(--secondary))',
foreground: 'hsla(var(--secondary-foreground))'
DEFAULT: "hsla(var(--secondary))",
foreground: "hsla(var(--secondary-foreground))",
},
destructive: {
DEFAULT: 'hsla(var(--destructive))',
foreground: 'hsla(var(--destructive-foreground))'
DEFAULT: "hsla(var(--destructive))",
foreground: "hsla(var(--destructive-foreground))",
},
muted: {
DEFAULT: 'hsla(var(--muted))',
foreground: 'hsla(var(--muted-foreground))'
DEFAULT: "hsla(var(--muted))",
foreground: "hsla(var(--muted-foreground))",
},
accent: {
DEFAULT: 'hsla(var(--accent))',
foreground: 'hsla(var(--accent-foreground))'
DEFAULT: "hsla(var(--accent))",
foreground: "hsla(var(--accent-foreground))",
},
popover: {
DEFAULT: 'hsla(var(--popover))',
foreground: 'hsla(var(--popover-foreground))'
DEFAULT: "hsla(var(--popover))",
foreground: "hsla(var(--popover-foreground))",
},
card: {
DEFAULT: 'hsla(var(--card))',
foreground: 'hsla(var(--card-foreground))'
}
DEFAULT: "hsla(var(--card))",
foreground: "hsla(var(--card-foreground))",
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: [...fontFamily.sans]
}
}
sans: [...fontFamily.sans],
},
},
},
plugins: [addDynamicIconSelectors()]
plugins: [addDynamicIconSelectors()],
};
export default config;