@goldlabelapps/theme 3.2.8 → 3.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +2 -2
  2. package/dist/components/RenderMarkdown.js +7 -2
  3. package/dist/components/download/DownloadBanner.js +1 -8
  4. package/dist/components/footer/Footer.js +6 -3
  5. package/dist/components/footer/FooterWrapper.d.ts +7 -0
  6. package/dist/components/footer/FooterWrapper.js +15 -0
  7. package/dist/components/footer/NXContactColumn.d.ts +13 -0
  8. package/dist/components/footer/NXContactColumn.js +32 -0
  9. package/dist/components/footer/NXLiveApps.d.ts +22 -0
  10. package/dist/components/footer/NXLiveApps.js +87 -0
  11. package/dist/components/footer/NXSocialColumn.d.ts +14 -0
  12. package/dist/components/footer/NXSocialColumn.js +17 -0
  13. package/dist/components/footer/NXTagsColumn.d.ts +14 -0
  14. package/dist/components/footer/NXTagsColumn.js +31 -0
  15. package/dist/components/footer/index.d.ts +6 -0
  16. package/dist/components/footer/index.js +6 -0
  17. package/dist/components/hero/HeroSection.js +1 -1
  18. package/dist/components/hero/VideoModal.js +1 -1
  19. package/dist/components/index.d.ts +11 -3
  20. package/dist/components/index.js +11 -3
  21. package/dist/components/layouts/GuideBreadcrumbs.d.ts +14 -0
  22. package/dist/components/layouts/GuideBreadcrumbs.js +15 -0
  23. package/dist/components/{guide → layouts}/MarkdownContent.js +1 -1
  24. package/dist/components/layouts/Standard.d.ts +10 -0
  25. package/dist/components/layouts/Standard.js +7 -0
  26. package/dist/components/navigation/DropdownMenu.js +1 -1
  27. package/dist/components/navigation/Header.js +3 -6
  28. package/dist/components/navigation/LayoutActions.d.ts +7 -0
  29. package/dist/components/navigation/LayoutActions.js +12 -0
  30. package/dist/components/navigation/LogoContextMenu.js +1 -1
  31. package/dist/components/navigation/MarkdownMenu.d.ts +9 -0
  32. package/dist/components/navigation/MarkdownMenu.js +12 -0
  33. package/dist/components/navigation/MobileMenu.js +1 -1
  34. package/dist/components/navigation/Waiting.d.ts +10 -0
  35. package/dist/components/navigation/Waiting.js +45 -0
  36. package/dist/components/search/HeroSearchBar.d.ts +16 -0
  37. package/dist/components/search/HeroSearchBar.js +38 -0
  38. package/dist/components/search/SearchDialog.d.ts +11 -0
  39. package/dist/components/search/SearchDialog.js +69 -0
  40. package/dist/components/search/SearchIconButton.d.ts +10 -0
  41. package/dist/components/search/SearchIconButton.js +10 -0
  42. package/dist/components/search/SearchResultsList.d.ts +13 -0
  43. package/dist/components/search/SearchResultsList.js +18 -0
  44. package/dist/components/search/index.d.ts +5 -0
  45. package/dist/components/search/index.js +5 -0
  46. package/dist/components/search/useSearch.d.ts +54 -0
  47. package/dist/components/search/useSearch.js +106 -0
  48. package/dist/components/shortcodes/PageLinkShortcode.d.ts +15 -0
  49. package/dist/components/shortcodes/PageLinkShortcode.js +51 -0
  50. package/dist/components/showcase/BlogSection.js +2 -2
  51. package/dist/components/showcase/FeatureExplorer.js +2 -2
  52. package/dist/components/showcase/FeaturedCard.d.ts +13 -0
  53. package/dist/components/showcase/FeaturedCard.js +6 -0
  54. package/dist/components/showcase/SolutionsSection.js +2 -2
  55. package/dist/components/showcase/StatementSection.js +1 -1
  56. package/dist/components/showcase/VideoSection.js +2 -2
  57. package/dist/components/ui/Bash.d.ts +16 -0
  58. package/dist/components/ui/Bash.js +30 -0
  59. package/dist/components/ui/Button.js +1 -1
  60. package/dist/components/ui/ThemeToggle.d.ts +5 -1
  61. package/dist/components/ui/ThemeToggle.js +32 -2
  62. package/dist/components/video/VideoLayout.js +2 -2
  63. package/dist/context/ThemeContext.d.ts +4 -0
  64. package/dist/context/ThemeContext.js +22 -17
  65. package/dist/data/og-images.json +205 -151
  66. package/dist/design-system/components/DesignSystemProvider/DesignSystemProvider.js +3 -8
  67. package/dist/design-system/components/buttons/Button/Button.js +6 -3
  68. package/dist/design-system/components/buttons/IconButton/IconButton.js +6 -3
  69. package/dist/design-system/components/feedback/Alert/Alert.js +3 -2
  70. package/dist/design-system/components/feedback/Card/Card.js +5 -3
  71. package/dist/design-system/components/headings/Heading/Heading.js +2 -1
  72. package/dist/design-system/components/images/FeaturedImage/FeaturedImage.js +6 -6
  73. package/dist/design-system/components/layout/index.d.ts +1 -0
  74. package/dist/design-system/components/layout/index.js +1 -0
  75. package/dist/design-system/components/navigation/SiteNav/SiteNav.js +1 -1
  76. package/dist/design-system/components/nxStyle.d.ts +0 -21
  77. package/dist/design-system/components/nxStyle.js +0 -14
  78. package/dist/design-system/types.d.ts +1 -0
  79. package/dist/design-system/types.js +1 -0
  80. package/dist/index.d.ts +4 -3
  81. package/dist/index.js +1 -0
  82. package/dist/styles/globals.css +10 -12
  83. package/dist/styles/theme.js +30 -3
  84. package/dist/types/index.d.ts +220 -0
  85. package/package.json +3 -2
  86. package/dist/components/guide/GuideLayout.d.ts +0 -8
  87. package/dist/components/guide/GuideLayout.js +0 -12
  88. /package/dist/components/{guide → layouts}/MarkdownContent.d.ts +0 -0
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { LayoutDashboard, ExternalLink } from "lucide-react";
4
+ import { useSiteConfig } from "../../context/ConfigContext";
5
+ import { ShareMenu } from "./ShareMenu";
6
+ import { MobileMenu } from "./MobileMenu";
7
+ import { Button } from "../ui/Button";
8
+ import { cn } from "../../lib/utils";
9
+ export function LayoutActions({ mobileMenuOpen, setMobileMenuOpen, className, }) {
10
+ const siteConfig = useSiteConfig();
11
+ return (_jsxs("div", { className: cn("flex items-center gap-2.5 sm:gap-3", className), children: [_jsx(ShareMenu, {}), siteConfig.navigation.remoteControlBadge?.enabled && (_jsxs("a", { href: siteConfig.navigation.remoteControlBadge.href, target: "_blank", rel: "noopener noreferrer", title: siteConfig.navigation.remoteControlBadge.tooltip, className: "hidden sm:inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold text-slate-700 bg-slate-100/80 hover:bg-[#012867]/[0.08] hover:text-[#012867] border border-slate-200/80 transition-colors", children: [_jsx(LayoutDashboard, { className: "h-3.5 w-3.5 text-slate-500" }), _jsx("span", { children: siteConfig.navigation.remoteControlBadge.label }), _jsx(ExternalLink, { className: "h-3 w-3 text-slate-400" })] })), _jsx(Button, { href: siteConfig.navigation.primaryCta.href, variant: "primary", size: "sm", icon: siteConfig.navigation.primaryCta.icon, iconPosition: "right", className: "shadow-sm shadow-[#012867]/20 font-bold bg-[#012867] hover:bg-[#011f52] text-white", children: _jsx("span", { children: siteConfig.navigation.primaryCta.label }) }), _jsx(MobileMenu, { isOpen: mobileMenuOpen, onToggle: () => setMobileMenuOpen((prev) => !prev), onClose: () => setMobileMenuOpen(false) })] }));
12
+ }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useRef } from "react";
4
- import Link from "next/link";
4
+ import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
5
5
  import Image from "next/image";
