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;