From c095c039a5c3de6994deec1db31e190652d0f4e8 Mon Sep 17 00:00:00 2001 From: Ibrahim Isa Jajere Date: Sat, 18 Apr 2026 21:11:12 +0100 Subject: [PATCH 001/233] fix(expo/ai): resolve apple intelligence availability check error --- apps/expo/features/ai/lib/localModelManager.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/expo/features/ai/lib/localModelManager.ts b/apps/expo/features/ai/lib/localModelManager.ts index 76e4c4395e..68a4281553 100644 --- a/apps/expo/features/ai/lib/localModelManager.ts +++ b/apps/expo/features/ai/lib/localModelManager.ts @@ -20,7 +20,6 @@ import { } from '../atoms/aiModeAtoms'; import { LLAMA_MODEL_ID, LLAMA_MODEL_SIZE_BYTES } from './constants'; -import { createLocalTools } from './tools'; const LLAMA_MODEL_FILENAME = 'SmolLM3-Q4_K_M.gguf'; const LLAMA_MODELS_DIR = `${RNBlobUtil.fs.dirs.DocumentDir}/llama-models`; @@ -57,9 +56,12 @@ function getAppleModule() { if (appleModule) return appleModule; try { - appleModule = import('@react-native-ai/apple'); + // require() is synchronous — import() returns a Promise, which breaks + // the synchronous callers (isAppleIntelligenceAvailable, etc.) + appleModule = require('@react-native-ai/apple'); return appleModule; - } catch { + } catch (err) { + console.error('Failed to load Apple module:', err); return null; } } @@ -246,7 +248,7 @@ async function _initAppleModel(): Promise { if (!mod) throw new Error('Apple module not available'); appleModel = mod.apple(); - appleModel.updateTools(createLocalTools()); + // appleModel.updateTools(createLocalTools()); store.set(localModelStatusAtom, 'ready'); } catch { store.set(localModelStatusAtom, 'error'); From 0c44ac4fe7df59425b41abdc8536c093877dbd16 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:01:11 +0000 Subject: [PATCH 002/233] fix: improve Lighthouse LCP scores for guides and landing apps Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/396e79f1-4bef-4512-802b-b8f182cd9250 Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com> --- apps/guides/app/page.tsx | 117 ++---------------- apps/guides/components/guides-content.tsx | 113 +++++++++++++++++ .../components/providers/query-provider.tsx | 8 +- .../components/sections/landing-hero.tsx | 21 ++-- apps/landing/components/ui/device-mockup.tsx | 9 +- 5 files changed, 130 insertions(+), 138 deletions(-) create mode 100644 apps/guides/components/guides-content.tsx diff --git a/apps/guides/app/page.tsx b/apps/guides/app/page.tsx index 199b11581c..e9da6afc03 100644 --- a/apps/guides/app/page.tsx +++ b/apps/guides/app/page.tsx @@ -1,55 +1,14 @@ -'use client'; - import { Button } from '@packrat/web-ui/components/button'; -import CategoryFilter from 'guides-app/components/category-filter'; -import FeaturedGuides from 'guides-app/components/featured-guides'; -import GuideCard from 'guides-app/components/guide-card'; -import { getAllCategories } from 'guides-app/lib/categories'; -import { featuresConfig } from 'guides-app/lib/config'; -import { getAllPosts } from 'guides-app/lib/mdx-static'; +import GuidesContent from 'guides-app/components/guides-content'; import Link from 'next/link'; -import { useSearchParams } from 'next/navigation'; import { Suspense } from 'react'; -function HomeContent() { - const searchParams = useSearchParams() ?? new URLSearchParams(); - const category = searchParams.get('category'); - const search = searchParams.get('search'); - - const allPosts = getAllPosts(); - const categories = getAllCategories(); - - // Filter posts by category if provided - let filteredPosts = allPosts; - - if (category) { - filteredPosts = allPosts.filter((post) => post.categories?.includes(category as string)); - } else if (search) { - const searchQuery = search.toLowerCase(); - filteredPosts = allPosts.filter((post) => { - const searchContent = `${post.title} ${ - post.description - } ${post.categories?.join(' ')}`.toLowerCase(); - return searchContent.includes(searchQuery); - }); - } - - // Get featured guides (most recent 3) - const featuredGuides = allPosts.slice(0, 3); - - // Determine the page title based on search params - let pageTitle = 'All Guides'; - if (search) { - pageTitle = `Search Results for "${search}"`; - } else if (category) { - pageTitle = `${category.charAt(0).toUpperCase() + category.slice(1)} Guides`; - } - +export default function Home() { return (
- {/* Hero Section - Apple style */} + {/* Hero Section — server-rendered for fast LCP */}
-
+

PackRat Guides @@ -72,70 +31,10 @@ function HomeContent() {

- {/* Features Section - Apple style */} - {!category && !search && ( - <> -
-
-
- {featuresConfig.map((feature) => ( -
-
- -
-

{feature.title}

-

{feature.description}

-
- ))} -
-
-
- - {/* Featured Guides - Apple style */} -
-
-

- Featured Guides -

- -
-
- - )} - - {/* All Guides - Apple style */} -
-
-

{pageTitle}

- - - - - -
- {filteredPosts.length > 0 ? ( - filteredPosts.map((post) => ) - ) : ( -
-

- No guides found. Try a different search or category. -

- -
- )} -
-
-
+ {/* Dynamic content — rendered client-side (uses useSearchParams) */} + + +
); } - -export default function Home() { - return ( - - - - ); -} diff --git a/apps/guides/components/guides-content.tsx b/apps/guides/components/guides-content.tsx new file mode 100644 index 0000000000..0a296eb06a --- /dev/null +++ b/apps/guides/components/guides-content.tsx @@ -0,0 +1,113 @@ +'use client'; + +import { Button } from '@packrat/web-ui/components/button'; +import CategoryFilter from 'guides-app/components/category-filter'; +import FeaturedGuides from 'guides-app/components/featured-guides'; +import GuideCard from 'guides-app/components/guide-card'; +import { getAllCategories } from 'guides-app/lib/categories'; +import { featuresConfig } from 'guides-app/lib/config'; +import { getAllPosts } from 'guides-app/lib/mdx-static'; +import Link from 'next/link'; +import { useSearchParams } from 'next/navigation'; +import { Suspense } from 'react'; + +function GuidesContentInner() { + const searchParams = useSearchParams() ?? new URLSearchParams(); + const category = searchParams.get('category'); + const search = searchParams.get('search'); + + const allPosts = getAllPosts(); + const categories = getAllCategories(); + + let filteredPosts = allPosts; + + if (category) { + filteredPosts = allPosts.filter((post) => post.categories?.includes(category as string)); + } else if (search) { + const searchQuery = search.toLowerCase(); + filteredPosts = allPosts.filter((post) => { + const searchContent = `${post.title} ${ + post.description + } ${post.categories?.join(' ')}`.toLowerCase(); + return searchContent.includes(searchQuery); + }); + } + + const featuredGuides = allPosts.slice(0, 3); + + let pageTitle = 'All Guides'; + if (search) { + pageTitle = `Search Results for "${search}"`; + } else if (category) { + pageTitle = `${category.charAt(0).toUpperCase() + category.slice(1)} Guides`; + } + + return ( + <> + {/* Features Section - Apple style */} + {!category && !search && ( + <> +
+
+
+ {featuresConfig.map((feature) => ( +
+
+ +
+

{feature.title}

+

{feature.description}

+
+ ))} +
+
+
+ + {/* Featured Guides - Apple style */} +
+
+

+ Featured Guides +

+ +
+
+ + )} + + {/* All Guides - Apple style */} +
+
+

{pageTitle}

+ + + + + +
+ {filteredPosts.length > 0 ? ( + filteredPosts.map((post) => ) + ) : ( +
+

+ No guides found. Try a different search or category. +

+ +
+ )} +
+
+
+ + ); +} + +export default function GuidesContent() { + return ( + + + + ); +} diff --git a/apps/guides/components/providers/query-provider.tsx b/apps/guides/components/providers/query-provider.tsx index bd57484629..735dff1770 100644 --- a/apps/guides/components/providers/query-provider.tsx +++ b/apps/guides/components/providers/query-provider.tsx @@ -1,7 +1,6 @@ 'use client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { type ReactNode, useState } from 'react'; export function QueryProvider({ children }: { children: ReactNode }) { @@ -17,10 +16,5 @@ export function QueryProvider({ children }: { children: ReactNode }) { }), ); - return ( - - {children} - - - ); + return {children}; } diff --git a/apps/landing/components/sections/landing-hero.tsx b/apps/landing/components/sections/landing-hero.tsx index 18ea36eed7..d8e55aede2 100644 --- a/apps/landing/components/sections/landing-hero.tsx +++ b/apps/landing/components/sections/landing-hero.tsx @@ -18,9 +18,8 @@ export default function LandingHero() { }; const containerVariants = { - hidden: { opacity: 0 }, + hidden: {}, visible: { - opacity: 1, transition: { staggerChildren: 0.1, delayChildren: 0.2 }, }, }; @@ -56,24 +55,18 @@ export default function LandingHero() { - {/* Heading */} - + {/* Heading — rendered immediately for LCP */} +

{siteConfig.hero.titleLine1} {siteConfig.hero.titleLine2} - +

- {/* Subtitle */} - + {/* Subtitle — rendered immediately for LCP */} +

{siteConfig.hero.subtitle} - +

{/* CTA buttons */} { - setMounted(true); - }, []); - return ( // biome-ignore lint/a11y/noStaticElementInteractions: ignore
setIsHovered(true)} From 05d4da60d8bffe4b4107cd2c37454ee1bca4d47b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Apr 2026 02:31:00 +0000 Subject: [PATCH 003/233] refactor: replace Framer Motion with Tailwind CSS animations, remove unnecessary 'use client' directives Agent-Logs-Url: https://github.com/PackRat-AI/PackRat/sessions/4c56e6bb-bbc6-4033-9d86-d72ec9deb619 Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com> --- apps/guides/components/footer.tsx | 9 +- apps/guides/components/header.tsx | 7 +- apps/guides/styles/globals.css | 5 +- apps/landing/components/app-preview.tsx | 28 ++--- .../components/sections/landing-hero.tsx | 101 ++++++------------ apps/landing/components/site-footer.tsx | 15 --- apps/landing/components/ui/device-mockup.tsx | 29 +---- apps/landing/package.json | 2 - apps/landing/styles/globals.css | 5 +- apps/landing/tailwind.config.ts | 20 ++++ 10 files changed, 76 insertions(+), 145 deletions(-) diff --git a/apps/guides/components/footer.tsx b/apps/guides/components/footer.tsx index cad9e5bc62..0103181539 100644 --- a/apps/guides/components/footer.tsx +++ b/apps/guides/components/footer.tsx @@ -1,6 +1,3 @@ -'use client'; - -import { useQuery } from '@tanstack/react-query'; import { assertDefined } from 'guides-app/lib/assertDefined'; import { getAllCategories } from 'guides-app/lib/categories'; import { footerConfig, siteConfig } from 'guides-app/lib/config'; @@ -8,11 +5,7 @@ import { Backpack, Globe } from 'lucide-react'; import Link from 'next/link'; export default function Footer() { - // Fetch categories using TanStack Query - const { data: categories = [] } = useQuery({ - queryKey: ['categories'], - queryFn: getAllCategories, - }); + const categories = getAllCategories(); const company = footerConfig.mainSections[1]; assertDefined(company); diff --git a/apps/guides/components/header.tsx b/apps/guides/components/header.tsx index 988e6405e7..a2c508011b 100644 --- a/apps/guides/components/header.tsx +++ b/apps/guides/components/header.tsx @@ -8,7 +8,6 @@ import { } from '@packrat/web-ui/components/dropdown-menu'; import { Sheet, SheetContent, SheetTrigger } from '@packrat/web-ui/components/sheet'; import { cn } from '@packrat/web-ui/lib/utils'; -import { useQuery } from '@tanstack/react-query'; import { getAllCategories } from 'guides-app/lib/categories'; import { navigationConfig, siteConfig } from 'guides-app/lib/config'; import { Backpack, ChevronDown } from 'lucide-react'; @@ -20,11 +19,7 @@ import { ThemeToggle } from './theme-toggle'; export default function Header() { const [scrolled, setScrolled] = useState(false); - // Fetch categories using TanStack Query - const { data: categories = [] } = useQuery({ - queryKey: ['categories'], - queryFn: getAllCategories, - }); + const categories = getAllCategories(); // Add scroll listener useEffect(() => { diff --git a/apps/guides/styles/globals.css b/apps/guides/styles/globals.css index ac6844236c..b23e1032a2 100644 --- a/apps/guides/styles/globals.css +++ b/apps/guides/styles/globals.css @@ -13,7 +13,10 @@ body { } @layer base { - :root { + html { + scroll-behavior: smooth; + } + --background: 0 0% 100%; --foreground: 0 0% 3.9%; --card: 0 0% 100%; diff --git a/apps/landing/components/app-preview.tsx b/apps/landing/components/app-preview.tsx index eb48935d54..a3c1b7eaa5 100644 --- a/apps/landing/components/app-preview.tsx +++ b/apps/landing/components/app-preview.tsx @@ -1,7 +1,5 @@ 'use client'; -import { AnimatePresence, motion } from 'framer-motion'; -import { assertDefined } from 'landing-app/lib/typeAssertions'; import Image from 'next/image'; import { useEffect, useState } from 'react'; @@ -29,28 +27,24 @@ export default function AppPreview() { return () => clearInterval(interval); }, [screens.length]); - assertDefined(screens[currentScreen]); - return ( <> - - ( +
{screens[currentScreen].alt} - - +
+ ))}
{screens.map((_, index) => (
{/* Device mockup column */} - {/* Floating cards – Apple style: clean card with subtle shadow */} -
@@ -167,13 +130,11 @@ export default function LandingHero() {
-
+ -
@@ -186,8 +147,8 @@ export default function LandingHero() {
-
- + + diff --git a/apps/landing/components/site-footer.tsx b/apps/landing/components/site-footer.tsx index 0e52ad3ddc..efc8e9e153 100644 --- a/apps/landing/components/site-footer.tsx +++ b/apps/landing/components/site-footer.tsx @@ -1,21 +1,9 @@ -'use client'; - import { siteConfig } from 'landing-app/config/site'; import { LucideIcon, TikTokIcon } from 'landing-app/lib/icons'; import { Backpack } from 'lucide-react'; import Link from 'next/link'; -import type React from 'react'; export default function SiteFooter() { - const scrollToSection = (e: React.MouseEvent, href: string) => { - e.preventDefault(); - const targetId = href.substring(1); - const element = document.getElementById(targetId); - if (element) { - element.scrollIntoView({ behavior: 'smooth' }); - } - }; - return (