diff --git a/freedium-library/src/freedium_library/api/handlers/render.py b/freedium-library/src/freedium_library/api/handlers/render.py index 05eb8fb..b3d89c3 100644 --- a/freedium-library/src/freedium_library/api/handlers/render.py +++ b/freedium-library/src/freedium_library/api/handlers/render.py @@ -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: diff --git a/new-web/src/app.css b/new-web/src/app.css index 05539f5..825f323 100644 --- a/new-web/src/app.css +++ b/new-web/src/app.css @@ -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 { diff --git a/new-web/src/lib/elements/Advertise.svelte b/new-web/src/lib/elements/Advertise.svelte index e3a5847..01ff08c 100644 --- a/new-web/src/lib/elements/Advertise.svelte +++ b/new-web/src/lib/elements/Advertise.svelte @@ -1,4 +1,4 @@ -
+

Advertise here and support our project! Reach out to us at admin@freedium.cfd

diff --git a/new-web/src/lib/elements/Header.svelte b/new-web/src/lib/elements/Header.svelte index 9373ea4..13bb568 100644 --- a/new-web/src/lib/elements/Header.svelte +++ b/new-web/src/lib/elements/Header.svelte @@ -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 @@
{/if} - +
+ +
diff --git a/new-web/src/lib/elements/ProgressOverlay.svelte b/new-web/src/lib/elements/ProgressOverlay.svelte index e87fe5a..1118195 100644 --- a/new-web/src/lib/elements/ProgressOverlay.svelte +++ b/new-web/src/lib/elements/ProgressOverlay.svelte @@ -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); $: { diff --git a/new-web/src/routes/+layout.svelte b/new-web/src/routes/+layout.svelte index d4621ed..0e2b306 100644 --- a/new-web/src/routes/+layout.svelte +++ b/new-web/src/routes/+layout.svelte @@ -4,6 +4,8 @@ import ProgressOverlay from '$lib/elements/ProgressOverlay.svelte'; - - - +
+ + + +
diff --git a/new-web/src/routes/[slug]/+page.server.js b/new-web/src/routes/[slug]/+page.server.js index b4300f2..6260fc1 100644 --- a/new-web/src/routes/[slug]/+page.server.js +++ b/new-web/src/routes/[slug]/+page.server.js @@ -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) { diff --git a/new-web/src/services/render.js b/new-web/src/services/render.js index 8ae2f53..fcf4a75 100644 --- a/new-web/src/services/render.js +++ b/new-web/src/services/render.js @@ -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 }; diff --git a/new-web/tailwind.config.js b/new-web/tailwind.config.js index 9228ccf..991dcf5 100644 --- a/new-web/tailwind.config.js +++ b/new-web/tailwind.config.js @@ -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;