diff --git a/AGENTS.md b/AGENTS.md index 9384539..b061495 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ Your content is instantly available to browsers, LLMs, and AI agents.. Write mar - **Total Posts**: 17 - **Total Pages**: 5 - **Latest Post**: 2025-12-29 -- **Last Updated**: 2025-12-30T08:48:17.735Z +- **Last Updated**: 2025-12-30T20:03:38.734Z ## Tech stack diff --git a/CLAUDE.md b/CLAUDE.md index b9ac7e2..b138588 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,6 +4,9 @@ Project instructions for Claude Code. ## Project context + + + Markdown sync framework. Write markdown in `content/`, run sync commands, content appears instantly via Convex real-time database. Built for developers and AI agents. ## Quick start diff --git a/content/pages/changelog-page.md b/content/pages/changelog-page.md index 214e373..cbfb0ff 100644 --- a/content/pages/changelog-page.md +++ b/content/pages/changelog-page.md @@ -3,6 +3,7 @@ title: "Changelog" slug: "changelog" published: true order: 5 +rightSidebar: true layout: "sidebar" --- @@ -190,6 +191,7 @@ Released December 29, 2025 - Works with all four themes (dark, light, tan, cloud) Updated files: `src/components/ContactForm.tsx`, `src/components/NewsletterSignup.tsx` + - Honeypot fields use CSS positioning (position: absolute, left: -9999px) to hide from users - Fields include aria-hidden="true" and tabIndex={-1} for accessibility - Different field names per form (website/fax) to avoid pattern detection diff --git a/public/llms.txt b/public/llms.txt index 4faa36c..859ae91 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -1,6 +1,6 @@ # llms.txt - Information for AI assistants and LLMs # Learn more: https://llmstxt.org/ -# Last updated: 2025-12-30T08:48:17.736Z +# Last updated: 2025-12-30T20:03:38.736Z > Your content is instantly available to browsers, LLMs, and AI agents. diff --git a/public/raw/changelog.md b/public/raw/changelog.md index 16f55b6..41bdff0 100644 --- a/public/raw/changelog.md +++ b/public/raw/changelog.md @@ -189,6 +189,7 @@ Released December 29, 2025 - Works with all four themes (dark, light, tan, cloud) Updated files: `src/components/ContactForm.tsx`, `src/components/NewsletterSignup.tsx` + - Honeypot fields use CSS positioning (position: absolute, left: -9999px) to hide from users - Fields include aria-hidden="true" and tabIndex={-1} for accessibility - Different field names per form (website/fax) to avoid pattern detection diff --git a/src/App.tsx b/src/App.tsx index 565bdc4..1bbc2ce 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import NewsletterAdmin from "./pages/NewsletterAdmin"; import Dashboard from "./pages/Dashboard"; import Callback from "./pages/Callback"; import Layout from "./components/Layout"; +import ScrollToTopOnNav from "./components/ScrollToTopOnNav"; import { usePageTracking } from "./hooks/usePageTracking"; import { SidebarProvider } from "./context/SidebarContext"; import siteConfig from "./config/siteConfig"; @@ -45,6 +46,7 @@ function App() { return ( + {/* Homepage route - either default Home or custom page/post */} diff --git a/src/components/AIChatView.tsx b/src/components/AIChatView.tsx index a53822b..deb5133 100644 --- a/src/components/AIChatView.tsx +++ b/src/components/AIChatView.tsx @@ -99,10 +99,15 @@ export default function AIChatView({ initChat(); }, [sessionId, contextId, getOrCreateChat]); - // Auto-scroll to bottom when new messages arrive + // Auto-scroll to bottom when new messages arrive (only if there are messages) useEffect(() => { - if (messagesEndRef.current) { - messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); + // Only scroll if there are actual messages (don't scroll on initial empty state) + if (messagesEndRef.current && chat?.messages && chat.messages.length > 0) { + // Use scrollTo on the parent container instead of scrollIntoView to avoid page scroll + const messagesContainer = messagesEndRef.current.parentElement; + if (messagesContainer) { + messagesContainer.scrollTop = messagesContainer.scrollHeight; + } } }, [chat?.messages]); diff --git a/src/components/ScrollToTopOnNav.tsx b/src/components/ScrollToTopOnNav.tsx new file mode 100644 index 0000000..38223e5 --- /dev/null +++ b/src/components/ScrollToTopOnNav.tsx @@ -0,0 +1,22 @@ +import { useLayoutEffect } from "react"; +import { useLocation } from "react-router-dom"; + +/** + * Scrolls to top of page on route changes + * Skips scroll if navigating to a hash anchor + * Uses useLayoutEffect to run before browser paint + */ +export default function ScrollToTopOnNav() { + const { pathname, hash } = useLocation(); + + // useLayoutEffect runs synchronously before browser paint + useLayoutEffect(() => { + // Skip if navigating to a hash anchor + if (hash) return; + + // Scroll to top immediately (before paint) + window.scrollTo(0, 0); + }, [pathname, hash]); + + return null; +} diff --git a/src/main.tsx b/src/main.tsx index 4f8a097..d273dd3 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,6 +7,11 @@ import { FontProvider } from "./context/FontContext"; import { isWorkOSConfigured } from "./utils/workos"; import "./styles/global.css"; +// Disable browser scroll restoration to prevent scroll position being restored on navigation +if ("scrollRestoration" in window.history) { + window.history.scrollRestoration = "manual"; +} + const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL); // Lazy load the appropriate App wrapper based on WorkOS configuration