Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
9fcda3c
feat(auth): add visible focus state to error dismiss button
google-labs-jules[bot] Jan 6, 2026
7a93085
feat(seo): Align robots.txt with sitemap exclusions
google-labs-jules[bot] Jan 6, 2026
d02e310
Merge pull request #32 from lynxx-st/streamline-auth-focus-fix-394368…
lynxx-st Jan 6, 2026
0d768ad
Merge pull request #33 from lynxx-st/jules-index-robots-txt-cleanup-4…
lynxx-st Jan 6, 2026
3b92433
Merge pull request #34 from FlagForgeCTF/mainv2
lynxx-st Jan 6, 2026
495e332
feat(auth): improve external link UX and accessibility
google-labs-jules[bot] Jan 7, 2026
9fc1f68
Merge pull request #35 from lynxx-st/palette/ux-external-links-140738…
lynxx-st Jan 7, 2026
792ff09
feat(auth): remove loading components on 3 static pages (home, about,…
lynxx-st Jan 7, 2026
3cf4a23
Merge branch 'mainv2' of https://github.com/lynxx-st/flagForge into m…
lynxx-st Jan 7, 2026
5096eba
🎨 Palette: Add hover state to dropdown sub-trigger
google-labs-jules[bot] Jan 7, 2026
5150197
Merge pull request #38 from lynxx-st/palette-dropdown-hover--style-up…
lynxx-st Jan 7, 2026
6c9ef84
This commit adds a smooth rotation and scale animation to the theme t…
google-labs-jules[bot] Jan 8, 2026
c9a15ea
🎨 Palette: Improve Dropdown Menu Focus Visibility
google-labs-jules[bot] Jan 8, 2026
49b4b81
feat(ux): Improve UX for completed challenge cards
google-labs-jules[bot] Jan 8, 2026
446e7c6
🎨 Palette: Enhance tooltip accessibility and animation
google-labs-jules[bot] Jan 9, 2026
db18e29
feat(home): Group Start Challenge link and icon
google-labs-jules[bot] Jan 9, 2026
aec5ce8
🧭 Streamline: Add visual feedback for incorrect flag submissions
google-labs-jules[bot] Jan 9, 2026
11ba086
🛡️ Sentinel: [HIGH] Fix XSS Vulnerability in Markdown Rendering
google-labs-jules[bot] Jan 10, 2026
1d5fc81
feat(a11y): add aria-label to mobile menu button
google-labs-jules[bot] Jan 12, 2026
5116318
feat(ux): Hide pagination on empty search
google-labs-jules[bot] Jan 12, 2026
9e4574e
🎨 Palette: Add ARIA labels to chat buttons for accessibility
google-labs-jules[bot] Jan 13, 2026
91ea9c4
Merge pull request #42 from lynxx-st/feat/animate-theme-toggle-626396…
lynxx-st Jan 13, 2026
d945a2a
Merge pull request #45 from lynxx-st/palette/dropdown-focus-styles-77…
lynxx-st Jan 13, 2026
4f85262
Merge pull request #48 from lynxx-st/streamline-completed-challenge-u…
lynxx-st Jan 13, 2026
c7ecd95
Merge pull request #49 from lynxx-st/palette-tooltip-enhancement-4409…
lynxx-st Jan 13, 2026
4cf702f
Merge pull request #52 from lynxx-st/palette-grouped-start-challenge-…
lynxx-st Jan 13, 2026
f946faf
Merge pull request #54 from lynxx-st/streamline-submission-feedback-4…
lynxx-st Jan 13, 2026
b0650fa
Merge pull request #56 from lynxx-st/sentinel-xss-fix-rehype-order-68…
lynxx-st Jan 13, 2026
b5213a3
Merge pull request #75 from lynxx-st/streamline-fix-pagination-ux-161…
lynxx-st Jan 13, 2026
1750e99
Merge pull request #76 from lynxx-st/palette-chat-button-aria-labels-…
lynxx-st Jan 13, 2026
9815775
🎨 Palette: Make Latest Challenge card a single clickable link
google-labs-jules[bot] Jan 13, 2026
2ee56d6
feat(security): harden Permissions-Policy header
google-labs-jules[bot] Jan 13, 2026
1e51b59
💡 What changed
google-labs-jules[bot] Jan 13, 2026
6d086b8
🎨 Palette: Improve Pagination Button Accessibility
google-labs-jules[bot] Jan 14, 2026
13a8d40
Merge pull request #82 from lynxx-st/streamline-add-validation-feedba…
lynxx-st Jan 14, 2026
fde63e7
Merge pull request #79 from lynxx-st/palette-clickable-challenge-card…
lynxx-st Jan 14, 2026
fa33b95
Merge pull request #80 from lynxx-st/sentinel-harden-permissions-poli…
lynxx-st Jan 14, 2026
8c17be5
Merge pull request #72 from lynxx-st/palette-add-aria-label-to-menu-b…
lynxx-st Jan 14, 2026
6ff24b2
Merge pull request #83 from lynxx-st/palette/pagination-a11y-7824-121…
lynxx-st Jan 14, 2026
08b3c4c
Merge pull request #85 from FlagForgeCTF/mainv2
lynxx-st Jan 14, 2026
1625557
feat: add hint count tracking and update blog URLs
lynxx-st Jan 14, 2026
4a57527
feat(ui): enhance challenge completion page with detailed stats
lynxx-st Jan 14, 2026
a572016
feat(problems): add practice mode for redoing challenges without scor…
lynxx-st Jan 14, 2026
c6f7ddf
Merge branch 'FlagForgeCTF-mainv2' into pr-140, resolve conflicts, an…
shimonenator Jan 21, 2026
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
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-24 - Accessible Icon-Only Buttons
**Learning:** Icon-only buttons are a common source of accessibility issues. Without a text label, screen reader users have no way of knowing the button's function.
**Action:** Always add a descriptive `aria-label` to any button that does not contain descriptive text. This provides a clear, accessible name for the button that screen readers can announce.
5 changes: 5 additions & 0 deletions .Jules/streamline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 2024-07-25 - Provide Immediate Feedback for Invalid User Actions

**Learning:** When a user action cannot be completed (e.g., submitting a form), the system must provide immediate, clear, and actionable feedback. Silently blocking an action, even with client-side validation, creates a confusing and frustrating user experience. Users are left wondering if the system is broken or if they did something wrong.

**Action:** In any form or user input flow, always connect client-side validation logic directly to the UI. If a check fails, display a descriptive, temporary message that explains *why* the action was blocked and what the user should do next. This transforms a moment of friction into a moment of guidance, improving user confidence and flow.
4 changes: 2 additions & 2 deletions app/(footer)/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ export default function About() {
<h1 className="text-5xl lg:text-7xl font-black tracking-tighter leading-[0.9] text-gray-900 dark:text-white">
About <span className="text-red-400 dark:text-red-500">FlagForge</span>
</h1>
<p className="text-md text-gray-500 dark:text-gray-400 italic font-medium tracking-wide uppercase">
<h2 className="text-md text-gray-500 dark:text-gray-400 italic font-medium tracking-wide uppercase">
"Where curiosity meets cybersecurity."
</p>
</h2>
<p className="text-lg lg:text-xl font-medium text-gray-600 dark:text-gray-300 max-w-3xl mx-auto leading-relaxed">
FlagForge is a dynamic and engaging CTF platform dedicated to promoting{" "}
<span className="text-red-500 dark:text-red-500 font-black">
Expand Down
16 changes: 16 additions & 0 deletions app/(main)/authentication/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Metadata } from "next";
import type { ReactNode } from "react";

export const metadata: Metadata = {
title: "Sign In & Sign Up",
description:
"Sign in to FlagForge to access CTF challenges, track progress, and compete on the leaderboard.",
};

export default function AuthenticationLayout({
children,
}: {
children: ReactNode;
}) {
return children;
}
21 changes: 12 additions & 9 deletions app/(main)/authentication/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import React, { useEffect, useState } from "react";
import { signIn, useSession } from "next-auth/react";
import { FcGoogle } from "react-icons/fc";
import { useRouter, useSearchParams } from "next/navigation";
import { Flame, ShieldCheck, Sparkles, Orbit, ArrowRight, AlertCircle, Home as HomeIcon, X, Loader2 } from "lucide-react";
import { Flame, ShieldCheck, Sparkles, Orbit, ArrowRight, AlertCircle, Home as HomeIcon, X, Loader2, ExternalLink } from "lucide-react";
import Link from "next/link";
import Loading from "@/components/loading";

const AuthPage = () => {
const router = useRouter();
Expand Down Expand Up @@ -47,10 +46,6 @@ const AuthPage = () => {
}
}, [sessionStatus, router, errorStatus, callbackUrl]);