6
6
  import { Copy, Check, FileText } from "lucide-react";
7
7
  import { useSiteConfig } from "../../context/ConfigContext";
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { GuideMeta } from "../../types";
3
+ export interface MarkdownMenuProps {
4
+ guides: GuideMeta[];
5
+ currentGuide?: GuideMeta;
6
+ title?: string;
7
+ basePath?: string;
8
+ }
9
+ export declare function MarkdownMenu({ guides, currentGuide, title, basePath, }: MarkdownMenuProps): React.JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
4
+ import { BookOpen } from "lucide-react";
5
+ export function MarkdownMenu({ guides, currentGuide, title = "How To Guides", basePath = "/how-to", }) {
6
+ return (_jsxs("div", { className: "p-4 rounded-2xl bg-slate-50 border border-red-500", children: [_jsxs("div", { className: "flex items-center gap-2 mb-4 text-[#012867] font-bold text-sm", children: [_jsx(BookOpen, { className: "h-4 w-4" }), _jsx("span", { children: title })] }), _jsx("ul", { className: "space-y-1", children: guides.map((g) => {
7
+ const isActive = currentGuide?.slug === g.slug;
8
+ return (_jsx("li", { children: _jsx(Link, { href: `${basePath}${g.slug === "/" ? "" : g.slug}`, className: `block px-3 py-2 rounded-xl text-xs font-semibold transition-colors ${isActive
9
+ ? "bg-slate-200 text-[#012867]"
10
+ : "text-slate-700 hover:bg-slate-200/70"}`, children: g.title }) }, g.slug));
11
+ }) })] }));
12
+ }
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useSyncExternalStore } from "react";
4
4
  import { createPortal } from "react-dom";
5
- import Link from "next/link";
5
+ import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
6
6
  import { Menu, X, ChevronDown, ExternalLink } from "lucide-react";
7
7
  import { useSiteConfig } from "../../context/ConfigContext";
