THRISHAL12345 commited on
Commit
b50f30a
Β·
1 Parent(s): 10f60a6

feat: implement curated themes and profile integration

Browse files
backend/requirements.txt CHANGED
@@ -65,5 +65,5 @@ python-magic; sys_platform != "win32"
65
  python-docx
66
  pypdf
67
  reportlab
68
- crawl4ai
69
- ddgs
 
65
  python-docx
66
  pypdf
67
  reportlab
68
+ # crawl4ai
69
+ # ddgs
frontend/src/app/globals.css CHANGED
@@ -142,6 +142,93 @@
142
  --sidebar-ring: oklch(0.55 0.23 265);
143
  }
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  @layer base {
146
  * {
147
  @apply border-border outline-ring/50;
 
142
  --sidebar-ring: oklch(0.55 0.23 265);
143
  }
144
 
145
+ .ocean {
146
+ --background: oklch(0.18 0.05 250);
147
+ --foreground: oklch(0.98 0.02 240);
148
+ --card: oklch(0.22 0.06 250);
149
+ --card-foreground: oklch(0.98 0.02 240);
150
+ --popover: oklch(0.22 0.06 250);
151
+ --popover-foreground: oklch(0.98 0.02 240);
152
+ --primary: oklch(0.65 0.15 220);
153
+ --primary-foreground: oklch(0.98 0.02 240);
154
+ --secondary: oklch(0.28 0.07 250);
155
+ --secondary-foreground: oklch(0.98 0.02 240);
156
+ --muted: oklch(0.28 0.07 250);
157
+ --muted-foreground: oklch(0.7 0.05 240);
158
+ --accent: oklch(0.65 0.15 220);
159
+ --accent-foreground: oklch(0.98 0.02 240);
160
+ --destructive: oklch(0.55 0.2 25);
161
+ --border: oklch(0.35 0.08 250 / 50%);
162
+ --input: oklch(0.35 0.08 250 / 50%);
163
+ --ring: oklch(0.65 0.15 220);
164
+ --sidebar: oklch(0.16 0.05 250);
165
+ --sidebar-foreground: oklch(0.98 0.02 240);
166
+ --sidebar-primary: oklch(0.65 0.15 220);
167
+ --sidebar-primary-foreground: oklch(0.98 0.02 240);
168
+ --sidebar-accent: oklch(0.22 0.06 250);
169
+ --sidebar-accent-foreground: oklch(0.98 0.02 240);
170
+ --sidebar-border: oklch(0.35 0.08 250 / 50%);
171
+ --sidebar-ring: oklch(0.65 0.15 220);
172
+ }
173
+
174
+ .forest {
175
+ --background: oklch(0.2 0.04 150);
176
+ --foreground: oklch(0.98 0.02 140);
177
+ --card: oklch(0.24 0.05 150);
178
+ --card-foreground: oklch(0.98 0.02 140);
179
+ --popover: oklch(0.24 0.05 150);
180
+ --popover-foreground: oklch(0.98 0.02 140);
181
+ --primary: oklch(0.6 0.12 140);
182
+ --primary-foreground: oklch(0.98 0.02 140);
183
+ --secondary: oklch(0.3 0.06 150);
184
+ --secondary-foreground: oklch(0.98 0.02 140);
185
+ --muted: oklch(0.3 0.06 150);
186
+ --muted-foreground: oklch(0.7 0.05 140);
187
+ --accent: oklch(0.6 0.12 140);
188
+ --accent-foreground: oklch(0.98 0.02 140);
189
+ --destructive: oklch(0.55 0.2 25);
190
+ --border: oklch(0.35 0.07 150 / 50%);
191
+ --input: oklch(0.35 0.07 150 / 50%);
192
+ --ring: oklch(0.6 0.12 140);
193
+ --sidebar: oklch(0.18 0.04 150);
194
+ --sidebar-foreground: oklch(0.98 0.02 140);
195
+ --sidebar-primary: oklch(0.6 0.12 140);
196
+ --sidebar-primary-foreground: oklch(0.98 0.02 140);
197
+ --sidebar-accent: oklch(0.24 0.05 150);
198
+ --sidebar-accent-foreground: oklch(0.98 0.02 140);
199
+ --sidebar-border: oklch(0.35 0.07 150 / 50%);
200
+ --sidebar-ring: oklch(0.6 0.12 140);
201
+ }
202
+
203
+ .sunset {
204
+ --background: oklch(0.18 0.06 330);
205
+ --foreground: oklch(0.98 0.03 350);
206
+ --card: oklch(0.22 0.08 330);
207
+ --card-foreground: oklch(0.98 0.03 350);
208
+ --popover: oklch(0.22 0.08 330);
209
+ --popover-foreground: oklch(0.98 0.03 350);
210
+ --primary: oklch(0.65 0.18 15);
211
+ --primary-foreground: oklch(0.98 0.03 350);
212
+ --secondary: oklch(0.28 0.09 330);
213
+ --secondary-foreground: oklch(0.98 0.03 350);
214
+ --muted: oklch(0.28 0.09 330);
215
+ --muted-foreground: oklch(0.7 0.06 340);
216
+ --accent: oklch(0.65 0.18 15);
217
+ --accent-foreground: oklch(0.98 0.03 350);
218
+ --destructive: oklch(0.55 0.2 25);
219
+ --border: oklch(0.35 0.1 330 / 50%);
220
+ --input: oklch(0.35 0.1 330 / 50%);
221
+ --ring: oklch(0.65 0.18 15);
222
+ --sidebar: oklch(0.16 0.06 330);
223
+ --sidebar-foreground: oklch(0.98 0.03 350);
224
+ --sidebar-primary: oklch(0.65 0.18 15);
225
+ --sidebar-primary-foreground: oklch(0.98 0.03 350);
226
+ --sidebar-accent: oklch(0.22 0.08 330);
227
+ --sidebar-accent-foreground: oklch(0.98 0.03 350);
228
+ --sidebar-border: oklch(0.35 0.1 330 / 50%);
229
+ --sidebar-ring: oklch(0.65 0.18 15);
230
+ }
231
+
232
  @layer base {
233
  * {
234
  @apply border-border outline-ring/50;
frontend/src/app/layout.tsx CHANGED
@@ -33,6 +33,7 @@ export default function RootLayout({
33
  defaultTheme="dark"
34
  enableSystem={false}
35
  disableTransitionOnChange
 
36
  >
37
  <AuthProvider>
38
  <I18nProvider>
 
33
  defaultTheme="dark"
34
  enableSystem={false}
35
  disableTransitionOnChange
36
+ themes={["light", "dark", "ocean", "forest", "sunset"]}
37
  >
38
  <AuthProvider>
39
  <I18nProvider>
frontend/src/app/page.tsx CHANGED
@@ -3,7 +3,8 @@
3
  import { useEffect, useState } from "react";
4
  import { useRouter } from "next/navigation";
5
  import { useAuth } from "@/lib/auth";
6
- import { FileText, MessageSquare, Brain, Shield, Zap, Search } from "lucide-react";
 
7
  import { Button } from "@/components/ui/button";
8
  import Link from "next/link";
9
  import ContributorsPanel from "@/components/layout/ContributorsPanel";
@@ -13,6 +14,19 @@ export default function HomePage() {
13
  const { user, loading } = useAuth();
14
  const router = useRouter();
15
  const [hallOfFameOpen, setHallOfFameOpen] = useState(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  useEffect(() => {
18
  if (!loading && user) {
@@ -29,7 +43,19 @@ export default function HomePage() {
29
  }
30
 
31
  return (
32
- <div className="min-h-screen flex flex-col">
 
 
 
 
 
 
 
 
 
 
 
 
33
  {/* ── Hero ────────────────────────────────────── */}
34
  <div className="flex-1 flex flex-col items-center justify-center px-6 py-20">
35
  {/* Glow effect */}
 
3
  import { useEffect, useState } from "react";
4
  import { useRouter } from "next/navigation";
5
  import { useAuth } from "@/lib/auth";
6
+ import { FileText, MessageSquare, Brain, Shield, Zap, Search, Sun, Moon } from "lucide-react";
7
+ import { useTheme } from "next-themes";
8
  import { Button } from "@/components/ui/button";
9
  import Link from "next/link";
10
  import ContributorsPanel from "@/components/layout/ContributorsPanel";
 
14
  const { user, loading } = useAuth();
15
  const router = useRouter();
16
  const [hallOfFameOpen, setHallOfFameOpen] = useState(false);
17
+ const { theme, setTheme } = useTheme();
18
+ const [mounted, setMounted] = useState(false);
19
+
20
+ useEffect(() => {
21
+ setMounted(true);
22
+ }, []);
23
+
24
+ useEffect(() => {
25
+ // Force only light/dark mode on the home page
26
+ if (mounted && theme !== "light" && theme !== "dark") {
27
+ setTheme("dark");
28
+ }
29
+ }, [mounted, theme, setTheme]);
30
 
31
  useEffect(() => {
32
  if (!loading && user) {
 
43
  }
44
 
45
  return (
46
+ <div className="min-h-screen flex flex-col relative">
47
+ {mounted && (
48
+ <Button
49
+ variant="ghost"
50
+ size="icon"
51
+ className="absolute top-4 right-4 z-50 rounded-full"
52
+ onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
53
+ title="Toggle theme"
54
+ >
55
+ {theme === "dark" ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
56
+ </Button>
57
+ )}
58
+
59
  {/* ── Hero ────────────────────────────────────── */}
60
  <div className="flex-1 flex flex-col items-center justify-center px-6 py-20">
61
  {/* Glow effect */}
frontend/src/components/layout/Header.tsx CHANGED
@@ -11,6 +11,12 @@ import {
11
  DropdownMenuItem,
12
  DropdownMenuSeparator,
13
  DropdownMenuTrigger,
 
 
 
 
 
 
14
  } from "@/components/ui/dropdown-menu";
15
  import {
16
  Brain,
@@ -19,12 +25,12 @@ import {
19
  PanelRightClose,
20
  PanelRightOpen,
21
  LogOut,
22
- Moon,
23
- Sun,
24
  Menu,
25
  X,
 
 
 
26
  } from "lucide-react";
27
- import { Briefcase, ChevronDown } from "lucide-react";
28
  import { useWorkspaceStore, WORKSPACES, type WorkspaceId } from "@/store/workspace-store";
29
  import { api } from "@/lib/api";
30
  import { useTheme } from "next-themes";
@@ -59,9 +65,6 @@ export default function Header({
59
  const workspace = useWorkspaceStore((s) => s.workspace);
60
  const setWorkspace = useWorkspaceStore((s) => s.setWorkspace);
61
 
62
- const isDark = theme === "dark";
63
- const toggleTheme = () => setTheme(isDark ? "light" : "dark");
64
-
65
  const handleLogout = () => {
66
  logout();
67
  router.replace("/login");
@@ -137,18 +140,6 @@ export default function Header({
137
  )}
138
  </Button>
139
 
140
- {mounted && (
141
- <Button
142
- variant="ghost"
143
- size="icon"
144
- className="h-8 w-8"
145
- onClick={toggleTheme}
146
- title={isDark ? "Light mode" : "Dark mode"}
147
- >
148
- {isDark ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
149
- </Button>
150
- )}
151
-
152
  {/* Workspace switcher */}
153
  <DropdownMenu>
154
  <DropdownMenuTrigger className="flex items-center h-8 gap-2 px-2 rounded-md hover:bg-accent transition-colors cursor-pointer">
@@ -175,7 +166,7 @@ export default function Header({
175
  <DropdownMenu>
176
  <DropdownMenuTrigger className="flex items-center h-8 gap-2 px-2 rounded-md hover:bg-accent transition-colors cursor-pointer">
177
  <Avatar className="w-6 h-6">
178
- <AvatarFallback className="text-[10px] bg-primary/20 text-primary">
179
  {user?.username?.slice(0, 2).toUpperCase() || "U"}
180
  </AvatarFallback>
181
  </Avatar>
@@ -187,6 +178,24 @@ export default function Header({
187
  <p className="text-xs text-muted-foreground truncate">{user?.email}</p>
188
  </div>
189
  <DropdownMenuSeparator />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  <DropdownMenuItem
191
  className="text-destructive cursor-pointer"
192
  onClick={handleLogout}
 
11
  DropdownMenuItem,
12
  DropdownMenuSeparator,
13
  DropdownMenuTrigger,
14
+ DropdownMenuSub,
15
+ DropdownMenuSubTrigger,
16
+ DropdownMenuSubContent,
17
+ DropdownMenuPortal,
18
+ DropdownMenuRadioGroup,
19
+ DropdownMenuRadioItem,
20
  } from "@/components/ui/dropdown-menu";
21
  import {
22
  Brain,
 
25
  PanelRightClose,
26
  PanelRightOpen,
27
  LogOut,
 
 
28
  Menu,
29
  X,
30
+ Palette,
31
+ Briefcase,
32
+ ChevronDown
33
  } from "lucide-react";
 
34
  import { useWorkspaceStore, WORKSPACES, type WorkspaceId } from "@/store/workspace-store";
35
  import { api } from "@/lib/api";
36
  import { useTheme } from "next-themes";
 
65
  const workspace = useWorkspaceStore((s) => s.workspace);
66
  const setWorkspace = useWorkspaceStore((s) => s.setWorkspace);
67
 
 
 
 
68
  const handleLogout = () => {
69
  logout();
70
  router.replace("/login");
 
140
  )}
141
  </Button>
142
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  {/* Workspace switcher */}
144
  <DropdownMenu>
145
  <DropdownMenuTrigger className="flex items-center h-8 gap-2 px-2 rounded-md hover:bg-accent transition-colors cursor-pointer">
 
166
  <DropdownMenu>
167
  <DropdownMenuTrigger className="flex items-center h-8 gap-2 px-2 rounded-md hover:bg-accent transition-colors cursor-pointer">
168
  <Avatar className="w-6 h-6">
169
+ <AvatarFallback className="text-[10px] bg-primary text-primary-foreground">
170
  {user?.username?.slice(0, 2).toUpperCase() || "U"}
171
  </AvatarFallback>
172
  </Avatar>
 
178
  <p className="text-xs text-muted-foreground truncate">{user?.email}</p>
179
  </div>
180
  <DropdownMenuSeparator />
181
+ <DropdownMenuSub>
182
+ <DropdownMenuSubTrigger className="cursor-pointer">
183
+ <Palette className="w-4 h-4 mr-2" />
184
+ Theme
185
+ </DropdownMenuSubTrigger>
186
+ <DropdownMenuPortal>
187
+ <DropdownMenuSubContent>
188
+ <DropdownMenuRadioGroup value={mounted ? theme : "dark"} onValueChange={setTheme}>
189
+ <DropdownMenuRadioItem value="light">Light</DropdownMenuRadioItem>
190
+ <DropdownMenuRadioItem value="dark">Dark</DropdownMenuRadioItem>
191
+ <DropdownMenuRadioItem value="ocean">Ocean</DropdownMenuRadioItem>
192
+ <DropdownMenuRadioItem value="forest">Forest</DropdownMenuRadioItem>
193
+ <DropdownMenuRadioItem value="sunset">Sunset</DropdownMenuRadioItem>
194
+ </DropdownMenuRadioGroup>
195
+ </DropdownMenuSubContent>
196
+ </DropdownMenuPortal>
197
+ </DropdownMenuSub>
198
+ <DropdownMenuSeparator />
199
  <DropdownMenuItem
200
  className="text-destructive cursor-pointer"
201
  onClick={handleLogout}