diff --git a/package.json b/package.json
index ec0f0ae..e0302b2 100644
--- a/package.json
+++ b/package.json
@@ -9,19 +9,65 @@
"lint": "next lint"
},
"dependencies": {
+ "@fontsource/jetbrains-mono": "^5.2.6",
+ "@hookform/resolvers": "^5.2.1",
+ "@radix-ui/react-accordion": "^1.2.11",
+ "@radix-ui/react-alert-dialog": "^1.1.14",
+ "@radix-ui/react-aspect-ratio": "^1.1.7",
+ "@radix-ui/react-avatar": "^1.1.10",
+ "@radix-ui/react-checkbox": "^1.3.2",
+ "@radix-ui/react-collapsible": "^1.1.11",
+ "@radix-ui/react-context-menu": "^2.2.15",
+ "@radix-ui/react-dialog": "^1.1.14",
+ "@radix-ui/react-dropdown-menu": "^2.1.15",
+ "@radix-ui/react-hover-card": "^1.1.14",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-menubar": "^1.1.15",
+ "@radix-ui/react-navigation-menu": "^1.2.13",
+ "@radix-ui/react-popover": "^1.1.14",
+ "@radix-ui/react-progress": "^1.1.7",
+ "@radix-ui/react-radio-group": "^1.3.7",
+ "@radix-ui/react-scroll-area": "^1.2.9",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-separator": "^1.1.7",
+ "@radix-ui/react-slider": "^1.3.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.12",
+ "@radix-ui/react-toggle": "^1.1.9",
+ "@radix-ui/react-toggle-group": "^1.1.10",
+ "@radix-ui/react-tooltip": "^1.2.7",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "date-fns": "^4.1.0",
+ "embla-carousel-react": "^8.6.0",
+ "input-otp": "^1.4.2",
+ "lucide-react": "^0.536.0",
+ "motion": "^12.23.12",
+ "next": "15.4.5",
+ "next-themes": "^0.4.6",
"react": "19.1.0",
+ "react-day-picker": "^9.8.1",
"react-dom": "19.1.0",
- "next": "15.4.5"
+ "react-hook-form": "^7.62.0",
+ "react-resizable-panels": "^3.0.4",
+ "recharts": "2.15.4",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.3.1",
+ "vaul": "^1.1.2",
+ "zod": "^4.0.14"
},
"devDependencies": {
- "typescript": "^5",
+ "@eslint/eslintrc": "^3",
+ "@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
- "@tailwindcss/postcss": "^4",
- "tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.4.5",
- "@eslint/eslintrc": "^3"
+ "tailwindcss": "^4",
+ "tailwindcss-animate": "^1.0.7",
+ "typescript": "^5"
}
}
diff --git a/postcss.config.mjs b/postcss.config.mjs
index c7bcb4b..64f939a 100644
--- a/postcss.config.mjs
+++ b/postcss.config.mjs
@@ -1,5 +1,6 @@
const config = {
plugins: ["@tailwindcss/postcss"],
+
};
export default config;
diff --git a/public/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf b/public/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf
new file mode 100644
index 0000000..ecb5f73
Binary files /dev/null and b/public/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf differ
diff --git a/public/fonts/JetBrainsMono-VariableFont_wght.ttf b/public/fonts/JetBrainsMono-VariableFont_wght.ttf
new file mode 100644
index 0000000..4c96e79
Binary files /dev/null and b/public/fonts/JetBrainsMono-VariableFont_wght.ttf differ
diff --git a/src/app/globals.css b/src/app/globals.css
deleted file mode 100644
index a2dc41e..0000000
--- a/src/app/globals.css
+++ /dev/null
@@ -1,26 +0,0 @@
-@import "tailwindcss";
-
-:root {
- --background: #ffffff;
- --foreground: #171717;
-}
-
-@theme inline {
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background: #0a0a0a;
- --foreground: #ededed;
- }
-}
-
-body {
- background: var(--background);
- color: var(--foreground);
- font-family: Arial, Helvetica, sans-serif;
-}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index f7fa87e..9952eea 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
-import "./globals.css";
-
+import "../styles/globals.css";
+import { ThemeProvider } from "@/components/theme-provider"
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
@@ -23,11 +23,18 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
- {children}
+
+ {children}
+
);
diff --git a/src/app/page.tsx b/src/app/page.tsx
index a932894..0a675f0 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,103 +1,176 @@
+"use client";
import Image from "next/image";
+import { PlaceholdersAndVanishInput } from "@/components/ui/placeholders-and-vanish-input";
+import { BackgroundGradient } from "@/components/ui/background-gradient";
+import { useState } from "react";
+import { useRouter } from "next/navigation"; // Import useRouter
+import { Search, Settings, X } from "lucide-react";
export default function Home() {
- return (
-
-
-
-
- -
- Get started by editing{" "}
-
- src/app/page.tsx
-
- .
-
- -
- Save and see your changes instantly.
-
-
+ const [searchQuery, setSearchQuery] = useState("");
+ const [showAdvanced, setShowAdvanced] = useState(false);
+ const [advancedFields, setAdvancedFields] = useState({
+ title: "",
+ author: "",
+ abstract: ""
+ });
-
-
-
- Deploy now
-
-
- Read our docs
-
+ const router = useRouter(); // Initialize router
+
+ const handleAdvancedSearch = () => {
+ // Build query params for advanced search
+ const params = new URLSearchParams();
+
+ if (searchQuery) params.set("q", searchQuery);
+ params.set("p", "1");
+ params.set("pageSize", "20");
+
+ // Add advanced fields if they have values
+ if (advancedFields.title) params.set("title", advancedFields.title);
+ if (advancedFields.author) params.set("author", advancedFields.author);
+ if (advancedFields.abstract) params.set("abstract", advancedFields.abstract);
+
+ router.push(`/search?${params.toString()}`); // Use router.push instead of redirect
+ };
+
+ const handleBasicSearch = () => {
+ router.push("/search?q=" + searchQuery + "&p=1" + "&pageSize=" + 20); // Use router.push instead of redirect
+ };
+
+ return (
+
+
+ Refinity
+
+
+ {/* Main Search Bar */}
+
+ setSearchQuery(v.target.value)}
+ onSubmit={handleBasicSearch}
+ />
+
+
+ {/* Advanced Search Toggle */}
+
+
+
+
+ {/* Advanced Search Window */}
+ {showAdvanced && (
+
+
+
+ Advanced Search
+
+
+
+
+
+
+ {/* Advanced Search Button */}
+
+
+
+
+
+ {/* Info Text */}
+
+ Leave fields empty to search all content. Combine with general search above for more specific results.
+
+
+ )}
-
-
-
- );
-}
+ );
+}
\ No newline at end of file
diff --git a/src/app/search/favicon.ico b/src/app/search/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/src/app/search/favicon.ico differ
diff --git a/src/app/search/layout.tsx b/src/app/search/layout.tsx
new file mode 100644
index 0000000..cb753e5
--- /dev/null
+++ b/src/app/search/layout.tsx
@@ -0,0 +1,41 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import "@/styles/globals.css";
+import { ThemeProvider } from "@/components/theme-provider"
+const geistSans = Geist({
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx
new file mode 100644
index 0000000..5be7999
--- /dev/null
+++ b/src/app/search/page.tsx
@@ -0,0 +1,289 @@
+"use client";
+import {getArticlesPage, getArticlesPageAdvanced} from "@/lib/CrossRefAPI"
+import { useState, useEffect } from "react";
+import Article from "@/components/ui/articles"
+import { Search, Settings, ChevronDown, ChevronUp, X } from "lucide-react";
+import {redirect, useSearchParams} from "next/navigation";
+import { PlaceholdersAndVanishInput } from "@/components/ui/placeholders-and-vanish-input";
+import { BackgroundGradient } from "@/components/ui/background-gradient";
+import {
+ Pagination,
+ PaginationContent,
+ PaginationEllipsis,
+ PaginationItem,
+ PaginationLink,
+ PaginationNext,
+ PaginationPrevious,
+} from "@/components/ui/pagination"
+import ArticlesContainer from "@/components/ui/articles";
+
+export default function SearchPage() {
+ const searchParams = useSearchParams();
+ const page = parseInt(searchParams.get("p") ?? "1");
+ const pageSize = parseInt(searchParams.get("pageSize") ?? "10");
+ const query = searchParams.get("q") ?? "";
+ const titleQuery = searchParams.get("title") ?? "";
+ const authorQuery = searchParams.get("author") ?? "";
+ const abstractQuery = searchParams.get("abstract") ?? "";
+
+ const [hovered, setHovered] = useState(false);
+ const [showInput, setShowInput] = useState(false);
+ const [searchQuery, setSearchQuery] = useState(query);
+ const [showAdvanced, setShowAdvanced] = useState(false);
+ const [advancedFields, setAdvancedFields] = useState({
+ title: titleQuery,
+ author: authorQuery,
+ abstract: abstractQuery
+ });
+
+ // Control rendering input with delay for smooth collapse animation
+ useEffect(() => {
+ if (hovered) {
+ setShowInput(true);
+ } else {
+ const timeout = setTimeout(() => setShowInput(false), 300); // match CSS transition duration
+ return () => clearTimeout(timeout);
+ }
+ }, [hovered]);
+
+ // Update search query when URL changes
+ useEffect(() => {
+ setSearchQuery(query);
+ setAdvancedFields({
+ title: titleQuery,
+ author: authorQuery,
+ abstract: abstractQuery
+ });
+ }, [query, titleQuery, authorQuery, abstractQuery]);
+
+ const handleAdvancedSearch = () => {
+ // Build query params for advanced search
+ const params = new URLSearchParams();
+
+ if (searchQuery) params.set("q", searchQuery);
+ params.set("p", "1");
+ params.set("pageSize", pageSize.toString());
+
+ // Add advanced fields if they have values
+ if (advancedFields.title) params.set("title", advancedFields.title);
+ if (advancedFields.author) params.set("author", advancedFields.author);
+ if (advancedFields.abstract) params.set("abstract", advancedFields.abstract);
+
+ redirect(`/search?${params.toString()}`);
+ };
+
+ const handleBasicSearch = () => {
+ redirect("/search?q=" + searchQuery + "&p=1" + "&pageSize=" + pageSize);
+ };
+
+ const clearAdvancedFields = () => {
+ setAdvancedFields({ title: "", author: "", abstract: "" });
+ };
+
+ // Check if any advanced fields are active
+ const hasAdvancedFilters = titleQuery || authorQuery || abstractQuery;
+
+ return (
+
+ {/* Top Search Bar */}
+
setHovered(true)}
+ onMouseLeave={() => setHovered(false)}
+ >
+ {!showInput ? (
+
+
+
+ ) : (
+
+
+ setSearchQuery(v.target.value)}
+ onSubmit={handleBasicSearch}
+ value={searchQuery}
+ />
+
+
+ )}
+
+
+ {/* Advanced Search Toggle - Top Right */}
+
+
+
+
+ {/* Advanced Search Panel */}
+
+
+
+
+
+ Advanced Search
+
+
+
+
+
+
+ {/* Action Buttons */}
+
+
+
+
+
+ {/* Active Filters Indicator */}
+ {hasAdvancedFilters && (
+
+
Active filters:
+
+ {titleQuery && (
+
+ Title: {titleQuery}
+
+ )}
+ {authorQuery && (
+
+ Author: {authorQuery}
+
+ )}
+ {abstractQuery && (
+
+ Abstract: {abstractQuery}
+
+ )}
+
+
+ )}
+
+ {/* Info Text */}
+
+ Combine with general search for more specific results.
+
+
+
+
+
+ {/* Content area below */}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 0000000..755059a
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,20 @@
+@import "./theme.css";
+@import "../../node_modules/tailwindcss/index.css";
+
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+/* Add your JetBrains Mono font */
+@font-face {
+ font-family: 'JetBrains Mono';
+ src: url('/fonts/JetBrainsMono-VariableFont_wght.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+.jetbrains {
+ font-family: 'JetBrains Mono', monospace;
+}