8
8
  import { Button } from "../ui/Button";
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ export interface WaitingProps {
3
+ /** Optional container class name override */
4
+ className?: string;
5
+ /** Safety timeout in ms before auto-dismissing (default 4000ms). Pass 0 to disable. */
6
+ timeout?: number;
7
+ }
8
+ export declare function WaitingContent({ className, timeout }: WaitingProps): React.JSX.Element | null;
9
+ export declare function Waiting(props: WaitingProps): React.JSX.Element;
10
+ export default Waiting;
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Suspense, useEffect, useState } from "react";
4
+ import { createPortal } from "react-dom";
5
+ import { Flash } from "@goldlabelapps/flash";
6
+ import { useSlice } from "@goldlabelapps/uberedux";
7
+ export function WaitingContent({ className, timeout = 4000 }) {
8
+ const [mounted, setMounted] = useState(false);
9
+ const [timedOut, setTimedOut] = useState(false);
10
+ useEffect(() => {
11
+ setMounted(true);
12
+ }, []);
13
+ let isWaiting = false;
14
+ try {
15
+ const slice = useSlice();
16
+ isWaiting = Boolean(slice?.waiting || slice?.loadingOverlay || slice?.navigation?.isNavigating);
17
+ }
18
+ catch {
19
+ isWaiting = false;
20
+ }
21
+ useEffect(() => {
22
+ if (!isWaiting) {
23
+ setTimedOut(false);
24
+ return;
25
+ }
26
+ if (timeout > 0) {
27
+ const timer = setTimeout(() => {
28
+ setTimedOut(true);
29
+ }, timeout);
30
+ return () => clearTimeout(timer);
31
+ }
32
+ }, [isWaiting, timeout]);
33
+ if (!isWaiting || timedOut)
34
+ return null;
35
+ const content = (_jsx("div", { role: "dialog", "aria-modal": "true", "aria-label": "Loading page", onClick: () => setTimedOut(true), style: { zIndex: 999999 }, className: className ||
36
+ "fixed inset-0 z-[999999] flex items-center justify-center bg-white/80 dark:bg-slate-950/80 backdrop-blur-sm transition-opacity duration-200 pointer-events-auto", children: _jsx(Flash, { movie: "NXLoading", width: 320, height: 320, color: "transparent", loop: true, autoPlay: true }) }));
37
+ if (mounted && typeof document !== "undefined") {
38
+ return createPortal(content, document.body);
39
+ }
40
+ return content;
41
+ }
42
+ export function Waiting(props) {
43
+ return (_jsx(Suspense, { fallback: null, children: _jsx(WaitingContent, { ...props }) }));
44
+ }
45
+ export default Waiting;
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import { UseSearchOptions } from "./useSearch";
3
+ export interface HeroSearchBarProps {
4
+ guides?: any[];
5
+ searchOptions?: UseSearchOptions;
6
+ founderName?: string;
7
+ founderTitle?: string;
8
+ founderTagline?: string;
9
+ tags?: Array<{
10
+ name: string;
11
+ slug: string;
12
+ }>;
13
+ antigravityGuide?: any;
14
+ className?: string;
15
+ }
16
+ export declare function HeroSearchBar({ guides, searchOptions, founderName: _founderName, founderTitle, founderTagline: _founderTagline, tags, antigravityGuide, className, }: HeroSearchBarProps): React.JSX.Element;
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { Search, X } from "lucide-react";
5
+ import { CleverText } from "@goldlabelapps/flash";
6
+ import { clsx } from "clsx";
7
+ import { twMerge } from "tailwind-merge";
8
+ import { ParticleCanvas } from "../hero/ParticleCanvas";
9
+ import { useSearch } from "./useSearch";
10
+ import { SearchResultsList } from "./SearchResultsList";
11
+ import { FeaturedCard } from "../showcase/FeaturedCard";
12
+ function cn(...inputs) {
13
+ return twMerge(clsx(inputs));
14
+ }
15
+ export function HeroSearchBar({ guides = [], searchOptions, founderName: _founderName = "Wei Zang", founderTitle = "Creator & Author", founderTagline: _founderTagline = "Building next-generation digital products & multi-platform architectures.", tags = [], antigravityGuide, className, }) {
16
+ const mergedSearchOptions = {
17
+ initialGuides: guides,
18
+ ...searchOptions,
19
+ };
20
+ const { query, setQuery, searchResults, hasResults } = useSearch(mergedSearchOptions);
21
+ const searchContainerRef = React.useRef(null);
22
+ const handleTagClick = (label) => {
23
+ setQuery(label);
24
+ if (searchContainerRef.current?.scrollIntoView) {
25
+ searchContainerRef.current.scrollIntoView({
26
+ behavior: "smooth",
27
+ block: "start",
28
+ });
29
+ }
30
+ };
31
+ return (_jsxs("section", { className: cn("relative z-30 flex flex-col items-center justify-center pt-0 pb-6 w-full", className), children: [_jsx("div", { className: "pointer-events-none absolute inset-0 overflow-hidden z-0", children: _jsx(ParticleCanvas, {}) }), _jsxs("div", { className: "relative z-20 w-full max-w-4xl mx-auto px-4 flex flex-col items-center text-center", children: [_jsx("h1", { className: "text-3xl sm:text-5xl md:text-6xl font-extrabold tracking-tight text-slate-900 dark:text-white max-w-3xl leading-tight mb-1", children: _jsx("span", { className: "text-transparent bg-clip-text bg-gradient-to-r from-[#85580C] via-[#9E6E17] to-[#784E07] dark:from-[#F1D57A] dark:via-[#C09F52] dark:to-[#E6CA65] font-bold", children: _jsx(CleverText, { text: founderTitle, speed: 40, style: { fontFamily: "inherit", display: "inline" } }) }) }), antigravityGuide && (_jsx(FeaturedCard, { categoryTag: "Career / AI / Antigravity Overview", badge: "Featured", title: antigravityGuide.title || "Building Next.js Apps with Agentic AI & Google Antigravity", description: antigravityGuide.description || "Software development has transformed from manually typing boilerplate code into steering high-autonomy agentic systems. Discover our workflow for shipping Next.js apps with automated monorepos and static site pipelines.", href: antigravityGuide.slug || "/career/ai/antigravity", readMoreLabel: "Read more" })), _jsxs("div", { ref: searchContainerRef, className: "w-full max-w-[340px] mt-1 mb-2 sm:mb-3 relative z-20 scroll-mt-24 sm:scroll-mt-28", children: [_jsxs("div", { className: "relative flex items-center shadow-xl rounded-2xl overflow-hidden bg-white/90 dark:bg-slate-900/90 backdrop-blur-md border-2 border-indigo-500/30 focus-within:border-indigo-600 focus-within:ring-4 focus-within:ring-indigo-500/20 transition-all", children: [_jsx("div", { className: "pl-4 text-indigo-500 dark:text-indigo-400", children: _jsx(Search, { className: "w-5 h-5" }) }), _jsx("input", { type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search experience...", className: "w-full py-3 px-3 text-sm sm:text-base bg-transparent text-slate-900 dark:text-white placeholder-slate-400 dark:placeholder-slate-500 focus:outline-none" }), query && (_jsx("button", { onClick: () => setQuery(""), className: "pr-4 text-slate-400 hover:text-slate-600 dark:hover:text-white transition-colors", "aria-label": "Clear search query", children: _jsx(X, { className: "w-4 h-4" }) }))] }), !query && tags.length > 0 && (_jsxs("div", { className: "mt-3 flex flex-wrap justify-center gap-2", children: [_jsx("span", { className: "text-xs text-slate-400 dark:text-slate-500 self-center mr-1 font-medium", children: "Try:" }), tags
32
+ .filter((t) => t.name !== "FastAPI" && t.name !== "Supabase" && t.name !== "Firebase")
33
+ .slice(0, 6)
34
+ .map((tag) => {
35
+ const label = tag.slug === "flash" ? "Flash" : tag.name;
36
+ return (_jsx("button", { onClick: () => handleTagClick(label), className: "px-3 py-1 text-xs font-medium rounded-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-700 hover:border-slate-300 dark:hover:border-slate-600 transition-all shadow-sm cursor-pointer", children: label }, tag.slug));
37
+ })] })), query.trim() && (_jsx("div", { className: "absolute top-full left-0 right-0 mt-4 p-4 bg-white dark:bg-slate-900 rounded-2xl shadow-2xl border border-slate-200 dark:border-slate-800 text-left max-h-[70vh] overflow-y-auto z-50", children: _jsx(SearchResultsList, { query: query, searchResults: searchResults, hasResults: hasResults, onSelectResult: () => setQuery(""), allTags: tags }) }))] })] })] }));
38
+ }
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { UseSearchOptions } from "./useSearch";
3
+ export interface SearchDialogProps {
4
+ isOpen?: boolean;
5
+ onClose?: () => void;
6
+ searchOptions?: UseSearchOptions;
7
+ title?: string;
8
+ placeholder?: string;
9
+ renderTrigger?: boolean;
10
+ }
11
+ export declare function SearchDialog({ isOpen: externalIsOpen, onClose: externalOnClose, searchOptions, title, placeholder, renderTrigger, }: SearchDialogProps): React.JSX.Element;
@@ -0,0 +1,69 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useEffect, useRef, useSyncExternalStore } from "react";
4
+ import { Search, X } from "lucide-react";
5
+ import { Dialog, DialogContent, IconButton } from "@mui/material";
6
+ import { useTheme } from "../../context/ThemeContext";
7
+ import { useSearch } from "./useSearch";
8
+ import { SearchResultsList } from "./SearchResultsList";
9
+ import { cn } from "../../lib/utils";
10
+ const emptySubscribe = () => () => { };
11
+ const getClientSnapshot = () => true;
12
+ const getServerSnapshot = () => false;
13
+ export function SearchDialog({ isOpen: externalIsOpen, onClose: externalOnClose, searchOptions, title = "Search Experience & Documentation", placeholder = "Search site (e.g. Next.js, Flash, Agentic AI, React)...", renderTrigger, }) {
14
+ const [internalIsOpen, setInternalIsOpen] = useState(false);
15
+ const isControlled = externalIsOpen !== undefined;
16
+ const isOpen = isControlled ? externalIsOpen : internalIsOpen;
17
+ const mounted = useSyncExternalStore(emptySubscribe, getClientSnapshot, getServerSnapshot);
18
+ const { theme } = useTheme();
19
+ const isDark = mounted && theme === "dark";
20
+ const inputRef = useRef(null);
21
+ const { query, setQuery, searchResults, hasResults } = useSearch(searchOptions);
22
+ // Keyboard shortcut (Cmd+K / Ctrl+K) to open search dialog
23
+ useEffect(() => {
24
+ const handleKeyDown = (e) => {
25
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
26
+ e.preventDefault();
27
+ if (isControlled) {
28
+ if (isOpen && externalOnClose) {
29
+ externalOnClose();
30
+ }
31
+ else if (!isOpen && externalOnClose) {
32
+ // allow opening via keydown if parent supports it
33
+ externalOnClose();
34
+ }
35
+ }
36
+ else {
37
+ setInternalIsOpen((prev) => !prev);
38
+ }
39
+ }
40
+ };
41
+ window.addEventListener("keydown", handleKeyDown);
42
+ return () => window.removeEventListener("keydown", handleKeyDown);
43
+ }, [isControlled, isOpen, externalOnClose]);
44
+ const handleClose = () => {
45
+ if (isControlled) {
46
+ externalOnClose?.();
47
+ }
48
+ else {
49
+ setInternalIsOpen(false);
50
+ }
51
+ setQuery("");
52
+ };
53
+ const handleSelectResult = () => {
54
+ handleClose();
55
+ };
56
+ const shouldRenderTrigger = renderTrigger !== undefined ? renderTrigger : !isControlled;
57
+ return (_jsxs(_Fragment, { children: [shouldRenderTrigger && (_jsx("button", { type: "button", onClick: () => (isControlled ? externalOnClose?.() : setInternalIsOpen(true)), "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Search", title: "Search (Cmd+K)", className: cn("inline-flex items-center justify-center h-9 w-9 rounded-full text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer", isOpen && "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white"), children: _jsx(Search, { className: "h-4 w-4", "aria-hidden": "true" }) })), _jsxs(Dialog, { open: isOpen, onClose: handleClose, "aria-labelledby": "search-dialog-title", maxWidth: "md", fullWidth: true, PaperProps: {
58
+ sx: {
59
+ borderRadius: "20px",
60
+ p: 1.5,
61
+ maxWidth: "680px",
62
+ maxHeight: "85vh",
63
+ backgroundColor: isDark ? "#0f172a" : "#ffffff",
64
+ color: isDark ? "#f8fafc" : "#0f172a",
65
+ backgroundImage: "none",
66
+ border: isDark ? "1px solid #334155" : "1px solid #e2e8f0",
67
+ },
68
+ }, children: [_jsxs("div", { className: "flex items-center justify-between px-2 pt-1 pb-3 border-b border-slate-200 dark:border-slate-800", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-bold text-slate-700 dark:text-slate-200", id: "search-dialog-title", children: [_jsx(Search, { className: "w-4 h-4 text-indigo-500" }), _jsx("span", { children: title })] }), _jsx(IconButton, { onClick: handleClose, size: "small", "aria-label": "Close search", sx: { color: isDark ? "#94a3b8" : "#64748b" }, children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsxs(DialogContent, { sx: { p: 2 }, children: [_jsxs("div", { className: "relative flex items-center shadow-sm rounded-xl overflow-hidden bg-slate-50 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-700 focus-within:border-indigo-500 transition-all", children: [_jsx("div", { className: "pl-3.5 text-slate-400", children: _jsx(Search, { className: "w-5 h-5" }) }), _jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: placeholder, autoFocus: true, className: "w-full py-3 px-3 text-base bg-transparent text-slate-900 dark:text-white placeholder-slate-400 focus:outline-none" }), query && (_jsx("button", { onClick: () => setQuery(""), className: "pr-3.5 text-slate-400 hover:text-slate-600 dark:hover:text-white", "aria-label": "Clear search query", children: _jsx(X, { className: "w-4 h-4" }) }))] }), query.trim() && (_jsx("div", { className: "mt-4 max-h-[55vh] overflow-y-auto pr-1", children: _jsx(SearchResultsList, { query: query, searchResults: searchResults, hasResults: hasResults, onSelectResult: handleSelectResult }) }))] })] })] }));
69
+ }
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { SearchDialogProps } from "./SearchDialog";
3
+ export interface SearchIconButtonProps extends Omit<SearchDialogProps, "isOpen" | "onClose"> {
4
+ className?: string;
5
+ iconClassName?: string;
6
+ ariaLabel?: string;
7
+ title?: string;
8
+ buttonClassName?: string;
9
+ }
10
+ export declare function SearchIconButton({ className, iconClassName, ariaLabel, title, buttonClassName, ...dialogProps }: SearchIconButtonProps): React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Search } from "lucide-react";
5
+ import { cn } from "../../lib/utils";
6
+ import { SearchDialog } from "./SearchDialog";
7
+ export function SearchIconButton({ className, iconClassName = "h-4 w-4", ariaLabel = "Search", title = "Search (Cmd+K)", buttonClassName, ...dialogProps }) {
8
+ const [isOpen, setIsOpen] = useState(false);
9
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => setIsOpen(true), "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": ariaLabel, title: title, className: cn("inline-flex items-center justify-center h-9 w-9 rounded-full text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer", isOpen && "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white", className, buttonClassName), children: _jsx(Search, { className: iconClassName, "aria-hidden": "true" }) }), _jsx(SearchDialog, { ...dialogProps, isOpen: isOpen, onClose: () => setIsOpen(false), renderTrigger: false })] }));
10
+ }
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import { SearchResultsData } from "./useSearch";
3
+ export interface SearchResultsListProps {
4
+ query: string;
5
+ searchResults: SearchResultsData;
6
+ hasResults: boolean;
7
+ onSelectResult?: () => void;
8
+ allTags?: Array<{
9
+ name: string;
10
+ slug: string;
11
+ }>;
12
+ }
13
+ export declare function SearchResultsList({ query, searchResults, hasResults, onSelectResult, allTags, }: SearchResultsListProps): React.JSX.Element | null;
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
4
+ import { ArrowRight, Tag, Briefcase, BookOpen, Video, Layers } from "lucide-react";
5
+ import { HighlightText, getContentSnippet } from "./useSearch";
6
+ export function SearchResultsList({ query, searchResults, hasResults, onSelectResult, allTags = [], }) {
7
+ if (!query.trim())
8
+ return null;
9
+ if (!hasResults) {
10
+ return (_jsxs("div", { className: "p-6 text-center text-slate-500 dark:text-slate-400", children: ["No exact matches for \"", query, "\". Try searching for ", _jsx("i", { children: "Next.js" }), ", ", _jsx("i", { children: "React" }), ", ", _jsx("i", { children: "Flash" }), ", or ", _jsx("i", { children: "Agentic AI" }), "."] }));
11
+ }
12
+ return (_jsxs("div", { className: "space-y-6", children: [searchResults.guides.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(BookOpen, { className: "w-3.5 h-3.5" }), "(", searchResults.guides.length, ") Results"] }), _jsx("div", { className: "space-y-2", children: searchResults.guides.map((guide) => (_jsxs(Link, { href: guide.cleanSlug === "index" ? "/" : `/${guide.cleanSlug}`, onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsxs("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-2", children: [_jsx("span", { children: _jsx(HighlightText, { text: guide.title, query: query }) }), guide.category && (_jsx("span", { className: "text-[10px] px-2 py-0.5 rounded-full bg-indigo-100 dark:bg-indigo-900/60 text-indigo-700 dark:text-indigo-300 font-medium", children: guide.category }))] }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-1 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(guide.description || "", query), query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-3" })] }, guide.cleanSlug))) })] })), searchResults.features.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Layers, { className: "w-3.5 h-3.5" }), "Features (", searchResults.features.length, ")"] }), _jsx("div", { className: "space-y-2", children: searchResults.features.map((feature, idx) => (_jsxs(Link, { href: "/features", onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsxs("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-2", children: [_jsx("span", { children: _jsx(HighlightText, { text: feature.title, query: query }) }), feature.tag && (_jsx("span", { className: "text-[10px] px-2 py-0.5 rounded-full bg-purple-100 dark:bg-purple-900/60 text-purple-700 dark:text-purple-300 font-medium", children: feature.tag }))] }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-1 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(feature.description || "", query), query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-3" })] }, feature.title || idx))) })] })), searchResults.videos.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Video, { className: "w-3.5 h-3.5" }), "Videos (", searchResults.videos.length, ")"] }), _jsx("div", { className: "space-y-2", children: searchResults.videos.map((video, idx) => (_jsxs(Link, { href: "/demos", onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsx("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-2", children: _jsx("span", { children: _jsx(HighlightText, { text: video.title, query: query }) }) }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-1 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(video.tagline || video.description || "", query), query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-3" })] }, video.title || idx))) })] })), searchResults.tags.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Tag, { className: "w-3.5 h-3.5" }), "Skills & Topics (", searchResults.tags.length, ")"] }), _jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2", children: searchResults.tags.map((tag) => (_jsxs(Link, { href: `/tag/${tag.slug}`, onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsx("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400", children: _jsx(HighlightText, { text: tag.name, query: query }) }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-0.5 line-clamp-1", children: _jsx(HighlightText, { text: tag.description, query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-2" })] }, tag.slug))) })] })), searchResults.roles.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Briefcase, { className: "w-3.5 h-3.5" }), "Experience (", searchResults.roles.length, ")"] }), _jsx("div", { className: "space-y-2", children: searchResults.roles.map((role) => {
13
+ const availableTags = allTags.length > 0 ? allTags : searchResults.tags;
14
+ const matchingTag = availableTags.find((t) => role.skills?.some((s) => s.toLowerCase() === t.name.toLowerCase()));
15
+ const targetUrl = matchingTag ? `/tag/${matchingTag.slug}` : `/tag/${role.eraId || "experience"}`;
16
+ return (_jsxs(Link, { href: targetUrl, onClick: onSelectResult, className: "block p-3.5 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all group cursor-pointer", children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsxs("span", { className: "font-bold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-1.5", children: [_jsx("span", { children: _jsx(HighlightText, { text: role.title, query: query }) }), _jsx("span", { className: "text-slate-400 font-normal", children: "@" }), _jsx("span", { className: "text-slate-700 dark:text-slate-300", children: _jsx(HighlightText, { text: role.company, query: query }) })] }), role.period && (_jsx("span", { className: "text-[11px] text-slate-400 font-mono", children: role.period }))] }), role.summary && (_jsx("p", { className: "text-xs text-slate-600 dark:text-slate-300 mt-1.5 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(role.summary, query, 140), query: query }) }))] }, role.id));
17
+ }) })] }))] }));
18
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./useSearch";
2
+ export * from "./SearchResultsList";
3
+ export * from "./SearchDialog";
4
+ export * from "./SearchIconButton";
5
+ export * from "./HeroSearchBar";
@@ -0,0 +1,5 @@
1
+ export * from "./useSearch";
2
+ export * from "./SearchResultsList";
3
+ export * from "./SearchDialog";
4
+ export * from "./SearchIconButton";
5
+ export * from "./HeroSearchBar";
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+ export interface SearchResultsData {
3
+ guides: any[];
4
+ videos: any[];
5
+ features: any[];
6
+ tags: any[];
7
+ roles: any[];
8
+ }
9
+ export interface UseSearchOptions {
10
+ initialGuides?: any[];
11
+ profileData?: {
12
+ tags?: Array<{
13
+ name: string;
14
+ slug: string;
15
+ description: string;
16
+ }>;
17
+ roles?: Array<{
18
+ id: string;
19
+ title: string;
20
+ company: string;
21
+ period?: string;
22
+ eraId?: string;
23
+ summary?: string;
24
+ skills: string[];
25
+ }>;
26
+ };
27
+ videosConfig?: {
28
+ items?: Array<{
29
+ title: string;
30
+ tagline: string;
31
+ description: string;
32
+ keyFeatures?: string[];
33
+ }>;
34
+ };
35
+ featuresConfig?: {
36
+ items?: Array<{
37
+ title: string;
38
+ tabLabel?: string;
39
+ tag?: string;
40
+ description: string;
41
+ }>;
42
+ };
43
+ }
44
+ export declare function useSearch(initialGuidesOrOptions?: any[] | UseSearchOptions, optionsParam?: UseSearchOptions): {
45
+ query: string;
46
+ setQuery: React.Dispatch<React.SetStateAction<string>>;
47
+ searchResults: SearchResultsData;
48
+ hasResults: boolean;
49
+ };
50
+ export declare function HighlightText({ text, query }: {
51
+ text: string;
52
+ query: string;
53
+ }): React.JSX.Element;
54
+ export declare function getContentSnippet(content: string, query: string, maxLength?: number): string;
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useState, useMemo, useEffect } from "react";
4
+ import { Box } from "@mui/material";
5
+ export function useSearch(initialGuidesOrOptions = [], optionsParam) {
6
+ const options = Array.isArray(initialGuidesOrOptions)
7
+ ? { initialGuides: initialGuidesOrOptions, ...optionsParam }
8
+ : initialGuidesOrOptions;
9
+ const { initialGuides = [], profileData, videosConfig, featuresConfig } = options;
10
+ const [query, setQuery] = useState("");
11
+ const [fetchedGuides, setFetchedGuides] = useState([]);
12
+ const hasInitialGuides = initialGuides && initialGuides.length > 0;
13
+ const guides = hasInitialGuides ? initialGuides : fetchedGuides;
14
+ useEffect(() => {
15
+ if (hasInitialGuides || !query.trim() || fetchedGuides.length > 0) {
16
+ return;
17
+ }
18
+ let isMounted = true;
19
+ fetch("/api/guides")
20
+ .then((res) => (res.ok ? res.json() : { guides: [] }))
21
+ .then((data) => {
22
+ if (isMounted && Array.isArray(data.guides)) {
23
+ setFetchedGuides(data.guides);
24
+ }
25
+ })
26
+ .catch(() => { });
27
+ return () => {
28
+ isMounted = false;
29
+ };
30
+ }, [hasInitialGuides, query, fetchedGuides.length]);
31
+ const searchResults = useMemo(() => {
32
+ if (!query.trim()) {
33
+ return { guides: [], videos: [], features: [], tags: [], roles: [] };
34
+ }
35
+ const q = query.toLowerCase().trim();
36
+ const matchedGuides = (guides || []).filter((g) => (g.title && g.title.toLowerCase().includes(q)) ||
37
+ (g.description && g.description.toLowerCase().includes(q)) ||
38
+ (g.tags && g.tags.some((t) => t.toLowerCase().includes(q))) ||
39
+ (g.category && g.category.toLowerCase().includes(q)) ||
40
+ (g.cleanSlug && g.cleanSlug.toLowerCase().includes(q)));
41
+ const matchedVideos = (videosConfig?.items || []).filter((v) => (v.title && v.title.toLowerCase().includes(q)) ||
42
+ (v.tagline && v.tagline.toLowerCase().includes(q)) ||
43
+ (v.description && v.description.toLowerCase().includes(q)) ||
44
+ (v.keyFeatures && v.keyFeatures.some((kf) => kf.toLowerCase().includes(q))));
45
+ const matchedFeatures = (featuresConfig?.items || []).filter((f) => (f.title && f.title.toLowerCase().includes(q)) ||
46
+ (f.tabLabel && f.tabLabel.toLowerCase().includes(q)) ||
47
+ (f.tag && f.tag.toLowerCase().includes(q)) ||
48
+ (f.description && f.description.toLowerCase().includes(q)));
49
+ const matchedTags = (profileData?.tags || []).filter((t) => (t.name && t.name.toLowerCase().includes(q)) ||
50
+ (t.slug && t.slug.includes(q)) ||
51
+ (t.description && t.description.toLowerCase().includes(q)));
52
+ const matchedRoles = (profileData?.roles || []).filter((r) => (r.title && r.title.toLowerCase().includes(q)) ||
53
+ (r.company && r.company.toLowerCase().includes(q)) ||
54
+ (r.skills && r.skills.some((s) => s.toLowerCase().includes(q))) ||
55
+ (r.summary && r.summary.toLowerCase().includes(q)));
56
+ return {
57
+ guides: matchedGuides,
58
+ videos: matchedVideos,
59
+ features: matchedFeatures,
60
+ tags: matchedTags,
61
+ roles: matchedRoles,
62
+ };
63
+ }, [query, guides, profileData, videosConfig, featuresConfig]);
64
+ const hasResults = searchResults.guides.length > 0 ||
65
+ searchResults.videos.length > 0 ||
66
+ searchResults.features.length > 0 ||
67
+ searchResults.tags.length > 0 ||
68
+ searchResults.roles.length > 0;
69
+ return {
70
+ query,
71
+ setQuery,
72
+ searchResults,
73
+ hasResults,
74
+ };
75
+ }
76
+ export function HighlightText({ text, query }) {
77
+ if (!query.trim() || !text)
78
+ return _jsx(_Fragment, { children: text });
79
+ const escapedQuery = query.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
80
+ const regex = new RegExp(`(${escapedQuery})`, "gi");
81
+ const parts = text.split(regex);
82
+ return (_jsx(_Fragment, { children: parts.map((part, i) => part.toLowerCase() === query.trim().toLowerCase() ? (_jsx(Box, { component: "mark", sx: {
83
+ backgroundColor: "#fde047",
84
+ color: "#0f172a",
85
+ fontWeight: 700,
86
+ px: 0.5,
87
+ borderRadius: "4px",
88
+ }, children: part }, i)) : (part)) }));
89
+ }
90
+ export function getContentSnippet(content, query, maxLength = 110) {
91
+ if (!content || !query.trim())
92
+ return content.slice(0, maxLength);
93
+ const q = query.toLowerCase().trim();
94
+ const lower = content.toLowerCase();
95
+ const index = lower.indexOf(q);
96
+ if (index === -1)
97
+ return content.slice(0, maxLength) + (content.length > maxLength ? "..." : "");
98
+ const start = Math.max(0, index - 25);
99
+ const end = Math.min(content.length, index + query.length + 65);
100
+ let snippet = content.slice(start, end);
101
+ if (start > 0)
102
+ snippet = "..." + snippet;
103
+ if (end < content.length)
104
+ snippet = snippet + "...";
105
+ return snippet;
106
+ }
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ export interface PageLinkShortcodeProps {
3
+ url?: string;
4
+ href?: string;
5
+ icon?: string;
6
+ iconAlign?: 'left' | 'right';
7
+ title?: string;
8
+ description?: string;
9
+ variant?: 'contained' | 'outlined' | 'text';
10
+ color?: 'primary' | 'secondary' | 'inherit';
11
+ }
12
+ export type PageLinkProps = PageLinkShortcodeProps;
13
+ export declare function PageLink({ url, href, icon, iconAlign, title, description, variant, color, }: PageLinkProps): React.JSX.Element;
14
+ export declare const PageLinkShortcode: typeof PageLink;
15
+ export default PageLink;
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AppRouterLink as NextLink } from '@goldlabelapps/uberedux';
4
+ import { Button as MuiButton, Box, Typography } from '@mui/material';
5
+ import { useTheme } from '@mui/material/styles';
6
+ import Icon from '../../design-system/components/icons/Icon/Icon';
7
+ export function PageLink({ url, href, icon, iconAlign, title, description, variant = 'outlined', color = 'primary', }) {
8
+ const theme = useTheme();
9
+ const targetUrl = url || href || '#';
10
+ const isExternal = /^https?:\/\//.test(targetUrl);
11
+ const isRightAlign = iconAlign === 'right' || (icon === 'right' && !iconAlign);
12
+ const iconName = icon === 'right' ? 'right' : icon;
13
+ const iconElement = iconName ? _jsx(Icon, { icon: iconName, color: color === 'inherit' ? 'inherit' : color }) : null;
14
+ const isContained = variant === 'contained';
15
+ return (_jsx(MuiButton, { component: NextLink, href: targetUrl, target: isExternal ? '_blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, variant: variant, color: color, startIcon: !isRightAlign ? iconElement : undefined, endIcon: isRightAlign ? iconElement : undefined, sx: {
16
+ my: 1.5,
17
+ py: 1.25,
18
+ px: 2,
19
+ borderRadius: theme.shape?.borderRadius ? `${Number(theme.shape.borderRadius) * 2}px` : '6px',
20
+ textTransform: 'none',
21
+ justifyContent: 'flex-start',
22
+ textAlign: 'left',
23
+ width: '100%',
24
+ maxWidth: '600px',
25
+ display: 'inline-flex',
26
+ alignItems: 'center',
27
+ lineHeight: 1.4,
28
+ textDecoration: 'none',
29
+ backgroundColor: isContained ? undefined : theme.palette.background.paper,
30
+ borderColor: variant === 'outlined' ? theme.palette.divider || theme.palette.primary.main : undefined,
31
+ color: isContained ? 'primary.contrastText' : 'text.primary',
32
+ '&:hover': {
33
+ textDecoration: 'none',
34
+ backgroundColor: isContained ? undefined : theme.palette.action.hover,
35
+ borderColor: variant === 'outlined' ? theme.palette.primary.main : undefined,
36
+ },
37
+ }, children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', textAlign: 'left', minWidth: 0, flex: 1 }, children: [title && (_jsx(Typography, { component: "span", variant: "subtitle1", sx: {
38
+ fontWeight: 600,
39
+ lineHeight: 1.3,
40
+ color: isContained ? 'inherit' : 'text.primary',
41
+ }, children: title })), description && (_jsx(Typography, { component: "span", variant: "body2", sx: {
42
+ lineHeight: 1.3,
43
+ mt: title ? 0.25 : 0,
44
+ color: isContained ? 'inherit' : 'text.secondary',
45
+ opacity: isContained ? 0.85 : 1,
46
+ }, children: description })), !title && !description && (_jsx(Typography, { component: "span", variant: "button", sx: {
47
+ color: isContained ? 'inherit' : 'text.primary',
48
+ }, children: targetUrl }))] }) }));
49
+ }
50
+ export const PageLinkShortcode = PageLink;
51
+ export default PageLink;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import Link from "next/link";
3
+ import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
4
4
  import { useSiteConfig } from "../../context/ConfigContext";