if (sessionStatus === "loading") {
return <Loading />;
}

if (sessionStatus === "authenticated" && !errorStatus) {
return null;
}
Expand Down Expand Up @@ -141,7 +136,7 @@ const AuthPage = () => {
<button
onClick={() => setErrorStatus(null)}
aria-label="Dismiss error"
className="absolute top-0 right-0 p-1 hover:bg-red-500/10 rounded-lg transition-colors group/btn"
className="absolute top-0 right-0 p-1 hover:bg-red-500/10 rounded-lg transition-colors group/btn focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2 focus-visible:ring-offset-red-500/10"
>
<X className="w-4 h-4 text-gray-400 group-hover/btn:text-red-500" />
</button>
Expand Down Expand Up @@ -222,9 +217,17 @@ const AuthPage = () => {

<p className="text-[10px] leading-relaxed text-gray-400 dark:text-white-600 font-bold uppercase tracking-wider">
<span className="mx-2 opacity-70">By signing up, you agree to our <br></br></span>
<Link href="/privacy-policy" className="text-red-500/80 hover:text-red-500 hover:underline underline-offset-4 decoration-2 transition-colors">Privacy Policy</Link>
<Link href="/privacy-policy" className="text-red-500/80 hover:text-red-500 hover:underline underline-offset-4 decoration-2 transition-colors inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
Privacy Policy
<ExternalLink className="w-3 h-3" />
<span className="sr-only">(opens in new tab)</span>
</Link>
<span className="mx-2 opacity-50">&</span>
<Link href="/terms-of-service" className="text-red-500/80 hover:text-red-500 hover:underline underline-offset-4 decoration-2 transition-colors">Terms & Conditions</Link>
<Link href="/terms-of-service" className="text-red-500/80 hover:text-red-500 hover:underline underline-offset-4 decoration-2 transition-colors inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
Terms & Conditions
<ExternalLink className="w-3 h-3" />
<span className="sr-only">(opens in new tab)</span>
</Link>
</p>
</div>

Expand Down
2 changes: 1 addition & 1 deletion app/(main)/blogs/[id]/BlogPostClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ export default function BlogPostClient({
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[
rehypeSanitize, // Sanitizes HTML to prevent XSS
rehypeHighlight, // Syntax highlighting for code blocks
rehypeSanitize, // Sanitizes HTML to prevent XSS
]}
components={{
// Custom component styling
Expand Down
21 changes: 12 additions & 9 deletions app/(main)/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from "lucide-react";
import ArchivesSection from "@/components/ArchivesSection";
import ScoreboardSection from "@/components/ScoreboardSection";
import InstagramFeed from "@/components/InstagramFeed";

interface UserStats {
totalScore: number;
Expand Down Expand Up @@ -211,7 +212,7 @@ const Home = () => {
<div className="pointer-events-none absolute -bottom-32 -left-24 h-72 w-72 rounded-full bg-[radial-gradient(circle_at_center,rgba(244,63,94,0.18),rgba(244,63,94,0))] blur-3xl" />

<div className="relative z-10">
<div className="relative overflow-hidden transition-colors duration-300">
<div className="relative overflow-x-hidden overflow-y-visible transition-colors duration-300">
<div className="absolute inset-0 opacity-30">
<div
className="absolute inset-0"
Expand Down Expand Up @@ -453,10 +454,13 @@ const Home = () => {
</h2>
</div>
{latestRoom ? (
<div className="bg-white/70 dark:bg-white/[0.04] border border-white/60 dark:border-white/10 rounded-2xl p-5 hover:-translate-y-1 transition-all duration-300 cursor-pointer shadow-lg">
<Link
href={`/problems/${latestRoom._id}`}
className="group block bg-white/70 dark:bg-white/[0.04] border border-white/60 dark:border-white/10 rounded-2xl p-5 hover:-translate-y-1 transition-all duration-300 shadow-lg"
>
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 hover:text-red-500 dark:hover:text-red-500 transition-colors">
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 group-hover:text-red-500 dark:group-hover:text-red-500 transition-colors">
{latestRoom.title}
</h3>
<p className={` text-sm text-gray-600 dark:text-gray-400 mt-1 transition-colors duration-300`}>
Expand All @@ -477,20 +481,16 @@ const Home = () => {
</div>
</div>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center">
<span
className={`px-3 py-1 rounded-full text-xs font-semibold ${getCategoryColor(
latestRoom.category
)}`}
>
{latestRoom.category}
</span>
<div className="flex items-center gap-2 text-sm text-red-500 dark:text-red-500 hover:text-red-600 dark:hover:text-red-500 transition-colors duration-300">
<PlayCircle className="h-4 w-4" />
<Link href="/problems"> Start Challenge </Link>
</div>
</div>
</div>
</Link>
) : (
<div className="text-center py-6 text-gray-500 dark:text-gray-400 transition-colors duration-300">
<Clock className="h-8 w-8 mx-auto mb-2 opacity-50" />
Expand Down Expand Up @@ -610,6 +610,9 @@ const Home = () => {

{/* Scoreboards Section */}
<ScoreboardSection />

{/* Instagram Feed */}
<InstagramFeed />
</div>
</div>
</div>
Expand Down
Loading
Loading