diff --git a/.claude/settings.json b/.claude/settings.json index 0ab2e46..a09c021 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -71,7 +71,7 @@ "WebFetch(domain:vercel.com)", "WebFetch(domain:vitest.dev)" ], - "deny": ["Read(**/.env)", "Read(**/.envrc)", "Read(x_docs/OLD/**)"], + "deny": ["Read(**/.env)", "Read(**/.envrc)", "Read(x_docs/DONE/**)"], "ask": [] }, "enabledPlugins": { @@ -80,7 +80,9 @@ "frontend-design@my-claude-plugins": false, "tailwindcss@my-claude-plugins": false, "shadcn-ui@my-claude-plugins": true, - "feature-dev@claude-plugins-official": true + "feature-dev@claude-plugins-official": true, + "code-review@claude-plugins-official": true, + "pr-review-toolkit@claude-plugins-official": true }, "extraKnownMarketplaces": { "playwright-skill": { diff --git a/.mcp.json b/.mcp.json index 5dc496c..a592503 100644 --- a/.mcp.json +++ b/.mcp.json @@ -4,6 +4,10 @@ "type": "stdio", "command": "npx", "args": ["@playwright/mcp@latest", "--device", "iPhone 11"] + }, + "shadcn": { + "command": "npx", + "args": ["shadcn@latest", "mcp"] } } } diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx index 2b51a02..864caa3 100644 --- a/app/(app)/layout.tsx +++ b/app/(app)/layout.tsx @@ -21,7 +21,7 @@ const RootLayout = async ({ children }: { children: React.ReactNode }) => { {/* Content + Right Sidebar row */}
-
+
{children}
diff --git a/app/(app)/question/[id]/page.tsx b/app/(app)/questions/[id]/page.tsx similarity index 100% rename from app/(app)/question/[id]/page.tsx rename to app/(app)/questions/[id]/page.tsx diff --git a/app/(app)/ask-question/page.tsx b/app/(app)/questions/ask/page.tsx similarity index 100% rename from app/(app)/ask-question/page.tsx rename to app/(app)/questions/ask/page.tsx diff --git a/app/globals.css b/app/globals.css index fa3c889..0859e1a 100644 --- a/app/globals.css +++ b/app/globals.css @@ -68,7 +68,7 @@ /* Raw CSS variables (no Tailwind utilities generated). Access via var(--background), etc. */ :root { /* Layout */ - --top-bar-height: 4rem; /* 64px — desktop & mobile top navigation */ + --top-bar-height: 5rem; /* 64px — desktop & mobile top navigation */ --right-sidebar-width: 22rem; /* 352px — right sidebar width at xl+ */ /* Base */ diff --git a/components/navigation/desktop-topbar.tsx b/components/navigation/desktop-topbar.tsx index 2376f1c..d79d2a8 100644 --- a/components/navigation/desktop-topbar.tsx +++ b/components/navigation/desktop-topbar.tsx @@ -1,14 +1,14 @@ import { SignedOut, SignInButton, SignUpButton } from "@clerk/nextjs"; -import { GlobalSearch } from "@/components/search/global-search"; +import { Searchbox } from "@/components/search/searchbox"; import { Button } from "@/components/ui/button"; export function DesktopTopBar() { return ( -
+
{/* Left section: matches main content structure (padding + max-w-5xl centering) */} -
+
- +
diff --git a/components/navigation/mobile-nav.tsx b/components/navigation/mobile-nav.tsx index ba756f7..bfe0251 100644 --- a/components/navigation/mobile-nav.tsx +++ b/components/navigation/mobile-nav.tsx @@ -26,7 +26,7 @@ import { } from "@/components/ui/sheet"; import { cn } from "@/lib/utils"; -const MOBILE_NAV_MAX_WIDTH = "max-w-[320px]"; +const MOBILE_NAV_MAX_WIDTH = "max-w-[280px]"; export function MobileNav() { const [open, setOpen] = useState(false); @@ -39,7 +39,7 @@ export function MobileNav() { @@ -67,7 +67,7 @@ export function MobileNav() { Mobile navigation menu - + diff --git a/components/navigation/mobile-navlink.tsx b/components/navigation/mobile-navlink.tsx index 48238a4..0595764 100644 --- a/components/navigation/mobile-navlink.tsx +++ b/components/navigation/mobile-navlink.tsx @@ -32,7 +32,7 @@ export function MobileNavLink({ href={route} onClick={onClick} className={cn( - "flex items-center gap-3 rounded-lg px-3 py-3", + "flex items-center gap-3 rounded-lg px-2 py-3", isActive ? NAV_LINK_ACTIVE_CLASSES : `${NAV_LINK_INACTIVE_CLASSES} text-sidebar-foreground hover:bg-muted`, diff --git a/components/navigation/mobile-topbar.tsx b/components/navigation/mobile-topbar.tsx index dc511d7..d9eac7e 100644 --- a/components/navigation/mobile-topbar.tsx +++ b/components/navigation/mobile-topbar.tsx @@ -3,11 +3,11 @@ import { MobileNav } from "@/components/navigation/mobile-nav"; export function MobileTopBar() { return ( -
+
{/* biome-ignore lint/a11y/useAltText: Decorative logo, aria-label on parent link */} {/* biome-ignore lint/performance/noImgElement: SVG logo doesn't benefit from next/image optimisation */} - + diff --git a/components/navigation/nav-links.constants.ts b/components/navigation/nav-links.constants.ts index 182cc09..c0cd589 100644 --- a/components/navigation/nav-links.constants.ts +++ b/components/navigation/nav-links.constants.ts @@ -14,7 +14,7 @@ export const NAV_LINKS = [ { iconUrl: "/icons/tag.svg", route: "/tags", label: "Tags" }, { iconUrl: "/icons/question.svg", - route: "/ask-question", - label: "Ask a question", + route: "/questions/ask", + label: "Ask Question", }, ] as const satisfies readonly NavLink[]; diff --git a/components/question-card.tsx b/components/question-card.tsx index 3e593d9..d403697 100644 --- a/components/question-card.tsx +++ b/components/question-card.tsx @@ -22,7 +22,7 @@ export function QuestionCard({ question }: QuestionCardProps) {
- +

{question.title}

diff --git a/components/right-sidebar/question-link.tsx b/components/right-sidebar/question-link.tsx index 36d4490..418a947 100644 --- a/components/right-sidebar/question-link.tsx +++ b/components/right-sidebar/question-link.tsx @@ -9,7 +9,7 @@ type QuestionLinkProps = { export function QuestionLink({ id, title }: QuestionLinkProps) { return ( diff --git a/components/search/global-search.tsx b/components/search/global-search.tsx deleted file mode 100644 index 5d66ca1..0000000 --- a/components/search/global-search.tsx +++ /dev/null @@ -1,7 +0,0 @@ -// TODO: Implement global site search functionality -// This component will provide search across all site content (questions, users, tags, etc.) -// For now, it's a placeholder to preserve layout structure. - -export function GlobalSearch() { - return

Global Search

; -} diff --git a/components/search/searchbox.tsx b/components/search/searchbox.tsx new file mode 100644 index 0000000..64f9ed8 --- /dev/null +++ b/components/search/searchbox.tsx @@ -0,0 +1,5 @@ +// TODO: Implement search similar to stackoverflow + +export function Searchbox() { + return

Searchbox

; +} diff --git a/components/ui/command.tsx b/components/ui/command.tsx new file mode 100644 index 0000000..397d948 --- /dev/null +++ b/components/ui/command.tsx @@ -0,0 +1,183 @@ +"use client"; + +import { Command as CommandPrimitive } from "cmdk"; +import { SearchIcon } from "lucide-react"; +import type * as React from "react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { cn } from "@/lib/utils"; + +function Command({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandDialog({ + title = "Command Palette", + description = "Search for a command to run...", + children, + className, + showCloseButton = true, + ...props +}: React.ComponentProps & { + title?: string; + description?: string; + className?: string; + showCloseButton?: boolean; +}) { + return ( + + + {title} + {description} + + + + {children} + + + + ); +} + +function CommandInput({ + className, + ...props +}: React.ComponentProps) { + return ( +
+ + +
+ ); +} + +function CommandList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandEmpty({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function CommandShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ); +} + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +}; diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx new file mode 100644 index 0000000..a64f350 --- /dev/null +++ b/components/ui/dialog.tsx @@ -0,0 +1,143 @@ +"use client"; + +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { XIcon } from "lucide-react"; +import type * as React from "react"; + +import { cn } from "@/lib/utils"; + +function Dialog({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean; +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ); +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +}; diff --git a/components/ui/popover.tsx b/components/ui/popover.tsx new file mode 100644 index 0000000..d68b87a --- /dev/null +++ b/components/ui/popover.tsx @@ -0,0 +1,48 @@ +"use client"; + +import * as PopoverPrimitive from "@radix-ui/react-popover"; +import type * as React from "react"; + +import { cn } from "@/lib/utils"; + +function Popover({ + ...props +}: React.ComponentProps) { + return ; +} + +function PopoverTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function PopoverContent({ + className, + align = "center", + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +function PopoverAnchor({ + ...props +}: React.ComponentProps) { + return ; +} + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }; diff --git a/components/ui/sidebar.tsx b/components/ui/sidebar.tsx index e02a249..f892946 100644 --- a/components/ui/sidebar.tsx +++ b/components/ui/sidebar.tsx @@ -216,7 +216,7 @@ function Sidebar({ return (