Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 31 additions & 20 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
{
"extraKnownMarketplaces": {
"playwright-skill": {
"source": {
"source": "github",
"repo": "lackeyjb/playwright-skill"
}
}
},

"permissions": {
"deny": ["Read(**/.env)", "Read(**/.envrc)"],
"ask": [],

"allow": [
"mcp__ide__getDiagnostics",

"Bash(claude mcp get:*)",
"Bash(claude mcp list)",

"mcp__playwright__browser_click",
"mcp__playwright__browser_close",
"mcp__playwright__browser_console_messages",
Expand All @@ -31,7 +17,6 @@
"mcp__playwright__browser_tabs",
"mcp__playwright__browser_take_screenshot",
"mcp__playwright__browser_wait_for",

"Bash(cat:*)",
"Bash(echo:*)",
"Bash(find:*)",
Expand All @@ -41,13 +26,11 @@
"Bash(tree:*)",
"Bash(wc:*)",
"Bash(xargs:*)",

"Bash(git log:*)",
"Bash(gh pr checks:*)",
"Bash(gh pr list:*)",
"Bash(gh run list:*)",
"Bash(gh run view:*)",

"Bash(npm run build)",
"Bash(npm run dev)",
"Bash(npm run start)",
Expand All @@ -61,7 +44,6 @@
"Bash(npx @biomejs/biome:*)",
"Bash(npx lefthook:*)",
"Bash(npx playwright:*)",

"Bash(vercel --help)",
"Bash(vercel env --help)",
"Bash(vercel env ls:*)",
Expand All @@ -71,7 +53,6 @@
"Bash(vercel open)",
"Bash(vercel project ls:*)",
"Bash(vercel whoami)",

"WebFetch(domain:biomejs.dev)",
"WebFetch(domain:docs.github.com)",
"WebFetch(domain:github.com)",
Expand All @@ -82,6 +63,36 @@
"WebFetch(domain:ui.shadcn.com)",
"WebFetch(domain:vercel.com)",
"WebFetch(domain:vitest.dev)"
]
],
"deny": ["Read(**/.env)", "Read(**/.envrc)"],
"ask": []
},
"enabledPlugins": {
"playwright-skill@playwright-skill": false,
"skill-creator@my-claude-plugins": false,
"frontend-design@my-claude-plugins": false,
"tailwindcss@my-claude-plugins": false,
"shadcn-ui@my-claude-plugins": true,
"feature-dev@claude-plugins-official": true
},
"extraKnownMarketplaces": {
"playwright-skill": {
"source": {
"source": "github",
"repo": "lackeyjb/playwright-skill"
}
},
"my-claude-plugins": {
"source": {
"source": "github",
"repo": "michellepace/my-claude-plugins"
}
},
"claude-plugins-official": {
"source": {
"source": "github",
"repo": "anthropics/claude-plugins-official"
}
}
}
}
13 changes: 13 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# CLAUDE.md

**DevFlow** — A community-driven platform for asking and answering programming questions. Get help, share knowledge, and collaborate with developers from around the world. (Similar to Stack Overflow)

The project uses British English - strictly.