5
5
  import { ChevronLeft, ChevronRight, BookOpen } from "lucide-react";
6
6
  export function BlogSection({ posts: propPosts, title: propTitle, subtitle: propSubtitle, }) {
@@ -8,5 +8,5 @@ export function BlogSection({ posts: propPosts, title: propTitle, subtitle: prop
8
8
  const posts = propPosts || siteConfig.blogs.posts || [];
9
9
  const title = propTitle || siteConfig.blogs.title || "Guides & Articles";
10
10
  const subtitle = propSubtitle || siteConfig.blogs.subtitle;
11
- return (_jsx("section", { id: "articles", className: "py-24 sm:py-32 relative bg-white overflow-hidden transition-colors duration-200", children: _jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10", children: [_jsxs("div", { className: "flex flex-col md:flex-row md:items-end justify-between gap-6 mb-12", children: [_jsxs("div", { children: [_jsxs("div", { className: "inline-flex items-center gap-1.5 px-3.5 py-1 rounded-full bg-[#012867]/[0.06] border border-[#012867]/15 text-xs font-bold text-[#012867] mb-4 backdrop-blur-md", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5" }), _jsx("span", { children: "Articles & Guides" })] }), _jsx("h2", { className: "text-3xl sm:text-5xl font-extrabold text-slate-900 tracking-tight", children: title }), subtitle && (_jsx("p", { className: "mt-4 text-base text-slate-600 max-w-2xl", children: subtitle }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { "aria-label": "Previous blog posts", className: "p-2 rounded-full bg-slate-100 hover:bg-slate-200 text-slate-700 transition-colors cursor-pointer", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsx("button", { "aria-label": "Next blog posts", className: "p-2 rounded-full bg-slate-100 hover:bg-slate-200 text-slate-700 transition-colors cursor-pointer", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })] }), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-8", children: posts.map((post) => (_jsxs(Link, { href: post.href, className: "group rounded-3xl p-6 sm:p-8 bg-slate-50 border border-slate-200 hover:border-[#012867]/40 shadow-sm hover:shadow-xl transition-all duration-300 flex flex-col justify-between", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between gap-2 mb-4", children: [_jsx("span", { className: "text-[11px] font-bold px-2.5 py-0.5 rounded-full bg-[#012867]/[0.08] text-[#012867]", children: post.category }), _jsx("span", { className: "text-xs text-slate-400 font-medium", children: post.date })] }), _jsx("h3", { className: "text-lg sm:text-xl font-bold text-slate-900 group-hover:text-[#012867] transition-colors leading-snug mb-3", children: post.title }), post.summary && (_jsx("p", { className: "text-xs sm:text-sm text-slate-600 line-clamp-3 leading-relaxed", children: post.summary }))] }), _jsx("div", { className: "pt-6 mt-6 border-t border-slate-200/60 flex items-center text-xs font-bold text-[#012867] group-hover:underline", children: _jsx("span", { children: "Read guide \u2192" }) })] }, post.id))) })] }) }));
11
+ return (_jsx("section", { id: "articles", className: "py-24 sm:py-32 relative bg-white overflow-hidden transition-colors duration-200", children: _jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10", children: [_jsxs("div", { className: "flex flex-col md:flex-row md:items-end justify-between gap-6 mb-12", children: [_jsxs("div", { children: [_jsxs("div", { className: "inline-flex items-center gap-1.5 px-3.5 py-1 rounded-full bg-[#012867]/[0.06] border border-[#012867]/15 text-xs font-bold text-[#012867] mb-4 backdrop-blur-md", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5" }), _jsx("span", { children: "Articles & Guides" })] }), _jsx("h2", { className: "text-3xl sm:text-5xl font-normal text-slate-900 tracking-tight", children: title }), subtitle && (_jsx("p", { className: "mt-4 text-base text-slate-600 max-w-2xl", children: subtitle }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { "aria-label": "Previous blog posts", className: "p-2 rounded-full bg-slate-100 hover:bg-slate-200 text-slate-700 transition-colors cursor-pointer", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsx("button", { "aria-label": "Next blog posts", className: "p-2 rounded-full bg-slate-100 hover:bg-slate-200 text-slate-700 transition-colors cursor-pointer", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })] }), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-8", children: posts.map((post) => (_jsxs(Link, { href: post.href, className: "group rounded-3xl p-6 sm:p-8 bg-slate-50 border border-slate-200 hover:border-[#012867]/40 shadow-sm hover:shadow-xl transition-all duration-300 flex flex-col justify-between", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between gap-2 mb-4", children: [_jsx("span", { className: "text-[11px] font-bold px-2.5 py-0.5 rounded-full bg-[#012867]/[0.08] text-[#012867]", children: post.category }), _jsx("span", { className: "text-xs text-slate-400 font-medium", children: post.date })] }), _jsx("h3", { className: "text-lg sm:text-xl font-normal text-slate-900 group-hover:text-[#012867] transition-colors leading-snug mb-3", children: post.title }), post.summary && (_jsx("p", { className: "text-xs sm:text-sm text-slate-600 line-clamp-3 leading-relaxed", children: post.summary }))] }), _jsx("div", { className: "pt-6 mt-6 border-t border-slate-200/60 flex items-center text-xs font-bold text-[#012867] group-hover:underline", children: _jsx("span", { children: "Read guide \u2192" }) })] }, post.id))) })] }) }));
12
12
  }