## Tech Stack
Expand Down Expand Up @@ -29,6 +31,17 @@ vercel env ls # Check env vars are configured
vercel whoami # Verify CLI is authenticated
```

## shadcn/ui CLI

```bash
npx shadcn@latest list @shadcn # List all available components
npx shadcn@latest search @shadcn -q "nav" # Search components by query
npx shadcn@latest view button card # Preview code before installing
npx shadcn@latest add <component> # Add component to project
npx shadcn@latest add button --overwrite # Overwrite existing component
npx shadcn@latest add @v0/<block> # Add from v0.dev registry
```

## Coding Practices

- Only add `"use client"` when interactivity is needed
Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const AuthLayout = ({ children }: { children: React.ReactNode }) => {
return (
<main className="min-h-screen bg-cover bg-center bg-no-repeat bg-[url('/images/auth-bg-light.webp')] dark:bg-[url('/images/auth-bg-dark.webp')]">
<main className="flex min-h-screen items-center justify-center bg-cover bg-center bg-no-repeat bg-(image:--auth-bg) px-6 py-10 md:px-8 lg:px-12">
{children}
</main>
);
Expand Down
6 changes: 1 addition & 5 deletions app/(auth)/sign-in/[[...sign-in]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,5 @@ import { ClerkSignIn } from "@/components/auth/clerk-signin";
export default async function SignInPage() {
await connection();

return (
<div className="flex min-h-screen items-center justify-center p-6">
<ClerkSignIn />
</div>
);
return <ClerkSignIn />;
}
6 changes: 1 addition & 5 deletions app/(auth)/sign-up/[[...sign-up]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,5 @@ import { ClerkSignUp } from "@/components/auth/clerk-signup";
export default async function SignUpPage() {
await connection();

return (
<div className="flex min-h-screen items-center justify-center p-6">
<ClerkSignUp />
</div>
);
return <ClerkSignUp />;
}
3 changes: 3 additions & 0 deletions app/(root)/(no-right-sidebar)/community/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function CommunityPage() {
return <h1>Community</h1>;
}
Comment on lines +1 to +3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

LGTM! Consider extracting placeholder helper.

The page correctly remains a server component and provides a simple heading placeholder. However, this pattern is duplicated across six placeholder pages (AskQuestionPage, CollectionsPage, CommunityPage, JobsPage, ProfilePage, TagsPage).

🔎 Optional: Extract a reusable placeholder helper to reduce duplication

If you'd like to reduce duplication, consider creating a helper:

// lib/create-placeholder-page.tsx
export function createPlaceholderPage(title: string) {
  return function PlaceholderPage() {
    return <h1>{title}</h1>;
  };
}

Then each page becomes:

import { createPlaceholderPage } from "@/lib/create-placeholder-page";

export default createPlaceholderPage("Community");

This is optional for temporary scaffolding; the current approach is acceptable.

🤖 Prompt for AI Agents
In app/(root)/(no-right-sidebar)/community/page.tsx lines 1-3: this placeholder
H1 is duplicated across six similar pages; extract a reusable helper to reduce
duplication by adding a new helper file (e.g., lib/create-placeholder-page.tsx)
that exports a factory function taking a title and returning a server component
that renders the H1, then update this page to import that factory and export the
created placeholder for "Community" instead of the inline component; keep files
as server components and ensure the helper is typed/exported for reuse.

3 changes: 3 additions & 0 deletions app/(root)/(no-right-sidebar)/jobs/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function JobsPage() {
return <h1>Find Jobs</h1>;
}
7 changes: 7 additions & 0 deletions app/(root)/(no-right-sidebar)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Full-width layout without right sidebar
// Layout: [LeftSidebar (sticky)] [Main Content]
const NoRightSidebarLayout = ({ children }: { children: React.ReactNode }) => {
return <main className="flex-1 px-6 py-10 md:px-8 lg:px-12">{children}</main>;
};

export default NoRightSidebarLayout;
3 changes: 3 additions & 0 deletions app/(root)/(no-right-sidebar)/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function ProfilePage() {
return <h1>Profile</h1>;
}
3 changes: 3 additions & 0 deletions app/(root)/(no-right-sidebar)/tags/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function TagsPage() {
return <h1>Tags</h1>;
}
3 changes: 3 additions & 0 deletions app/(root)/(with-right-sidebar)/ask-question/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function AskQuestionPage() {
return <h1>Ask a Question</h1>;
}
3 changes: 3 additions & 0 deletions app/(root)/(with-right-sidebar)/collections/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function CollectionsPage() {
return <h1>Collections</h1>;
}
11 changes: 11 additions & 0 deletions app/(root)/(with-right-sidebar)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// TODO: Add <RightSidebar> for tag widgets, hot questions, etc.
// Layout: [LeftSidebar (sticky)] [Main Content] [RightSidebar]
const WithRightSidebarLayout = ({
children,
}: {
children: React.ReactNode;
}) => {
return <main className="flex-1 px-6 py-10 md:px-8 lg:px-12">{children}</main>;
};

export default WithRightSidebarLayout;
31 changes: 31 additions & 0 deletions app/(root)/(with-right-sidebar)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const Home = () => (
<>
<h1>Hello Root page with heading H1</h1>

{/* Header boundary marker */}
<div className="mt-8 bg-primary/30 p-4">HEADER: {"words ".repeat(50)}</div>

{/* Flexbox demo: grow vs flex-none */}
<div className="my-8 flex gap-4 border-2 border-dashed border-primary p-4">
{/* Fixed width - won't grow */}
<div className="flex h-24 w-24 flex-none items-center justify-center rounded-lg border-2 border-dashed border-primary bg-sky-500/20 text-sm font-medium">
flex-none
</div>

{/* Grows to fill available space */}
<div className="flex h-24 grow items-center justify-center rounded-lg border-2 border-dashed border-primary bg-indigo-500/30 text-sm font-medium">
grow
</div>

{/* Fixed width - won't grow */}
<div className="flex h-24 w-24 flex-none items-center justify-center rounded-lg border-2 border-dashed border-primary bg-sky-500/20 text-sm font-medium">
flex-none
</div>
</div>

{/* Footer boundary marker */}
<div className="bg-primary/30 p-4">FOOTER: {"words ".repeat(50)}</div>
</>
);

export default Home;
Comment on lines +1 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove debug scaffolding before merging.

The page contains obvious debug/test code including repeated words, visual debug borders, explicit layout markers, and testing banners. Whilst the PR notes acknowledge this scaffolding exists, debug code should not be committed to the main branch.

🔎 Apply this diff to replace with a proper placeholder:
-const Home = () => (
-  <>
-    <h1>Hello Root page with heading H1</h1>
-
-    {/* Header boundary marker */}
-    <div className="mt-8 bg-primary/30 p-4">HEADER: {"words ".repeat(50)}</div>
-
-    {/* Flexbox demo: grow vs flex-none */}
-    <div className="my-8 flex gap-4 border-2 border-dashed border-primary p-4">
-      {/* Fixed width - won't grow */}
-      <div className="flex h-24 w-24 flex-none items-center justify-center rounded-lg border-2 border-dashed border-primary bg-sky-500/20 text-sm font-medium">
-        flex-none
-      </div>
-
-      {/* Grows to fill available space */}
-      <div className="flex h-24 grow items-center justify-center rounded-lg border-2 border-dashed border-primary bg-indigo-500/30 text-sm font-medium">
-        grow
-      </div>
-
-      {/* Fixed width - won't grow */}
-      <div className="flex h-24 w-24 flex-none items-center justify-center rounded-lg border-2 border-dashed border-primary bg-sky-500/20 text-sm font-medium">
-        flex-none
-      </div>
-    </div>
-
-    {/* Footer boundary marker */}
-    <div className="bg-primary/30 p-4">FOOTER: {"words ".repeat(50)}</div>
-  </>
-);
+const Home = () => <h1>Home</h1>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Home = () => (
<>
<h1>Hello Root page with heading H1</h1>
{/* Header boundary marker */}
<div className="mt-8 bg-primary/30 p-4">HEADER: {"words ".repeat(50)}</div>
{/* Flexbox demo: grow vs flex-none */}
<div className="my-8 flex gap-4 border-2 border-dashed border-primary p-4">
{/* Fixed width - won't grow */}
<div className="flex h-24 w-24 flex-none items-center justify-center rounded-lg border-2 border-dashed border-primary bg-sky-500/20 text-sm font-medium">
flex-none
</div>
{/* Grows to fill available space */}
<div className="flex h-24 grow items-center justify-center rounded-lg border-2 border-dashed border-primary bg-indigo-500/30 text-sm font-medium">
grow
</div>
{/* Fixed width - won't grow */}
<div className="flex h-24 w-24 flex-none items-center justify-center rounded-lg border-2 border-dashed border-primary bg-sky-500/20 text-sm font-medium">
flex-none
</div>
</div>
{/* Footer boundary marker */}
<div className="bg-primary/30 p-4">FOOTER: {"words ".repeat(50)}</div>
</>
);
export default Home;
const Home = () => <h1>Home</h1>;
export default Home;
🤖 Prompt for AI Agents
In app/(root)/(with-right-sidebar)/page.tsx lines 1-31, the file contains debug
scaffolding (repeated "words", header/footer markers, dashed borders, and
flexbox demo blocks) that should be removed before merging; delete the visual
testing markup and repeated text and replace it with a minimal production
placeholder (e.g., a single H1 and a short descriptive paragraph or empty
container) while preserving the component signature and default export, and
ensure no leftover test classes or comments remain.

7 changes: 0 additions & 7 deletions app/(root)/page.tsx

This file was deleted.

34 changes: 24 additions & 10 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-mobile-nav: var(--mobile-nav);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
Expand All @@ -54,7 +55,14 @@

:root {
/* Creates CSS variables available to all elements (no utility generation) */
/* Only defined in root, not .dark */
--radius: 0.625rem;
--primary-gradient-to: oklch(0.7434 0.115 58.23); /* #E2995F */
--gradient-primary: linear-gradient(
93.22deg,
var(--primary) -13.95%,
var(--primary-gradient-to) 99.54%
);

--background: oklch(0.994 0 0); /* #FDFDFD */
--foreground: oklch(0.129 0.042 264.695);
Expand All @@ -64,7 +72,7 @@
--popover-foreground: oklch(0.129 0.042 264.695);
--primary: oklch(0.7089 0.1967 46.81);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.968 0.007 247.896);
--secondary: oklch(0.9147 0.0205 264.47); /* #DCE3F1 - Figma */
--secondary-foreground: oklch(0.208 0.042 265.755);
--muted: oklch(0.968 0.007 247.896);
--muted-foreground: oklch(0.554 0.046 257.417);
Expand All @@ -79,23 +87,19 @@
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(1 0 0); /* #FFFFFF */
--sidebar: oklch(1 0 0); /* Figma #ffffff */
--sidebar-foreground: oklch(0.2102 0.0185 270.39); /* #151821 */
--sidebar-primary: oklch(0.208 0.042 265.755);
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
--sidebar-accent: oklch(0.968 0.007 247.896);
--sidebar-accent-foreground: oklch(0.208 0.042 265.755);
--sidebar-border: oklch(0.929 0.013 255.508);
--sidebar-ring: oklch(0.704 0.04 256.788);
--primary-gradient-to: oklch(0.7434 0.115 58.23); /* #E2995F */
--gradient-primary: linear-gradient(
93.22deg,
var(--primary) -13.95%,
var(--primary-gradient-to) 99.54%
);
--mobile-nav: oklch(1 0 0); /* #FFFFFF */

/* Centralised logo theming */
--logo-full-themed: url("/images/logo-light.svg");
--auth-bg: url("/images/auth-bg-light.webp");
}

.dark {
Expand All @@ -108,7 +112,7 @@
--popover-foreground: oklch(0.984 0.003 247.858);
--primary: oklch(0.7089 0.1967 46.81);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.279 0.041 260.031);
--secondary: oklch(0.2728 0.0257 265.4); /* #212734 - Figma */
--secondary-foreground: oklch(0.984 0.003 247.858);
--muted: oklch(0.279 0.041 260.031);
--muted-foreground: oklch(0.704 0.04 256.788);
Expand All @@ -123,17 +127,19 @@
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.178 0.013 270.6); /* #0F1117 */
--sidebar: oklch(0.1783 0.0128 270.6); /* Figma #0F1117 */
--sidebar-foreground: oklch(1 0 0); /* #FFFFFF */
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
--sidebar-accent: oklch(0.279 0.041 260.031);
--sidebar-accent-foreground: oklch(0.984 0.003 247.858);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.551 0.027 264.364);
--mobile-nav: oklch(0.1288 0.0406 264.7); /* #07080b */

/* Centralised logo theming (dark) */
--logo-full-themed: url("/images/logo-dark.svg");
--auth-bg: url("/images/auth-bg-dark.webp");
}

@layer base {
Expand Down Expand Up @@ -187,3 +193,11 @@
@apply overflow-x-auto rounded-lg bg-muted p-4 text-sm;
}
}

@layer utilities {
/* Custom SVG icons use hardcoded white fill. Since <Image> loads them as external
files, CSS currentColor won't work. Invert to black in light mode instead. */
.invert-colors {
@apply invert dark:invert-0;
}
}
Loading
Loading